mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-05-29 18:18:26 +01:00
Compare commits
66 Commits
sync-subti
...
remove-opt
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7eec40df32 | ||
|
|
4690d23164 | ||
|
|
ac8baa5909 | ||
|
|
bd88de8a90 | ||
|
|
9cc119894f | ||
|
|
da3654ec4a | ||
|
|
e7f200a114 | ||
|
|
da9afacbf7 | ||
|
|
80fdd579f3 | ||
|
|
f79cf1925d | ||
|
|
8bb0d845a2 | ||
|
|
c7cd8217c9 | ||
|
|
235ba1473f | ||
|
|
284a4e3d41 | ||
|
|
1fd3574520 | ||
|
|
f1188c090a | ||
|
|
1321a5c000 | ||
|
|
52bc5e912d | ||
|
|
7bccafc476 | ||
|
|
8df61838d4 | ||
|
|
55776d887f | ||
|
|
2e7079cb5a | ||
|
|
428455f6a6 | ||
|
|
8c749cdc4d | ||
|
|
7ed0c00ce7 | ||
|
|
222ae69644 | ||
|
|
fec8df37f7 | ||
|
|
0e0e722e1c | ||
|
|
2ce810c191 | ||
|
|
564a593a3a | ||
|
|
479e23f001 | ||
|
|
019f863b3e | ||
|
|
54d8693999 | ||
|
|
8c0cbffd98 | ||
|
|
381dc351e2 | ||
|
|
3b2649bb65 | ||
|
|
bae8161591 | ||
|
|
9a17e36882 | ||
|
|
391ca897a5 | ||
|
|
c8fefdf4a1 | ||
|
|
956eea8848 | ||
|
|
895c245254 | ||
|
|
376d2e84da | ||
|
|
12047cbe12 | ||
|
|
32f4bbcc7d | ||
|
|
138a86f473 | ||
|
|
6dd111defe | ||
|
|
4ba03b669e | ||
|
|
aacf5327d1 | ||
|
|
f543fa9e3e | ||
|
|
4385fe5502 | ||
|
|
3d72c9c783 | ||
|
|
e41d1b4818 | ||
|
|
fd3766fc23 | ||
|
|
f211a9ce7a | ||
|
|
b7db06f53d | ||
|
|
ceac74dbfa | ||
|
|
b6bd427e19 | ||
|
|
47bf1c9201 | ||
|
|
4aaddd2104 | ||
|
|
4a75e8f551 | ||
|
|
0c8c27bfc0 | ||
|
|
67e61f3ab8 | ||
|
|
c66541ce4d | ||
|
|
07a0a48613 | ||
|
|
b630e0784b |
41
.github/copilot-instructions.md
vendored
41
.github/copilot-instructions.md
vendored
@@ -3,7 +3,7 @@
|
|||||||
## Project Overview
|
## Project Overview
|
||||||
|
|
||||||
Streamyfin is a cross-platform Jellyfin video streaming client built with Expo (React Native).
|
Streamyfin is a cross-platform Jellyfin video streaming client built with Expo (React Native).
|
||||||
It supports mobile (iOS/Android) and TV platforms, integrates with Jellyfin and Jellyseerr APIs,
|
It supports mobile (iOS/Android) and TV platforms, integrates with Jellyfin and Seerr APIs,
|
||||||
and provides seamless media streaming with offline capabilities and Chromecast support.
|
and provides seamless media streaming with offline capabilities and Chromecast support.
|
||||||
|
|
||||||
## Main Technologies
|
## Main Technologies
|
||||||
@@ -40,9 +40,30 @@ and provides seamless media streaming with offline capabilities and Chromecast s
|
|||||||
- `scripts/` – Automation scripts (Node.js, Bash)
|
- `scripts/` – Automation scripts (Node.js, Bash)
|
||||||
- `plugins/` – Expo/Metro plugins
|
- `plugins/` – Expo/Metro plugins
|
||||||
|
|
||||||
## Coding Standards
|
## Code Quality Standards
|
||||||
|
|
||||||
|
**CRITICAL: Code must be production-ready, reliable, and maintainable**
|
||||||
|
|
||||||
|
### Type Safety
|
||||||
- Use TypeScript for ALL files (no .js files)
|
- Use TypeScript for ALL files (no .js files)
|
||||||
|
- **NEVER use `any` type** - use proper types, generics, or `unknown` with type guards
|
||||||
|
- Use `@ts-expect-error` with detailed comments only when necessary (e.g., library limitations)
|
||||||
|
- When facing type issues, create proper type definitions and helper functions instead of using `any`
|
||||||
|
- Use type assertions (`as`) only as a last resort with clear documentation explaining why
|
||||||
|
- For Expo Router navigation: prefer string URLs with `URLSearchParams` over object syntax to avoid type conflicts
|
||||||
|
- Enable and respect strict TypeScript compiler options
|
||||||
|
- Define explicit return types for functions
|
||||||
|
- Use discriminated unions for complex state
|
||||||
|
|
||||||
|
### Code Reliability
|
||||||
|
- Implement comprehensive error handling with try-catch blocks
|
||||||
|
- Validate all external inputs (API responses, user input, query params)
|
||||||
|
- Handle edge cases explicitly (empty arrays, null, undefined)
|
||||||
|
- Use optional chaining (`?.`) and nullish coalescing (`??`) appropriately
|
||||||
|
- Add runtime checks for critical operations
|
||||||
|
- Implement proper loading and error states in components
|
||||||
|
|
||||||
|
### Best Practices
|
||||||
- Use descriptive English names for variables, functions, and components
|
- Use descriptive English names for variables, functions, and components
|
||||||
- Prefer functional React components with hooks
|
- Prefer functional React components with hooks
|
||||||
- Use Jotai atoms for global state management
|
- Use Jotai atoms for global state management
|
||||||
@@ -50,8 +71,10 @@ and provides seamless media streaming with offline capabilities and Chromecast s
|
|||||||
- Follow BiomeJS formatting and linting rules
|
- Follow BiomeJS formatting and linting rules
|
||||||
- Use `const` over `let`, avoid `var` entirely
|
- Use `const` over `let`, avoid `var` entirely
|
||||||
- Implement proper error boundaries
|
- Implement proper error boundaries
|
||||||
- Use React.memo() for performance optimization
|
- Use React.memo() for performance optimization when needed
|
||||||
- Handle both mobile and TV navigation patterns
|
- Handle both mobile and TV navigation patterns
|
||||||
|
- Write self-documenting code with clear intent
|
||||||
|
- Add comments only when code complexity requires explanation
|
||||||
|
|
||||||
## API Integration
|
## API Integration
|
||||||
|
|
||||||
@@ -85,6 +108,18 @@ Exemples:
|
|||||||
- `fix(auth): handle expired JWT tokens`
|
- `fix(auth): handle expired JWT tokens`
|
||||||
- `chore(deps): update Jellyfin SDK`
|
- `chore(deps): update Jellyfin SDK`
|
||||||
|
|
||||||
|
## Internationalization (i18n)
|
||||||
|
|
||||||
|
- **Primary workflow**: Always edit `translations/en.json` for new translation keys or updates
|
||||||
|
- **Translation files** (ar.json, ca.json, cs.json, de.json, etc.):
|
||||||
|
- **NEVER add or remove keys** - Crowdin manages the key structure
|
||||||
|
- **Editing translation values is safe** - Bidirectional sync handles merges
|
||||||
|
- Prefer letting Crowdin translators update values, but direct edits work if needed
|
||||||
|
- **Crowdin workflow**:
|
||||||
|
- New keys added to `en.json` sync to Crowdin automatically
|
||||||
|
- Approved translations sync back to language files via GitHub integration
|
||||||
|
- The source of truth is `en.json` for structure, Crowdin for translations
|
||||||
|
|
||||||
## Special Instructions
|
## Special Instructions
|
||||||
|
|
||||||
- Prioritize cross-platform compatibility (mobile + TV)
|
- Prioritize cross-platform compatibility (mobile + TV)
|
||||||
|
|||||||
2
.github/workflows/artifact-comment.yml
vendored
2
.github/workflows/artifact-comment.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 🔍 Get PR and Artifacts
|
- name: 🔍 Get PR and Artifacts
|
||||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
|
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
// Check if we're running from a fork (more precise detection)
|
// Check if we're running from a fork (more precise detection)
|
||||||
|
|||||||
38
.github/workflows/build-apps.yml
vendored
38
.github/workflows/build-apps.yml
vendored
@@ -41,12 +41,12 @@ jobs:
|
|||||||
show-progress: false
|
show-progress: false
|
||||||
|
|
||||||
- name: 🍞 Setup Bun
|
- name: 🍞 Setup Bun
|
||||||
uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2.1.2
|
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
|
||||||
with:
|
with:
|
||||||
bun-version: latest
|
bun-version: latest
|
||||||
|
|
||||||
- name: 💾 Cache Bun dependencies
|
- name: 💾 Cache Bun dependencies
|
||||||
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||||
with:
|
with:
|
||||||
path: ~/.bun/install/cache
|
path: ~/.bun/install/cache
|
||||||
key: ${{ runner.os }}-${{ runner.arch }}-bun-develop-${{ hashFiles('bun.lock') }}
|
key: ${{ runner.os }}-${{ runner.arch }}-bun-develop-${{ hashFiles('bun.lock') }}
|
||||||
@@ -60,7 +60,7 @@ jobs:
|
|||||||
bun run submodule-reload
|
bun run submodule-reload
|
||||||
|
|
||||||
- name: 💾 Cache Gradle global
|
- name: 💾 Cache Gradle global
|
||||||
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.gradle/caches
|
~/.gradle/caches
|
||||||
@@ -73,7 +73,7 @@ jobs:
|
|||||||
run: bun run prebuild
|
run: bun run prebuild
|
||||||
|
|
||||||
- name: 💾 Cache project Gradle (.gradle)
|
- name: 💾 Cache project Gradle (.gradle)
|
||||||
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||||
with:
|
with:
|
||||||
path: android/.gradle
|
path: android/.gradle
|
||||||
key: ${{ runner.os }}-android-gradle-develop-${{ hashFiles('android/**/build.gradle', 'android/gradle/wrapper/gradle-wrapper.properties') }}
|
key: ${{ runner.os }}-android-gradle-develop-${{ hashFiles('android/**/build.gradle', 'android/gradle/wrapper/gradle-wrapper.properties') }}
|
||||||
@@ -88,7 +88,7 @@ jobs:
|
|||||||
run: echo "DATE_TAG=$(date +%d-%m-%Y_%H-%M-%S)" >> $GITHUB_ENV
|
run: echo "DATE_TAG=$(date +%d-%m-%Y_%H-%M-%S)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: 📤 Upload APK artifact
|
- name: 📤 Upload APK artifact
|
||||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
with:
|
with:
|
||||||
name: streamyfin-android-phone-apk-${{ env.DATE_TAG }}
|
name: streamyfin-android-phone-apk-${{ env.DATE_TAG }}
|
||||||
path: |
|
path: |
|
||||||
@@ -124,12 +124,12 @@ jobs:
|
|||||||
show-progress: false
|
show-progress: false
|
||||||
|
|
||||||
- name: 🍞 Setup Bun
|
- name: 🍞 Setup Bun
|
||||||
uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2.1.2
|
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
|
||||||
with:
|
with:
|
||||||
bun-version: latest
|
bun-version: latest
|
||||||
|
|
||||||
- name: 💾 Cache Bun dependencies
|
- name: 💾 Cache Bun dependencies
|
||||||
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||||
with:
|
with:
|
||||||
path: ~/.bun/install/cache
|
path: ~/.bun/install/cache
|
||||||
key: ${{ runner.os }}-${{ runner.arch }}-bun-develop-${{ hashFiles('bun.lock') }}
|
key: ${{ runner.os }}-${{ runner.arch }}-bun-develop-${{ hashFiles('bun.lock') }}
|
||||||
@@ -143,7 +143,7 @@ jobs:
|
|||||||
bun run submodule-reload
|
bun run submodule-reload
|
||||||
|
|
||||||
- name: 💾 Cache Gradle global
|
- name: 💾 Cache Gradle global
|
||||||
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.gradle/caches
|
~/.gradle/caches
|
||||||
@@ -156,7 +156,7 @@ jobs:
|
|||||||
run: bun run prebuild:tv
|
run: bun run prebuild:tv
|
||||||
|
|
||||||
- name: 💾 Cache project Gradle (.gradle)
|
- name: 💾 Cache project Gradle (.gradle)
|
||||||
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||||
with:
|
with:
|
||||||
path: android/.gradle
|
path: android/.gradle
|
||||||
key: ${{ runner.os }}-android-gradle-develop-${{ hashFiles('android/**/build.gradle', 'android/gradle/wrapper/gradle-wrapper.properties') }}
|
key: ${{ runner.os }}-android-gradle-develop-${{ hashFiles('android/**/build.gradle', 'android/gradle/wrapper/gradle-wrapper.properties') }}
|
||||||
@@ -171,7 +171,7 @@ jobs:
|
|||||||
run: echo "DATE_TAG=$(date +%d-%m-%Y_%H-%M-%S)" >> $GITHUB_ENV
|
run: echo "DATE_TAG=$(date +%d-%m-%Y_%H-%M-%S)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: 📤 Upload APK artifact
|
- name: 📤 Upload APK artifact
|
||||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
with:
|
with:
|
||||||
name: streamyfin-android-tv-apk-${{ env.DATE_TAG }}
|
name: streamyfin-android-tv-apk-${{ env.DATE_TAG }}
|
||||||
path: |
|
path: |
|
||||||
@@ -195,12 +195,12 @@ jobs:
|
|||||||
show-progress: false
|
show-progress: false
|
||||||
|
|
||||||
- name: 🍞 Setup Bun
|
- name: 🍞 Setup Bun
|
||||||
uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2.1.2
|
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
|
||||||
with:
|
with:
|
||||||
bun-version: latest
|
bun-version: latest
|
||||||
|
|
||||||
- name: 💾 Cache Bun dependencies
|
- name: 💾 Cache Bun dependencies
|
||||||
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||||
with:
|
with:
|
||||||
path: ~/.bun/install/cache
|
path: ~/.bun/install/cache
|
||||||
key: ${{ runner.os }}-bun-cache-${{ hashFiles('bun.lock') }}
|
key: ${{ runner.os }}-bun-cache-${{ hashFiles('bun.lock') }}
|
||||||
@@ -216,12 +216,12 @@ jobs:
|
|||||||
run: bun run prebuild
|
run: bun run prebuild
|
||||||
|
|
||||||
- name: 🔧 Setup Xcode
|
- name: 🔧 Setup Xcode
|
||||||
uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1
|
uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1
|
||||||
with:
|
with:
|
||||||
xcode-version: "26.2"
|
xcode-version: "26.2"
|
||||||
|
|
||||||
- name: 🏗️ Setup EAS
|
- name: 🏗️ Setup EAS
|
||||||
uses: expo/expo-github-action@main
|
uses: expo/expo-github-action@b184ff86a3c926240f1b6db41764c83a01c02eef # main
|
||||||
with:
|
with:
|
||||||
eas-version: latest
|
eas-version: latest
|
||||||
token: ${{ secrets.EXPO_TOKEN }}
|
token: ${{ secrets.EXPO_TOKEN }}
|
||||||
@@ -236,7 +236,7 @@ jobs:
|
|||||||
run: echo "DATE_TAG=$(date +%d-%m-%Y_%H-%M-%S)" >> $GITHUB_ENV
|
run: echo "DATE_TAG=$(date +%d-%m-%Y_%H-%M-%S)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: 📤 Upload IPA artifact
|
- name: 📤 Upload IPA artifact
|
||||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
with:
|
with:
|
||||||
name: streamyfin-ios-phone-ipa-${{ env.DATE_TAG }}
|
name: streamyfin-ios-phone-ipa-${{ env.DATE_TAG }}
|
||||||
path: build-*.ipa
|
path: build-*.ipa
|
||||||
@@ -259,12 +259,12 @@ jobs:
|
|||||||
show-progress: false
|
show-progress: false
|
||||||
|
|
||||||
- name: 🍞 Setup Bun
|
- name: 🍞 Setup Bun
|
||||||
uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2.1.2
|
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
|
||||||
with:
|
with:
|
||||||
bun-version: latest
|
bun-version: latest
|
||||||
|
|
||||||
- name: 💾 Cache Bun dependencies
|
- name: 💾 Cache Bun dependencies
|
||||||
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||||
with:
|
with:
|
||||||
path: ~/.bun/install/cache
|
path: ~/.bun/install/cache
|
||||||
key: ${{ runner.os }}-bun-cache-${{ hashFiles('bun.lock') }}
|
key: ${{ runner.os }}-bun-cache-${{ hashFiles('bun.lock') }}
|
||||||
@@ -280,7 +280,7 @@ jobs:
|
|||||||
run: bun run prebuild
|
run: bun run prebuild
|
||||||
|
|
||||||
- name: 🔧 Setup Xcode
|
- name: 🔧 Setup Xcode
|
||||||
uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1
|
uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1
|
||||||
with:
|
with:
|
||||||
xcode-version: "26.2"
|
xcode-version: "26.2"
|
||||||
|
|
||||||
@@ -293,7 +293,7 @@ jobs:
|
|||||||
run: echo "DATE_TAG=$(date +%d-%m-%Y_%H-%M-%S)" >> $GITHUB_ENV
|
run: echo "DATE_TAG=$(date +%d-%m-%Y_%H-%M-%S)" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: 📤 Upload IPA artifact
|
- name: 📤 Upload IPA artifact
|
||||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
with:
|
with:
|
||||||
name: streamyfin-ios-phone-unsigned-ipa-${{ env.DATE_TAG }}
|
name: streamyfin-ios-phone-unsigned-ipa-${{ env.DATE_TAG }}
|
||||||
path: build/*.ipa
|
path: build/*.ipa
|
||||||
|
|||||||
4
.github/workflows/check-lockfile.yml
vendored
4
.github/workflows/check-lockfile.yml
vendored
@@ -27,12 +27,12 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: 🍞 Setup Bun
|
- name: 🍞 Setup Bun
|
||||||
uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2.1.2
|
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
|
||||||
with:
|
with:
|
||||||
bun-version: latest
|
bun-version: latest
|
||||||
|
|
||||||
- name: 💾 Cache Bun dependencies
|
- name: 💾 Cache Bun dependencies
|
||||||
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.bun/install/cache
|
~/.bun/install/cache
|
||||||
|
|||||||
6
.github/workflows/ci-codeql.yml
vendored
6
.github/workflows/ci-codeql.yml
vendored
@@ -27,13 +27,13 @@ jobs:
|
|||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
|
||||||
- name: 🏁 Initialize CodeQL
|
- name: 🏁 Initialize CodeQL
|
||||||
uses: github/codeql-action/init@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0
|
uses: github/codeql-action/init@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
queries: +security-extended,security-and-quality
|
queries: +security-extended,security-and-quality
|
||||||
|
|
||||||
- name: 🛠️ Autobuild
|
- name: 🛠️ Autobuild
|
||||||
uses: github/codeql-action/autobuild@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0
|
uses: github/codeql-action/autobuild@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5
|
||||||
|
|
||||||
- name: 🧪 Perform CodeQL Analysis
|
- name: 🧪 Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0
|
uses: github/codeql-action/analyze@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4.35.5
|
||||||
|
|||||||
2
.github/workflows/crowdin.yml
vendored
2
.github/workflows/crowdin.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: 🌐 Sync Translations with Crowdin
|
- name: 🌐 Sync Translations with Crowdin
|
||||||
uses: crowdin/github-action@b4b468cffefb50bdd99dd83e5d2eaeb63c880380 # v2.14.0
|
uses: crowdin/github-action@8868a33591d21088edfc398968173a3b98d51706 # v2.16.2
|
||||||
with:
|
with:
|
||||||
upload_sources: true
|
upload_sources: true
|
||||||
upload_translations: true
|
upload_translations: true
|
||||||
|
|||||||
12
.github/workflows/linting.yml
vendored
12
.github/workflows/linting.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2.9.4
|
- uses: marocchino/sticky-pull-request-comment@0ea0beb66eb9baf113663a64ec522f60e49231c0 # v3.0.4
|
||||||
if: always() && (steps.lint_pr_title.outputs.error_message != null)
|
if: always() && (steps.lint_pr_title.outputs.error_message != null)
|
||||||
with:
|
with:
|
||||||
header: pr-title-lint-error
|
header: pr-title-lint-error
|
||||||
@@ -39,7 +39,7 @@ jobs:
|
|||||||
```
|
```
|
||||||
|
|
||||||
- if: ${{ steps.lint_pr_title.outputs.error_message == null }}
|
- if: ${{ steps.lint_pr_title.outputs.error_message == null }}
|
||||||
uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2.9.4
|
uses: marocchino/sticky-pull-request-comment@0ea0beb66eb9baf113663a64ec522f60e49231c0 # v3.0.4
|
||||||
with:
|
with:
|
||||||
header: pr-title-lint-error
|
header: pr-title-lint-error
|
||||||
delete: true
|
delete: true
|
||||||
@@ -57,7 +57,7 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Dependency Review
|
- name: Dependency Review
|
||||||
uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2
|
uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
|
||||||
with:
|
with:
|
||||||
fail-on-severity: high
|
fail-on-severity: high
|
||||||
base-ref: ${{ github.event.pull_request.base.sha || 'develop' }}
|
base-ref: ${{ github.event.pull_request.base.sha || 'develop' }}
|
||||||
@@ -76,7 +76,7 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: 🍞 Setup Bun
|
- name: 🍞 Setup Bun
|
||||||
uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2.1.2
|
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
|
||||||
with:
|
with:
|
||||||
bun-version: latest
|
bun-version: latest
|
||||||
|
|
||||||
@@ -107,12 +107,12 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: "🟢 Setup Node.js"
|
- name: "🟢 Setup Node.js"
|
||||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||||
with:
|
with:
|
||||||
node-version: '24.x'
|
node-version: '24.x'
|
||||||
|
|
||||||
- name: "🍞 Setup Bun"
|
- name: "🍞 Setup Bun"
|
||||||
uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2.1.2
|
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
|
||||||
with:
|
with:
|
||||||
bun-version: latest
|
bun-version: latest
|
||||||
|
|
||||||
|
|||||||
6
.github/workflows/update-issue-form.yml
vendored
6
.github/workflows/update-issue-form.yml
vendored
@@ -21,14 +21,14 @@ jobs:
|
|||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
|
||||||
- name: "🟢 Setup Node.js"
|
- name: "🟢 Setup Node.js"
|
||||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||||
with:
|
with:
|
||||||
node-version: '24.x'
|
node-version: '24.x'
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
|
|
||||||
- name: 🔍 Extract minor version from app.json
|
- name: 🔍 Extract minor version from app.json
|
||||||
id: minor
|
id: minor
|
||||||
uses: actions/github-script@main
|
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # main
|
||||||
with:
|
with:
|
||||||
result-encoding: string
|
result-encoding: string
|
||||||
script: |
|
script: |
|
||||||
@@ -54,7 +54,7 @@ jobs:
|
|||||||
dry_run: no-push
|
dry_run: no-push
|
||||||
|
|
||||||
- name: 📬 Commit and create pull request
|
- name: 📬 Commit and create pull request
|
||||||
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
|
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
|
||||||
with:
|
with:
|
||||||
add-paths: .github/ISSUE_TEMPLATE/bug_report.yml
|
add-paths: .github/ISSUE_TEMPLATE/bug_report.yml
|
||||||
branch: ci-update-bug-report
|
branch: ci-update-bug-report
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|||||||
|
|
||||||
## Project Overview
|
## Project Overview
|
||||||
|
|
||||||
Streamyfin is a cross-platform Jellyfin video streaming client built with Expo (React Native). It supports mobile (iOS/Android) and TV platforms, with features including offline downloads, Chromecast support, and Jellyseerr integration.
|
Streamyfin is a cross-platform Jellyfin video streaming client built with Expo (React Native). It supports mobile (iOS/Android) and TV platforms, with features including offline downloads, Chromecast support, and Seerr integration.
|
||||||
|
|
||||||
## Development Commands
|
## Development Commands
|
||||||
|
|
||||||
|
|||||||
101
README.md
101
README.md
@@ -22,58 +22,75 @@
|
|||||||
|
|
||||||
<img src="./assets/images/screenshots/screenshot2.png" width="20%">
|
<img src="./assets/images/screenshots/screenshot2.png" width="20%">
|
||||||
|
|
||||||
<img src="./assets/images/jellyseerr.PNG" width="21%">
|
<img src="./assets/images/seerr.PNG" width="21%">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
## 🌟 Features
|
## 🌟 Features
|
||||||
|
|
||||||
- 🚀 **Skip Intro / Credits Support**: Lets you quickly skip intros and credits during playback
|
### 🎬 Media Playback
|
||||||
- 🖼️ **Trickplay images**: The new golden standard for chapter previews when seeking
|
- 🚀 **Skip Intro / Credits**: Automatically skip intros and credits during playback
|
||||||
- 📥 **Download media**: Save your media locally and watch it offline
|
- 🖼️ **Trickplay Images**: Chapter previews with thumbnails when seeking
|
||||||
- ⚙️ **Settings management**: Manage app configurations for all users through our plugin
|
- 🎵 **Music Library**: Full support for music playback with playlists and queue management
|
||||||
- 🤖 **Seerr (formerly Jellyseerr) integration**: Request media directly in the app
|
- 📺 **Live TV**: Watch live television streams
|
||||||
- 👁️ **Sessions view:** View all active sessions currently streaming on your server
|
|
||||||
- 📡 **Chromecast**: Cast your media to any Chromecast-enabled device
|
- 📡 **Chromecast**: Cast your media to any Chromecast-enabled device
|
||||||
|
- 🎥 **MPV Player**: Powerful open-source player with wide format support
|
||||||
|
|
||||||
## 🧪 Experimental Features
|
### 📱 Media Management
|
||||||
|
- 📥 **Download Media**: Save movies, shows, and music locally for offline viewing
|
||||||
|
- ⭐ **Favorites**: Quick access to your favorite content
|
||||||
|
- 📋 **Watchlists**: Create and manage custom watchlists with Streamystats integration
|
||||||
|
- 🔖 **Continue Watching**: Pick up right where you left off
|
||||||
|
- 🎯 **Next Up**: Suggestions for your next episode
|
||||||
|
|
||||||
Streamyfin offers exciting experimental features such as media downloading and Chromecast support. These features are under active development, and your feedback and patience help us make them even better.
|
### ⚙️ Advanced Features
|
||||||
|
- 🤖 **Seerr Integration**: Request new media directly in the app
|
||||||
|
- 🔍 **Smart Search**: Powerful search with Marlin Search and Streamystats support
|
||||||
|
- 👁️ **Active Sessions**: View all active streams on your server
|
||||||
|
- 🌐 **Multi-Language**: Available in 20+ languages with Crowdin integration
|
||||||
|
- 🎨 **Customizable**: Personalize your home screen and settings
|
||||||
|
- 🔌 **Plugin System**: Centralized settings sync across all devices via Jellyfin plugin
|
||||||
|
|
||||||
### 📥 Downloading
|
## 🧩 How It Works
|
||||||
|
|
||||||
|
### 📥 Downloads
|
||||||
|
|
||||||
Downloading works by using FFmpeg to convert an HLS stream into a video file on your device. This lets you download and watch any content that you can stream. The conversion is handled in real time by Jellyfin on the server during the download. While this may take a bit longer, it ensures compatibility with any file your server can transcode.
|
Downloading works by using FFmpeg to convert an HLS stream into a video file on your device. This lets you download and watch any content that you can stream. The conversion is handled in real time by Jellyfin on the server during the download. While this may take a bit longer, it ensures compatibility with any file your server can transcode.
|
||||||
|
|
||||||
### 🧩 Streamyfin Plugin
|
### 🧩 Streamyfin Plugin
|
||||||
|
|
||||||
The Jellyfin Plugin for Streamyfin is a plugin you install into Jellyfin that holds all settings for the client Streamyfin. This allows you to synchronize settings across all your users, like for example:
|
The Jellyfin Plugin for Streamyfin synchronizes settings across all your devices and users. Install it on your Jellyfin server to enable:
|
||||||
|
|
||||||
- Automatic Seerr login with no user input required
|
- Automatic Seerr login with no user input required
|
||||||
- Set your preferred default languages
|
- Default language preferences for audio and subtitles
|
||||||
- Configure download method and search provider
|
- Configure download settings and search providers (Marlin, Streamystats)
|
||||||
- Personalize your home screen
|
- Customize your home screen layout and sections
|
||||||
|
- Centralized configuration management
|
||||||
- And much more
|
- And much more
|
||||||
|
|
||||||
[Streamyfin Plugin](https://github.com/streamyfin/jellyfin-plugin-streamyfin)
|
[Streamyfin Plugin](https://github.com/streamyfin/jellyfin-plugin-streamyfin)
|
||||||
|
|
||||||
### 📡 Chromecast
|
|
||||||
|
|
||||||
Chromecast support is currently under development. Video casting is already available, and we're actively working on adding subtitle support and additional features.
|
|
||||||
|
|
||||||
### 🎬 MPV Player
|
### 🎬 MPV Player
|
||||||
|
|
||||||
Streamyfin uses [MPV](https://mpv.io/) as its primary video player on all platforms, powered by [MPVKit](https://github.com/mpvkit/MPVKit). MPV is a powerful, open-source media player known for its wide format support and high-quality playback.
|
Streamyfin uses [MPV](https://mpv.io/) as its primary video player on all platforms, powered by [MPVKit](https://github.com/mpvkit/MPVKit). MPV is a powerful, open-source media player known for its wide format support and high-quality playback.
|
||||||
|
|
||||||
Thanks to [@Alexk2309](https://github.com/Alexk2309) for the hard work building the native MPV module in Streamyfin.
|
Thanks to [@Alexk2309](https://github.com/Alexk2309) for the hard work building the native MPV module in Streamyfin.
|
||||||
|
|
||||||
### 🔍 Jellysearch
|
### 🎵 Music Library
|
||||||
|
|
||||||
[Jellysearch](https://gitlab.com/DomiStyle/jellysearch) works with Streamyfin
|
Full music library support with playlists, queue management, background playback, and offline downloads.
|
||||||
|
|
||||||
> A fast full-text search proxy for Jellyfin. Integrates seamlessly with most Jellyfin clients.
|
### 🔍 Search Providers
|
||||||
|
|
||||||
|
Streamyfin supports multiple search providers:
|
||||||
|
|
||||||
|
- **Marlin Search**: Fast semantic search for your Jellyfin library
|
||||||
|
- **Streamystats**: Advanced statistics and personalized recommendations
|
||||||
|
- **Jellysearch**: Fast full-text search proxy ([Jellysearch](https://gitlab.com/DomiStyle/jellysearch))
|
||||||
|
|
||||||
## 🛣️ Roadmap
|
## 🛣️ Roadmap
|
||||||
|
|
||||||
Check out our [Roadmap](https://github.com/users/fredrikburmester/projects/5) To see what we're working on next, we are always open to feedback and suggestions. Please let us know if you have any ideas or feature requests.
|
Check out our [Roadmap](https://github.com/users/fredrikburmester/projects/5) to see what we're working on next. We are always open to feedback and suggestions. Please let us know if you have any ideas or feature requests.
|
||||||
|
|
||||||
## 📥 Download Streamyfin
|
## 📥 Download Streamyfin
|
||||||
|
|
||||||
@@ -113,13 +130,12 @@ You can contribute translations directly on our [Crowdin project page](https://c
|
|||||||
|
|
||||||
### 👨💻 Development Info
|
### 👨💻 Development Info
|
||||||
|
|
||||||
1. Use node `>20`
|
1. Use Node.js `>20`
|
||||||
2. Install dependencies `bun i && bun run submodule-reload`
|
2. Install dependencies: `bun i && bun run submodule-reload`
|
||||||
3. Make sure you have xcode and/or android studio installed. (follow the guides for expo: https://docs.expo.dev/workflow/android-studio-emulator/)
|
3. Make sure you have Xcode and/or Android Studio installed ([Expo setup guide](https://docs.expo.dev/workflow/android-studio-emulator/))
|
||||||
- If iOS builds fail with `missing Metal Toolchain` (KSPlayer shaders), run `npm run ios:install-metal-toolchain` once
|
4. Install the [BiomeJS extension](https://biomejs.dev/) in your IDE
|
||||||
4. Install BiomeJS extension in VSCode/Your IDE (https://biomejs.dev/)
|
5. Run `npm run prebuild`
|
||||||
4. run `npm run prebuild`
|
6. Create an Expo dev build by running `npm run ios` or `npm run android`. This will open a simulator on your computer and run the app
|
||||||
5. Create an expo dev build by running `npm run ios` or `npm run android`. This will open a simulator on your computer and run the app
|
|
||||||
|
|
||||||
For the TV version suffix the npm commands with `:tv`.
|
For the TV version suffix the npm commands with `:tv`.
|
||||||
|
|
||||||
@@ -137,10 +153,20 @@ Need assistance or have any questions?
|
|||||||
|
|
||||||
## ❓ FAQ
|
## ❓ FAQ
|
||||||
|
|
||||||
1. Q: Why can't I see my libraries in Streamyfin?
|
1. **Q: Why can't I see my libraries in Streamyfin?**
|
||||||
A: Make sure your server is running one of the latest versions and that you have at least one library that isn't audio only
|
A: Ensure your Jellyfin server is running a recent version (10.10.0+) and that you have proper permissions to access the libraries.
|
||||||
2. Q: Why can't I see my music library?
|
|
||||||
A: We don't currently support music and are unlikely to support music in the near future
|
2. **Q: How do I enable downloads?**
|
||||||
|
A: Downloads use FFmpeg to convert HLS streams. Ensure your server has transcoding enabled and sufficient resources.
|
||||||
|
|
||||||
|
3. **Q: Does Streamyfin support subtitles?**
|
||||||
|
A: Yes, with full customization including size, color, position, and automatic language selection.
|
||||||
|
|
||||||
|
4. **Q: Can I use Streamyfin on Apple TV or Android TV?**
|
||||||
|
A: Yes, Streamyfin has dedicated TV builds, but they are currently in **early development** and may have stability issues.
|
||||||
|
|
||||||
|
5. **Q: How do I set up Seerr integration?**
|
||||||
|
A: Go to Settings → Plugins → Seerr, enter your server URL and Jellyfin credentials.
|
||||||
|
|
||||||
## 📝 Credits
|
## 📝 Credits
|
||||||
|
|
||||||
@@ -254,7 +280,9 @@ A special mention to the following people and projects for their contributions:
|
|||||||
## 📄 License
|
## 📄 License
|
||||||
|
|
||||||
Streamyfin is licensed under the Mozilla Public License 2.0 (MPL-2.0).
|
Streamyfin is licensed under the Mozilla Public License 2.0 (MPL-2.0).
|
||||||
|
|
||||||
This means you are free to use, modify, and distribute this software. The MPL-2.0 is a copyleft license that allows for more flexibility in combining the software with proprietary code.
|
This means you are free to use, modify, and distribute this software. The MPL-2.0 is a copyleft license that allows for more flexibility in combining the software with proprietary code.
|
||||||
|
|
||||||
Key points of the MPL-2.0:
|
Key points of the MPL-2.0:
|
||||||
|
|
||||||
- You can use the software for any purpose
|
- You can use the software for any purpose
|
||||||
@@ -263,10 +291,13 @@ Key points of the MPL-2.0:
|
|||||||
- You must disclose your source code for any modifications to the covered files
|
- You must disclose your source code for any modifications to the covered files
|
||||||
- Larger works may combine MPL code with code under other licenses
|
- Larger works may combine MPL code with code under other licenses
|
||||||
- MPL-licensed components must remain under the MPL, but the larger work can be under a different license
|
- MPL-licensed components must remain under the MPL, but the larger work can be under a different license
|
||||||
- For the full text of the license, please see the LICENSE file in this repository
|
|
||||||
|
For the full text of the license, please see the LICENSE file in this repository.
|
||||||
|
|
||||||
## ⚠️ Disclaimer
|
## ⚠️ Disclaimer
|
||||||
|
|
||||||
Streamyfin does not promote, support, or condone piracy in any form. The app is intended solely for streaming media that you personally own and control. It does not provide or include any media content. Any discussions, support requests, or references to piracy, as well as any tools, software, or websites related to piracy, are strictly prohibited across all our channels.
|
Streamyfin does not promote, support, or condone piracy in any form. The app is intended solely for streaming media that you personally own and control. It does not provide or include any media content. Any discussions, support requests, or references to piracy, as well as any tools, software, or websites related to piracy, are strictly prohibited across all our channels.
|
||||||
|
|
||||||
## 🤝 Sponsorship
|
## 🤝 Sponsorship
|
||||||
VPS hosting generously provided by [Hexabyte](https://hexabyte.se/en/vps/?currency=eur) and [SweHosting](https://swehosting.se/en/#tj%C3%A4nster)
|
|
||||||
|
VPS hosting generously provided by [Hexabyte](https://hexabyte.se/en/vps/?currency=eur) and [SweHosting](https://swehosting.se/en/#tj%C3%A4nster).
|
||||||
|
|||||||
@@ -222,9 +222,9 @@ export default function IndexLayout() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name='settings/plugins/jellyseerr/page'
|
name='settings/plugins/seerr/page'
|
||||||
options={{
|
options={{
|
||||||
title: "Jellyseerr",
|
title: "Seerr",
|
||||||
headerBlurEffect: "none",
|
headerBlurEffect: "none",
|
||||||
headerTransparent: Platform.OS === "ios",
|
headerTransparent: Platform.OS === "ios",
|
||||||
headerShadowVisible: false,
|
headerShadowVisible: false,
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ export default function page() {
|
|||||||
))}
|
))}
|
||||||
</ListGroup>
|
</ListGroup>
|
||||||
<Text className='px-4 text-xs text-neutral-500 mt-1'>
|
<Text className='px-4 text-xs text-neutral-500 mt-1'>
|
||||||
{t("home.settings.other.select_liraries_you_want_to_hide")}
|
{t("home.settings.other.select_libraries_you_want_to_hide")}
|
||||||
</Text>
|
</Text>
|
||||||
</DisabledSetting>
|
</DisabledSetting>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ export default function page() {
|
|||||||
))}
|
))}
|
||||||
</ListGroup>
|
</ListGroup>
|
||||||
<Text className='px-4 text-xs text-neutral-500 mt-1'>
|
<Text className='px-4 text-xs text-neutral-500 mt-1'>
|
||||||
{t("home.settings.other.select_liraries_you_want_to_hide")}
|
{t("home.settings.other.select_libraries_you_want_to_hide")}
|
||||||
</Text>
|
</Text>
|
||||||
</DisabledSetting>
|
</DisabledSetting>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -61,7 +61,10 @@ export default function Page() {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
logsFile.write(JSON.stringify(filteredLogs));
|
logsFile.write(JSON.stringify(filteredLogs));
|
||||||
await Sharing.shareAsync(logsFile.uri, { mimeType: "txt", UTI: "txt" });
|
await Sharing.shareAsync(logsFile.uri, {
|
||||||
|
mimeType: "text/plain",
|
||||||
|
UTI: "public.plain-text",
|
||||||
|
});
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
writeErrorLog("Something went wrong attempting to export", e);
|
writeErrorLog("Something went wrong attempting to export", e);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { ScrollView } from "react-native";
|
import { ScrollView } from "react-native";
|
||||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||||
import DisabledSetting from "@/components/settings/DisabledSetting";
|
import DisabledSetting from "@/components/settings/DisabledSetting";
|
||||||
import { JellyseerrSettings } from "@/components/settings/Jellyseerr";
|
import { SeerrSettings } from "@/components/settings/Seerr";
|
||||||
import { useSettings } from "@/utils/atoms/settings";
|
import { useSettings } from "@/utils/atoms/settings";
|
||||||
|
|
||||||
export default function page() {
|
export default function Page() {
|
||||||
const { pluginSettings } = useSettings();
|
const { pluginSettings } = useSettings();
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
|
|
||||||
@@ -17,10 +17,10 @@ export default function page() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DisabledSetting
|
<DisabledSetting
|
||||||
disabled={pluginSettings?.jellyseerrServerUrl?.locked === true}
|
disabled={pluginSettings?.seerrServerUrl?.locked === true}
|
||||||
className='px-4'
|
className='px-4'
|
||||||
>
|
>
|
||||||
<JellyseerrSettings />
|
<SeerrSettings />
|
||||||
</DisabledSetting>
|
</DisabledSetting>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
);
|
);
|
||||||
@@ -3,9 +3,9 @@ import { Image } from "expo-image";
|
|||||||
import { useLocalSearchParams } from "expo-router";
|
import { useLocalSearchParams } from "expo-router";
|
||||||
import { uniqBy } from "lodash";
|
import { uniqBy } from "lodash";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import ParallaxSlideShow from "@/components/jellyseerr/ParallaxSlideShow";
|
import SeerrPoster from "@/components/posters/SeerrPoster";
|
||||||
import JellyseerrPoster from "@/components/posters/JellyseerrPoster";
|
import ParallaxSlideShow from "@/components/seerr/ParallaxSlideShow";
|
||||||
import { Endpoints, useJellyseerr } from "@/hooks/useJellyseerr";
|
import { Endpoints, useSeerr } from "@/hooks/useSeerr";
|
||||||
import { DiscoverSliderType } from "@/utils/jellyseerr/server/constants/discover";
|
import { DiscoverSliderType } from "@/utils/jellyseerr/server/constants/discover";
|
||||||
import {
|
import {
|
||||||
type MovieResult,
|
type MovieResult,
|
||||||
@@ -13,9 +13,9 @@ import {
|
|||||||
} from "@/utils/jellyseerr/server/models/Search";
|
} from "@/utils/jellyseerr/server/models/Search";
|
||||||
import { COMPANY_LOGO_IMAGE_FILTER } from "@/utils/jellyseerr/src/components/Discover/NetworkSlider";
|
import { COMPANY_LOGO_IMAGE_FILTER } from "@/utils/jellyseerr/src/components/Discover/NetworkSlider";
|
||||||
|
|
||||||
export default function page() {
|
export default function CompanyPage() {
|
||||||
const local = useLocalSearchParams();
|
const local = useLocalSearchParams();
|
||||||
const { jellyseerrApi, isJellyseerrMovieOrTvResult } = useJellyseerr();
|
const { seerrApi, isSeerrMovieOrTvResult } = useSeerr();
|
||||||
|
|
||||||
const { companyId, image, type } = local as unknown as {
|
const { companyId, image, type } = local as unknown as {
|
||||||
companyId: string;
|
companyId: string;
|
||||||
@@ -25,12 +25,12 @@ export default function page() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const { data, fetchNextPage, hasNextPage, isLoading } = useInfiniteQuery({
|
const { data, fetchNextPage, hasNextPage, isLoading } = useInfiniteQuery({
|
||||||
queryKey: ["jellyseerr", "company", type, companyId],
|
queryKey: ["seerr", "company", type, companyId],
|
||||||
queryFn: async ({ pageParam }) => {
|
queryFn: async ({ pageParam }) => {
|
||||||
const params: any = {
|
const params: any = {
|
||||||
page: Number(pageParam),
|
page: Number(pageParam),
|
||||||
};
|
};
|
||||||
return jellyseerrApi?.discover(
|
return seerrApi?.discover(
|
||||||
`${
|
`${
|
||||||
Number(type) === DiscoverSliderType.NETWORKS
|
Number(type) === DiscoverSliderType.NETWORKS
|
||||||
? Endpoints.DISCOVER_TV_NETWORK
|
? Endpoints.DISCOVER_TV_NETWORK
|
||||||
@@ -39,7 +39,7 @@ export default function page() {
|
|||||||
params,
|
params,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
enabled: !!jellyseerrApi && !!companyId,
|
enabled: !!seerrApi && !!companyId,
|
||||||
initialPageParam: 1,
|
initialPageParam: 1,
|
||||||
getNextPageParam: (lastPage, pages) =>
|
getNextPageParam: (lastPage, pages) =>
|
||||||
(lastPage?.page || pages?.findLast((p) => p?.results.length)?.page || 1) +
|
(lastPage?.page || pages?.findLast((p) => p?.results.length)?.page || 1) +
|
||||||
@@ -53,25 +53,24 @@ export default function page() {
|
|||||||
data?.pages
|
data?.pages
|
||||||
?.filter((p) => p?.results.length)
|
?.filter((p) => p?.results.length)
|
||||||
.flatMap(
|
.flatMap(
|
||||||
(p) =>
|
(p) => p?.results.filter((r) => isSeerrMovieOrTvResult(r)) ?? [],
|
||||||
p?.results.filter((r) => isJellyseerrMovieOrTvResult(r)) ?? [],
|
|
||||||
),
|
),
|
||||||
"id",
|
"id",
|
||||||
) ?? [],
|
) ?? [],
|
||||||
[data],
|
[data, isSeerrMovieOrTvResult],
|
||||||
);
|
);
|
||||||
|
|
||||||
const backdrops = useMemo(
|
const backdrops = useMemo(
|
||||||
() =>
|
() =>
|
||||||
jellyseerrApi
|
seerrApi
|
||||||
? flatData.map((r) =>
|
? flatData.map((r) =>
|
||||||
jellyseerrApi.imageProxy(
|
seerrApi.imageProxy(
|
||||||
(r as TvResult | MovieResult).backdropPath,
|
(r as TvResult | MovieResult).backdropPath,
|
||||||
"w1920_and_h800_multi_faces",
|
"w1920_and_h800_multi_faces",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: [],
|
: [],
|
||||||
[jellyseerrApi, flatData],
|
[seerrApi, flatData],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -92,7 +91,7 @@ export default function page() {
|
|||||||
key={companyId}
|
key={companyId}
|
||||||
className='bottom-1 w-1/2'
|
className='bottom-1 w-1/2'
|
||||||
source={{
|
source={{
|
||||||
uri: jellyseerrApi?.imageProxy(image, COMPANY_LOGO_IMAGE_FILTER),
|
uri: seerrApi?.imageProxy(image, COMPANY_LOGO_IMAGE_FILTER),
|
||||||
}}
|
}}
|
||||||
cachePolicy={"memory-disk"}
|
cachePolicy={"memory-disk"}
|
||||||
contentFit='contain'
|
contentFit='contain'
|
||||||
@@ -101,7 +100,7 @@ export default function page() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
renderItem={(item, _index) => <JellyseerrPoster item={item} />}
|
renderItem={(item, _index) => <SeerrPoster item={item} />}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -3,15 +3,15 @@ import { useLocalSearchParams } from "expo-router";
|
|||||||
import { uniqBy } from "lodash";
|
import { uniqBy } from "lodash";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { Text } from "@/components/common/Text";
|
import { Text } from "@/components/common/Text";
|
||||||
import { textShadowStyle } from "@/components/jellyseerr/discover/GenericSlideCard";
|
import SeerrPoster from "@/components/posters/SeerrPoster";
|
||||||
import ParallaxSlideShow from "@/components/jellyseerr/ParallaxSlideShow";
|
import { textShadowStyle } from "@/components/seerr/discover/GenericSlideCard";
|
||||||
import JellyseerrPoster from "@/components/posters/JellyseerrPoster";
|
import ParallaxSlideShow from "@/components/seerr/ParallaxSlideShow";
|
||||||
import { Endpoints, useJellyseerr } from "@/hooks/useJellyseerr";
|
import { Endpoints, useSeerr } from "@/hooks/useSeerr";
|
||||||
import { DiscoverSliderType } from "@/utils/jellyseerr/server/constants/discover";
|
import { DiscoverSliderType } from "@/utils/jellyseerr/server/constants/discover";
|
||||||
|
|
||||||
export default function page() {
|
export default function GenrePage() {
|
||||||
const local = useLocalSearchParams();
|
const local = useLocalSearchParams();
|
||||||
const { jellyseerrApi, isJellyseerrMovieOrTvResult } = useJellyseerr();
|
const { seerrApi, isSeerrMovieOrTvResult } = useSeerr();
|
||||||
|
|
||||||
const { genreId, name, type } = local as unknown as {
|
const { genreId, name, type } = local as unknown as {
|
||||||
genreId: string;
|
genreId: string;
|
||||||
@@ -20,21 +20,21 @@ export default function page() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
|
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
|
||||||
queryKey: ["jellyseerr", "company", type, genreId],
|
queryKey: ["seerr", "genre", type, genreId],
|
||||||
queryFn: async ({ pageParam }) => {
|
queryFn: async ({ pageParam }) => {
|
||||||
const params: any = {
|
const params: any = {
|
||||||
page: Number(pageParam),
|
page: Number(pageParam),
|
||||||
genre: genreId,
|
genre: genreId,
|
||||||
};
|
};
|
||||||
|
|
||||||
return jellyseerrApi?.discover(
|
return seerrApi?.discover(
|
||||||
type === DiscoverSliderType.MOVIE_GENRES
|
type === DiscoverSliderType.MOVIE_GENRES
|
||||||
? Endpoints.DISCOVER_MOVIES
|
? Endpoints.DISCOVER_MOVIES
|
||||||
: Endpoints.DISCOVER_TV,
|
: Endpoints.DISCOVER_TV,
|
||||||
params,
|
params,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
enabled: !!jellyseerrApi && !!genreId,
|
enabled: !!seerrApi && !!genreId,
|
||||||
initialPageParam: 1,
|
initialPageParam: 1,
|
||||||
getNextPageParam: (lastPage, pages) =>
|
getNextPageParam: (lastPage, pages) =>
|
||||||
(lastPage?.page || pages?.findLast((p) => p?.results.length)?.page || 1) +
|
(lastPage?.page || pages?.findLast((p) => p?.results.length)?.page || 1) +
|
||||||
@@ -48,8 +48,7 @@ export default function page() {
|
|||||||
data?.pages
|
data?.pages
|
||||||
?.filter((p) => p?.results.length)
|
?.filter((p) => p?.results.length)
|
||||||
.flatMap(
|
.flatMap(
|
||||||
(p) =>
|
(p) => p?.results.filter((r) => isSeerrMovieOrTvResult(r)) ?? [],
|
||||||
p?.results.filter((r) => isJellyseerrMovieOrTvResult(r)) ?? [],
|
|
||||||
),
|
),
|
||||||
"id",
|
"id",
|
||||||
) ?? [],
|
) ?? [],
|
||||||
@@ -58,15 +57,12 @@ export default function page() {
|
|||||||
|
|
||||||
const backdrops = useMemo(
|
const backdrops = useMemo(
|
||||||
() =>
|
() =>
|
||||||
jellyseerrApi
|
seerrApi
|
||||||
? flatData.map((r) =>
|
? flatData.map((r) =>
|
||||||
jellyseerrApi.imageProxy(
|
seerrApi.imageProxy(r.backdropPath, "w1920_and_h800_multi_faces"),
|
||||||
r.backdropPath,
|
|
||||||
"w1920_and_h800_multi_faces",
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
: [],
|
: [],
|
||||||
[jellyseerrApi, flatData],
|
[seerrApi, flatData],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -91,7 +87,7 @@ export default function page() {
|
|||||||
{name}
|
{name}
|
||||||
</Text>
|
</Text>
|
||||||
}
|
}
|
||||||
renderItem={(item, _index) => <JellyseerrPoster item={item} />}
|
renderItem={(item, _index) => <SeerrPoster item={item} />}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -18,18 +18,18 @@ import { toast } from "sonner-native";
|
|||||||
import { Button } from "@/components/Button";
|
import { Button } from "@/components/Button";
|
||||||
import { Text } from "@/components/common/Text";
|
import { Text } from "@/components/common/Text";
|
||||||
import { GenreTags } from "@/components/GenreTags";
|
import { GenreTags } from "@/components/GenreTags";
|
||||||
import Cast from "@/components/jellyseerr/Cast";
|
|
||||||
import DetailFacts from "@/components/jellyseerr/DetailFacts";
|
|
||||||
import RequestModal from "@/components/jellyseerr/RequestModal";
|
|
||||||
import { OverviewText } from "@/components/OverviewText";
|
import { OverviewText } from "@/components/OverviewText";
|
||||||
import { ParallaxScrollView } from "@/components/ParallaxPage";
|
import { ParallaxScrollView } from "@/components/ParallaxPage";
|
||||||
import { PlatformDropdown } from "@/components/PlatformDropdown";
|
import { PlatformDropdown } from "@/components/PlatformDropdown";
|
||||||
import { JellyserrRatings } from "@/components/Ratings";
|
import { SeerrRatings } from "@/components/Ratings";
|
||||||
import JellyseerrSeasons from "@/components/series/JellyseerrSeasons";
|
import Cast from "@/components/seerr/Cast";
|
||||||
|
import DetailFacts from "@/components/seerr/DetailFacts";
|
||||||
|
import RequestModal from "@/components/seerr/RequestModal";
|
||||||
|
import SeerrSeasons from "@/components/series/SeerrSeasons";
|
||||||
import { ItemActions } from "@/components/series/SeriesActions";
|
import { ItemActions } from "@/components/series/SeriesActions";
|
||||||
import useRouter from "@/hooks/useAppRouter";
|
import useRouter from "@/hooks/useAppRouter";
|
||||||
import { useJellyseerr } from "@/hooks/useJellyseerr";
|
import { useSeerr } from "@/hooks/useSeerr";
|
||||||
import { useJellyseerrCanRequest } from "@/utils/_jellyseerr/useJellyseerrCanRequest";
|
import { useSeerrCanRequest } from "@/utils/_seerr/useSeerrCanRequest";
|
||||||
import { ANIME_KEYWORD_ID } from "@/utils/jellyseerr/server/api/themoviedb/constants";
|
import { ANIME_KEYWORD_ID } from "@/utils/jellyseerr/server/api/themoviedb/constants";
|
||||||
import {
|
import {
|
||||||
type IssueType,
|
type IssueType,
|
||||||
@@ -68,7 +68,7 @@ const Page: React.FC = () => {
|
|||||||
} & Partial<MovieResult | TvResult | MovieDetails | TvDetails>;
|
} & Partial<MovieResult | TvResult | MovieDetails | TvDetails>;
|
||||||
|
|
||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
const { jellyseerrApi, jellyseerrUser, requestMedia } = useJellyseerr();
|
const { seerrApi, seerrUser, requestMedia } = useSeerr();
|
||||||
|
|
||||||
const [issueType, setIssueType] = useState<IssueType>();
|
const [issueType, setIssueType] = useState<IssueType>();
|
||||||
const [issueMessage, setIssueMessage] = useState<string>();
|
const [issueMessage, setIssueMessage] = useState<string>();
|
||||||
@@ -83,8 +83,8 @@ const Page: React.FC = () => {
|
|||||||
isLoading,
|
isLoading,
|
||||||
refetch,
|
refetch,
|
||||||
} = useQuery({
|
} = useQuery({
|
||||||
enabled: !!jellyseerrApi && !!result && !!result.id,
|
enabled: !!seerrApi && !!result && !!result.id,
|
||||||
queryKey: ["jellyseerr", "detail", mediaType, result.id],
|
queryKey: ["seerr", "detail", mediaType, result.id],
|
||||||
staleTime: 0,
|
staleTime: 0,
|
||||||
refetchOnMount: true,
|
refetchOnMount: true,
|
||||||
refetchOnReconnect: true,
|
refetchOnReconnect: true,
|
||||||
@@ -93,21 +93,18 @@ const Page: React.FC = () => {
|
|||||||
refetchInterval: 0,
|
refetchInterval: 0,
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
return mediaType === MediaType.MOVIE
|
return mediaType === MediaType.MOVIE
|
||||||
? jellyseerrApi?.movieDetails(result.id!)
|
? seerrApi?.movieDetails(result.id!)
|
||||||
: jellyseerrApi?.tvDetails(result.id!);
|
: seerrApi?.tvDetails(result.id!);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const [canRequest, hasAdvancedRequestPermission] =
|
const [canRequest, hasAdvancedRequestPermission] =
|
||||||
useJellyseerrCanRequest(details);
|
useSeerrCanRequest(details);
|
||||||
|
|
||||||
const canManageRequests = useMemo(() => {
|
const canManageRequests = useMemo(() => {
|
||||||
if (!jellyseerrUser) return false;
|
if (!seerrUser) return false;
|
||||||
return hasPermission(
|
return hasPermission(Permission.MANAGE_REQUESTS, seerrUser.permissions);
|
||||||
Permission.MANAGE_REQUESTS,
|
}, [seerrUser]);
|
||||||
jellyseerrUser.permissions,
|
|
||||||
);
|
|
||||||
}, [jellyseerrUser]);
|
|
||||||
|
|
||||||
const pendingRequest = useMemo(() => {
|
const pendingRequest = useMemo(() => {
|
||||||
return details?.mediaInfo?.requests?.find(
|
return details?.mediaInfo?.requests?.find(
|
||||||
@@ -119,27 +116,27 @@ const Page: React.FC = () => {
|
|||||||
if (!pendingRequest?.id) return;
|
if (!pendingRequest?.id) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await jellyseerrApi?.approveRequest(pendingRequest.id);
|
await seerrApi?.approveRequest(pendingRequest.id);
|
||||||
toast.success(t("jellyseerr.toasts.request_approved"));
|
toast.success(t("seerr.toasts.request_approved"));
|
||||||
refetch();
|
refetch();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error(t("jellyseerr.toasts.failed_to_approve_request"));
|
toast.error(t("seerr.toasts.failed_to_approve_request"));
|
||||||
console.error("Failed to approve request:", error);
|
console.error("Failed to approve request:", error);
|
||||||
}
|
}
|
||||||
}, [jellyseerrApi, pendingRequest, refetch, t]);
|
}, [seerrApi, pendingRequest, refetch, t]);
|
||||||
|
|
||||||
const handleDeclineRequest = useCallback(async () => {
|
const handleDeclineRequest = useCallback(async () => {
|
||||||
if (!pendingRequest?.id) return;
|
if (!pendingRequest?.id) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await jellyseerrApi?.declineRequest(pendingRequest.id);
|
await seerrApi?.declineRequest(pendingRequest.id);
|
||||||
toast.success(t("jellyseerr.toasts.request_declined"));
|
toast.success(t("seerr.toasts.request_declined"));
|
||||||
refetch();
|
refetch();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error(t("jellyseerr.toasts.failed_to_decline_request"));
|
toast.error(t("seerr.toasts.failed_to_decline_request"));
|
||||||
console.error("Failed to decline request:", error);
|
console.error("Failed to decline request:", error);
|
||||||
}
|
}
|
||||||
}, [jellyseerrApi, pendingRequest, refetch, t]);
|
}, [seerrApi, pendingRequest, refetch, t]);
|
||||||
|
|
||||||
const renderBackdrop = useCallback(
|
const renderBackdrop = useCallback(
|
||||||
(props: BottomSheetBackdropProps) => (
|
(props: BottomSheetBackdropProps) => (
|
||||||
@@ -154,7 +151,7 @@ const Page: React.FC = () => {
|
|||||||
|
|
||||||
const submitIssue = useCallback(() => {
|
const submitIssue = useCallback(() => {
|
||||||
if (result.id && issueType && issueMessage && details) {
|
if (result.id && issueType && issueMessage && details) {
|
||||||
jellyseerrApi
|
seerrApi
|
||||||
?.submitIssue(details.mediaInfo.id, Number(issueType), issueMessage)
|
?.submitIssue(details.mediaInfo.id, Number(issueType), issueMessage)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
setIssueType(undefined);
|
setIssueType(undefined);
|
||||||
@@ -162,7 +159,7 @@ const Page: React.FC = () => {
|
|||||||
bottomSheetModalRef?.current?.close();
|
bottomSheetModalRef?.current?.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [jellyseerrApi, details, result, issueType, issueMessage]);
|
}, [seerrApi, details, result, issueType, issueMessage]);
|
||||||
|
|
||||||
const handleIssueModalDismiss = useCallback(() => {
|
const handleIssueModalDismiss = useCallback(() => {
|
||||||
setIssueTypeDropdownOpen(false);
|
setIssueTypeDropdownOpen(false);
|
||||||
@@ -214,7 +211,7 @@ const Page: React.FC = () => {
|
|||||||
const issueTypeOptionGroups = useMemo(
|
const issueTypeOptionGroups = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
title: t("jellyseerr.types"),
|
title: t("seerr.types"),
|
||||||
options: Object.entries(IssueTypeName)
|
options: Object.entries(IssueTypeName)
|
||||||
.reverse()
|
.reverse()
|
||||||
.map(([key, value]) => ({
|
.map(([key, value]) => ({
|
||||||
@@ -265,7 +262,7 @@ const Page: React.FC = () => {
|
|||||||
height: "100%",
|
height: "100%",
|
||||||
}}
|
}}
|
||||||
source={{
|
source={{
|
||||||
uri: jellyseerrApi?.imageProxy(
|
uri: seerrApi?.imageProxy(
|
||||||
result.backdropPath,
|
result.backdropPath,
|
||||||
"w1920_and_h800_multi_faces",
|
"w1920_and_h800_multi_faces",
|
||||||
),
|
),
|
||||||
@@ -295,7 +292,7 @@ const Page: React.FC = () => {
|
|||||||
<View className='px-4'>
|
<View className='px-4'>
|
||||||
<View className='flex flex-row justify-between w-full'>
|
<View className='flex flex-row justify-between w-full'>
|
||||||
<View className='flex flex-col w-56'>
|
<View className='flex flex-col w-56'>
|
||||||
<JellyserrRatings
|
<SeerrRatings
|
||||||
result={
|
result={
|
||||||
result as
|
result as
|
||||||
| MovieResult
|
| MovieResult
|
||||||
@@ -330,7 +327,7 @@ const Page: React.FC = () => {
|
|||||||
/>
|
/>
|
||||||
) : canRequest ? (
|
) : canRequest ? (
|
||||||
<Button color='purple' onPress={request} className='mt-4'>
|
<Button color='purple' onPress={request} className='mt-4'>
|
||||||
{t("jellyseerr.request_button")}
|
{t("seerr.request_button")}
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
details?.mediaInfo?.jellyfinMediaId && (
|
details?.mediaInfo?.jellyfinMediaId && (
|
||||||
@@ -353,7 +350,7 @@ const Page: React.FC = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text className='text-sm'>
|
<Text className='text-sm'>
|
||||||
{t("jellyseerr.report_issue_button")}
|
{t("seerr.report_issue_button")}
|
||||||
</Text>
|
</Text>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
@@ -389,12 +386,12 @@ const Page: React.FC = () => {
|
|||||||
<View className='flex flex-row items-center space-x-2'>
|
<View className='flex flex-row items-center space-x-2'>
|
||||||
<Ionicons name='person-outline' size={16} color='#9CA3AF' />
|
<Ionicons name='person-outline' size={16} color='#9CA3AF' />
|
||||||
<Text className='text-sm text-neutral-400'>
|
<Text className='text-sm text-neutral-400'>
|
||||||
{t("jellyseerr.requested_by", {
|
{t("seerr.requested_by", {
|
||||||
user:
|
user:
|
||||||
pendingRequest.requestedBy?.displayName ||
|
pendingRequest.requestedBy?.displayName ||
|
||||||
pendingRequest.requestedBy?.username ||
|
pendingRequest.requestedBy?.username ||
|
||||||
pendingRequest.requestedBy?.jellyfinUsername ||
|
pendingRequest.requestedBy?.jellyfinUsername ||
|
||||||
t("jellyseerr.unknown_user"),
|
t("seerr.unknown_user"),
|
||||||
})}
|
})}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
@@ -415,7 +412,7 @@ const Page: React.FC = () => {
|
|||||||
borderStyle: "solid",
|
borderStyle: "solid",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text className='text-sm'>{t("jellyseerr.approve")}</Text>
|
<Text className='text-sm'>{t("seerr.approve")}</Text>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
className='flex-1 bg-red-600/50 border-red-400 ring-red-400 text-red-100'
|
className='flex-1 bg-red-600/50 border-red-400 ring-red-400 text-red-100'
|
||||||
@@ -433,7 +430,7 @@ const Page: React.FC = () => {
|
|||||||
borderStyle: "solid",
|
borderStyle: "solid",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text className='text-sm'>{t("jellyseerr.decline")}</Text>
|
<Text className='text-sm'>{t("seerr.decline")}</Text>
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@@ -442,7 +439,7 @@ const Page: React.FC = () => {
|
|||||||
</View>
|
</View>
|
||||||
|
|
||||||
{mediaType === MediaType.TV && (
|
{mediaType === MediaType.TV && (
|
||||||
<JellyseerrSeasons
|
<SeerrSeasons
|
||||||
isLoading={isLoading || isFetching}
|
isLoading={isLoading || isFetching}
|
||||||
details={details as TvDetails}
|
details={details as TvDetails}
|
||||||
refetch={refetch}
|
refetch={refetch}
|
||||||
@@ -491,13 +488,13 @@ const Page: React.FC = () => {
|
|||||||
<View className='flex flex-col space-y-4 px-4 pb-8 pt-2'>
|
<View className='flex flex-col space-y-4 px-4 pb-8 pt-2'>
|
||||||
<View>
|
<View>
|
||||||
<Text className='font-bold text-2xl text-neutral-100'>
|
<Text className='font-bold text-2xl text-neutral-100'>
|
||||||
{t("jellyseerr.whats_wrong")}
|
{t("seerr.whats_wrong")}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className='flex flex-col space-y-2 items-start'>
|
<View className='flex flex-col space-y-2 items-start'>
|
||||||
<View className='flex flex-col w-full'>
|
<View className='flex flex-col w-full'>
|
||||||
<Text className='opacity-50 mb-1 text-xs'>
|
<Text className='opacity-50 mb-1 text-xs'>
|
||||||
{t("jellyseerr.issue_type")}
|
{t("seerr.issue_type")}
|
||||||
</Text>
|
</Text>
|
||||||
<PlatformDropdown
|
<PlatformDropdown
|
||||||
groups={issueTypeOptionGroups}
|
groups={issueTypeOptionGroups}
|
||||||
@@ -506,11 +503,11 @@ const Page: React.FC = () => {
|
|||||||
<Text numberOfLines={1}>
|
<Text numberOfLines={1}>
|
||||||
{issueType
|
{issueType
|
||||||
? IssueTypeName[issueType]
|
? IssueTypeName[issueType]
|
||||||
: t("jellyseerr.select_an_issue")}
|
: t("seerr.select_an_issue")}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
}
|
}
|
||||||
title={t("jellyseerr.types")}
|
title={t("seerr.types")}
|
||||||
open={issueTypeDropdownOpen}
|
open={issueTypeDropdownOpen}
|
||||||
onOpenChange={setIssueTypeDropdownOpen}
|
onOpenChange={setIssueTypeDropdownOpen}
|
||||||
/>
|
/>
|
||||||
@@ -522,7 +519,7 @@ const Page: React.FC = () => {
|
|||||||
maxLength={254}
|
maxLength={254}
|
||||||
style={{ color: "white" }}
|
style={{ color: "white" }}
|
||||||
clearButtonMode='always'
|
clearButtonMode='always'
|
||||||
placeholder={t("jellyseerr.describe_the_issue")}
|
placeholder={t("seerr.describe_the_issue")}
|
||||||
placeholderTextColor='#9CA3AF'
|
placeholderTextColor='#9CA3AF'
|
||||||
// Issue with multiline + Textinput inside a portal
|
// Issue with multiline + Textinput inside a portal
|
||||||
// https://github.com/callstack/react-native-paper/issues/1668
|
// https://github.com/callstack/react-native-paper/issues/1668
|
||||||
@@ -532,7 +529,7 @@ const Page: React.FC = () => {
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<Button className='mt-auto' onPress={submitIssue} color='purple'>
|
<Button className='mt-auto' onPress={submitIssue} color='purple'>
|
||||||
{t("jellyseerr.submit_button")}
|
{t("seerr.submit_button")}
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
</BottomSheetView>
|
</BottomSheetView>
|
||||||
@@ -5,31 +5,27 @@ import { orderBy, uniqBy } from "lodash";
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Text } from "@/components/common/Text";
|
import { Text } from "@/components/common/Text";
|
||||||
import ParallaxSlideShow from "@/components/jellyseerr/ParallaxSlideShow";
|
|
||||||
import { OverviewText } from "@/components/OverviewText";
|
import { OverviewText } from "@/components/OverviewText";
|
||||||
import JellyseerrPoster from "@/components/posters/JellyseerrPoster";
|
import SeerrPoster from "@/components/posters/SeerrPoster";
|
||||||
import { useJellyseerr } from "@/hooks/useJellyseerr";
|
import ParallaxSlideShow from "@/components/seerr/ParallaxSlideShow";
|
||||||
|
import { useSeerr } from "@/hooks/useSeerr";
|
||||||
import type { PersonCreditCast } from "@/utils/jellyseerr/server/models/Person";
|
import type { PersonCreditCast } from "@/utils/jellyseerr/server/models/Person";
|
||||||
|
|
||||||
export default function page() {
|
export default function PersonPage() {
|
||||||
const local = useLocalSearchParams();
|
const local = useLocalSearchParams();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const {
|
const { seerrApi, seerrLocale: locale } = useSeerr();
|
||||||
jellyseerrApi,
|
|
||||||
jellyseerrRegion: region,
|
|
||||||
jellyseerrLocale: locale,
|
|
||||||
} = useJellyseerr();
|
|
||||||
|
|
||||||
const { personId } = local as { personId: string };
|
const { personId } = local as { personId: string };
|
||||||
|
|
||||||
const { data } = useQuery({
|
const { data } = useQuery({
|
||||||
queryKey: ["jellyseerr", "person", personId],
|
queryKey: ["seerr", "person", personId],
|
||||||
queryFn: async () => ({
|
queryFn: async () => ({
|
||||||
details: await jellyseerrApi?.personDetails(personId),
|
details: await seerrApi?.personDetails(personId),
|
||||||
combinedCredits: await jellyseerrApi?.personCombinedCredits(personId),
|
combinedCredits: await seerrApi?.personCombinedCredits(personId),
|
||||||
}),
|
}),
|
||||||
enabled: !!jellyseerrApi && !!personId,
|
enabled: !!seerrApi && !!personId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const castedRoles: PersonCreditCast[] = useMemo(
|
const castedRoles: PersonCreditCast[] = useMemo(
|
||||||
@@ -46,22 +42,19 @@ export default function page() {
|
|||||||
);
|
);
|
||||||
const backdrops = useMemo(
|
const backdrops = useMemo(
|
||||||
() =>
|
() =>
|
||||||
jellyseerrApi
|
seerrApi
|
||||||
? castedRoles.map((c) =>
|
? castedRoles.map((c) =>
|
||||||
jellyseerrApi.imageProxy(
|
seerrApi.imageProxy(c.backdropPath, "w1920_and_h800_multi_faces"),
|
||||||
c.backdropPath,
|
|
||||||
"w1920_and_h800_multi_faces",
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
: [],
|
: [],
|
||||||
[jellyseerrApi, data?.combinedCredits],
|
[seerrApi, data?.combinedCredits],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ParallaxSlideShow
|
<ParallaxSlideShow
|
||||||
data={castedRoles}
|
data={castedRoles}
|
||||||
images={backdrops}
|
images={backdrops}
|
||||||
listHeader={t("jellyseerr.appearances")}
|
listHeader={t("seerr.appearances")}
|
||||||
keyExtractor={(item) => item.id.toString()}
|
keyExtractor={(item) => item.id.toString()}
|
||||||
logo={
|
logo={
|
||||||
<Image
|
<Image
|
||||||
@@ -69,7 +62,7 @@ export default function page() {
|
|||||||
id={data?.details?.id.toString()}
|
id={data?.details?.id.toString()}
|
||||||
className='rounded-full bottom-1'
|
className='rounded-full bottom-1'
|
||||||
source={{
|
source={{
|
||||||
uri: jellyseerrApi?.imageProxy(
|
uri: seerrApi?.imageProxy(
|
||||||
data?.details?.profilePath,
|
data?.details?.profilePath,
|
||||||
"w600_and_h600_bestv2",
|
"w600_and_h600_bestv2",
|
||||||
),
|
),
|
||||||
@@ -86,16 +79,13 @@ export default function page() {
|
|||||||
<>
|
<>
|
||||||
<Text className='font-bold text-2xl mb-1'>{data?.details?.name}</Text>
|
<Text className='font-bold text-2xl mb-1'>{data?.details?.name}</Text>
|
||||||
<Text className='opacity-50'>
|
<Text className='opacity-50'>
|
||||||
{t("jellyseerr.born")}{" "}
|
{t("seerr.born")}{" "}
|
||||||
{data?.details?.birthday &&
|
{data?.details?.birthday &&
|
||||||
new Date(data.details.birthday).toLocaleDateString(
|
new Date(data.details.birthday).toLocaleDateString(locale, {
|
||||||
`${locale}-${region}`,
|
year: "numeric",
|
||||||
{
|
month: "long",
|
||||||
year: "numeric",
|
day: "numeric",
|
||||||
month: "long",
|
})}{" "}
|
||||||
day: "numeric",
|
|
||||||
},
|
|
||||||
)}{" "}
|
|
||||||
| {data?.details?.placeOfBirth}
|
| {data?.details?.placeOfBirth}
|
||||||
</Text>
|
</Text>
|
||||||
</>
|
</>
|
||||||
@@ -103,7 +93,7 @@ export default function page() {
|
|||||||
MainContent={() => (
|
MainContent={() => (
|
||||||
<OverviewText text={data?.details?.biography} className='mt-4' />
|
<OverviewText text={data?.details?.biography} className='mt-4' />
|
||||||
)}
|
)}
|
||||||
renderItem={(item, _index) => <JellyseerrPoster item={item} />}
|
renderItem={(item, _index) => <SeerrPoster item={item} />}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -33,17 +33,17 @@ export default function SearchLayout() {
|
|||||||
headerShadowVisible: false,
|
headerShadowVisible: false,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen name='jellyseerr/page' options={commonScreenOptions} />
|
<Stack.Screen name='seerr/page' options={commonScreenOptions} />
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name='jellyseerr/person/[personId]'
|
name='seerr/person/[personId]'
|
||||||
options={commonScreenOptions}
|
options={commonScreenOptions}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name='jellyseerr/company/[companyId]'
|
name='seerr/company/[companyId]'
|
||||||
options={commonScreenOptions}
|
options={commonScreenOptions}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name='jellyseerr/genre/[genreId]'
|
name='seerr/genre/[genreId]'
|
||||||
options={commonScreenOptions}
|
options={commonScreenOptions}
|
||||||
/>
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -26,18 +26,18 @@ import { Input } from "@/components/common/Input";
|
|||||||
import { Text } from "@/components/common/Text";
|
import { Text } from "@/components/common/Text";
|
||||||
import { TouchableItemRouter } from "@/components/common/TouchableItemRouter";
|
import { TouchableItemRouter } from "@/components/common/TouchableItemRouter";
|
||||||
import { ItemCardText } from "@/components/ItemCardText";
|
import { ItemCardText } from "@/components/ItemCardText";
|
||||||
import {
|
|
||||||
JellyseerrSearchSort,
|
|
||||||
JellyserrIndexPage,
|
|
||||||
} from "@/components/jellyseerr/JellyseerrIndexPage";
|
|
||||||
import MoviePoster from "@/components/posters/MoviePoster";
|
import MoviePoster from "@/components/posters/MoviePoster";
|
||||||
import SeriesPoster from "@/components/posters/SeriesPoster";
|
import SeriesPoster from "@/components/posters/SeriesPoster";
|
||||||
import { DiscoverFilters } from "@/components/search/DiscoverFilters";
|
import { DiscoverFilters } from "@/components/search/DiscoverFilters";
|
||||||
import { LoadingSkeleton } from "@/components/search/LoadingSkeleton";
|
import { LoadingSkeleton } from "@/components/search/LoadingSkeleton";
|
||||||
import { SearchItemWrapper } from "@/components/search/SearchItemWrapper";
|
import { SearchItemWrapper } from "@/components/search/SearchItemWrapper";
|
||||||
import { SearchTabButtons } from "@/components/search/SearchTabButtons";
|
import { SearchTabButtons } from "@/components/search/SearchTabButtons";
|
||||||
|
import {
|
||||||
|
SeerrIndexPage,
|
||||||
|
SeerrSearchSort,
|
||||||
|
} from "@/components/seerr/SeerrIndexPage";
|
||||||
import useRouter from "@/hooks/useAppRouter";
|
import useRouter from "@/hooks/useAppRouter";
|
||||||
import { useJellyseerr } from "@/hooks/useJellyseerr";
|
import { useSeerr } from "@/hooks/useSeerr";
|
||||||
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
import { apiAtom, userAtom } from "@/providers/JellyfinProvider";
|
||||||
import { useSettings } from "@/utils/atoms/settings";
|
import { useSettings } from "@/utils/atoms/settings";
|
||||||
import { eventBus } from "@/utils/eventBus";
|
import { eventBus } from "@/utils/eventBus";
|
||||||
@@ -55,7 +55,7 @@ const exampleSearches = [
|
|||||||
"The Mandalorian",
|
"The Mandalorian",
|
||||||
];
|
];
|
||||||
|
|
||||||
export default function search() {
|
export default function SearchPage() {
|
||||||
const params = useLocalSearchParams();
|
const params = useLocalSearchParams();
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -93,16 +93,11 @@ export default function search() {
|
|||||||
const [api] = useAtom(apiAtom);
|
const [api] = useAtom(apiAtom);
|
||||||
|
|
||||||
const { settings } = useSettings();
|
const { settings } = useSettings();
|
||||||
const { jellyseerrApi } = useJellyseerr();
|
const { seerrApi } = useSeerr();
|
||||||
const [jellyseerrOrderBy, setJellyseerrOrderBy] =
|
const [seerrOrderBy, setSeerrOrderBy] = useState<SeerrSearchSort>(
|
||||||
useState<JellyseerrSearchSort>(
|
SeerrSearchSort[SeerrSearchSort.DEFAULT] as unknown as SeerrSearchSort,
|
||||||
JellyseerrSearchSort[
|
);
|
||||||
JellyseerrSearchSort.DEFAULT
|
const [seerrSortOrder, setSeerrSortOrder] = useState<"asc" | "desc">("desc");
|
||||||
] as unknown as JellyseerrSearchSort,
|
|
||||||
);
|
|
||||||
const [jellyseerrSortOrder, setJellyseerrSortOrder] = useState<
|
|
||||||
"asc" | "desc"
|
|
||||||
>("desc");
|
|
||||||
|
|
||||||
const searchEngine = useMemo(() => {
|
const searchEngine = useMemo(() => {
|
||||||
return settings?.searchEngine || "Jellyfin";
|
return settings?.searchEngine || "Jellyfin";
|
||||||
@@ -474,7 +469,7 @@ export default function search() {
|
|||||||
className='flex flex-col'
|
className='flex flex-col'
|
||||||
style={{ paddingTop: Platform.OS === "android" ? 10 : 0 }}
|
style={{ paddingTop: Platform.OS === "android" ? 10 : 0 }}
|
||||||
>
|
>
|
||||||
{jellyseerrApi && (
|
{seerrApi && (
|
||||||
<View className='pl-4 pr-4 flex flex-row'>
|
<View className='pl-4 pr-4 flex flex-row'>
|
||||||
<SearchTabButtons
|
<SearchTabButtons
|
||||||
searchType={searchType}
|
searchType={searchType}
|
||||||
@@ -488,10 +483,10 @@ export default function search() {
|
|||||||
<DiscoverFilters
|
<DiscoverFilters
|
||||||
searchFilterId={searchFilterId}
|
searchFilterId={searchFilterId}
|
||||||
orderFilterId={orderFilterId}
|
orderFilterId={orderFilterId}
|
||||||
jellyseerrOrderBy={jellyseerrOrderBy}
|
seerrOrderBy={seerrOrderBy}
|
||||||
setJellyseerrOrderBy={setJellyseerrOrderBy}
|
setSeerrOrderBy={setSeerrOrderBy}
|
||||||
jellyseerrSortOrder={jellyseerrSortOrder}
|
seerrSortOrder={seerrSortOrder}
|
||||||
setJellyseerrSortOrder={setJellyseerrSortOrder}
|
setSeerrSortOrder={setSeerrSortOrder}
|
||||||
t={t}
|
t={t}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -754,10 +749,10 @@ export default function search() {
|
|||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
) : (
|
) : (
|
||||||
<JellyserrIndexPage
|
<SeerrIndexPage
|
||||||
searchQuery={debouncedSearch}
|
searchQuery={debouncedSearch}
|
||||||
sortType={jellyseerrOrderBy}
|
sortType={seerrOrderBy}
|
||||||
order={jellyseerrSortOrder}
|
order={seerrSortOrder}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -72,9 +72,6 @@ export default function page() {
|
|||||||
const [isPlaybackStopped, setIsPlaybackStopped] = useState(false);
|
const [isPlaybackStopped, setIsPlaybackStopped] = useState(false);
|
||||||
const [showControls, _setShowControls] = useState(true);
|
const [showControls, _setShowControls] = useState(true);
|
||||||
const [isPipMode, setIsPipMode] = useState(false);
|
const [isPipMode, setIsPipMode] = useState(false);
|
||||||
const [aspectRatio] = useState<"default" | "16:9" | "4:3" | "1:1" | "21:9">(
|
|
||||||
"default",
|
|
||||||
);
|
|
||||||
const [isZoomedToFill, setIsZoomedToFill] = useState(false);
|
const [isZoomedToFill, setIsZoomedToFill] = useState(false);
|
||||||
const [isPlaying, setIsPlaying] = useState(false);
|
const [isPlaying, setIsPlaying] = useState(false);
|
||||||
const [isMuted, setIsMuted] = useState(false);
|
const [isMuted, setIsMuted] = useState(false);
|
||||||
@@ -1008,7 +1005,6 @@ export default function page() {
|
|||||||
pause={pause}
|
pause={pause}
|
||||||
seek={seek}
|
seek={seek}
|
||||||
enableTrickplay={true}
|
enableTrickplay={true}
|
||||||
aspectRatio={aspectRatio}
|
|
||||||
isZoomedToFill={isZoomedToFill}
|
isZoomedToFill={isZoomedToFill}
|
||||||
onZoomToggle={handleZoomToggle}
|
onZoomToggle={handleZoomToggle}
|
||||||
api={api}
|
api={api}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 1.9 MiB |
@@ -1,4 +1,3 @@
|
|||||||
export * from "./api";
|
export * from "./api";
|
||||||
export * from "./mmkv";
|
export * from "./mmkv";
|
||||||
export * from "./number";
|
export * from "./number";
|
||||||
export * from "./string";
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ declare global {
|
|||||||
bytesToReadable(decimals?: number): string;
|
bytesToReadable(decimals?: number): string;
|
||||||
secondsToMilliseconds(): number;
|
secondsToMilliseconds(): number;
|
||||||
minutesToMilliseconds(): number;
|
minutesToMilliseconds(): number;
|
||||||
hoursToMilliseconds(): number;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,8 +27,4 @@ Number.prototype.minutesToMilliseconds = function () {
|
|||||||
return this.valueOf() * (60).secondsToMilliseconds();
|
return this.valueOf() * (60).secondsToMilliseconds();
|
||||||
};
|
};
|
||||||
|
|
||||||
Number.prototype.hoursToMilliseconds = function () {
|
|
||||||
return this.valueOf() * (60).minutesToMilliseconds();
|
|
||||||
};
|
|
||||||
|
|
||||||
export {};
|
export {};
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
declare global {
|
|
||||||
interface String {
|
|
||||||
toTitle(): string;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String.prototype.toTitle = function () {
|
|
||||||
return this.replaceAll("_", " ").replace(
|
|
||||||
/\w\S*/g,
|
|
||||||
(text) => text.charAt(0).toUpperCase() + text.substring(1).toLowerCase(),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export {};
|
|
||||||
232
bun.lock
232
bun.lock
@@ -50,14 +50,14 @@
|
|||||||
"expo-system-ui": "~6.0.9",
|
"expo-system-ui": "~6.0.9",
|
||||||
"expo-task-manager": "14.0.9",
|
"expo-task-manager": "14.0.9",
|
||||||
"expo-web-browser": "~15.0.10",
|
"expo-web-browser": "~15.0.10",
|
||||||
"i18next": "^25.0.0",
|
"i18next": "^26.0.0",
|
||||||
"jotai": "2.16.2",
|
"jotai": "2.16.2",
|
||||||
"lodash": "4.17.23",
|
"lodash": "4.17.23",
|
||||||
"nativewind": "^2.0.11",
|
"nativewind": "^2.0.11",
|
||||||
"patch-package": "^8.0.0",
|
"patch-package": "^8.0.0",
|
||||||
"react": "19.1.0",
|
"react": "19.1.0",
|
||||||
"react-dom": "19.1.0",
|
"react-dom": "19.1.0",
|
||||||
"react-i18next": "16.5.4",
|
"react-i18next": "17.0.8",
|
||||||
"react-native": "0.81.5",
|
"react-native": "0.81.5",
|
||||||
"react-native-awesome-slider": "^2.9.0",
|
"react-native-awesome-slider": "^2.9.0",
|
||||||
"react-native-bottom-tabs": "1.1.0",
|
"react-native-bottom-tabs": "1.1.0",
|
||||||
@@ -97,16 +97,16 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.28.6",
|
"@babel/core": "7.28.6",
|
||||||
"@biomejs/biome": "2.3.11",
|
"@biomejs/biome": "2.3.11",
|
||||||
"@react-native-community/cli": "20.1.1",
|
"@react-native-community/cli": "20.1.3",
|
||||||
"@react-native-tvos/config-tv": "0.1.4",
|
"@react-native-tvos/config-tv": "0.1.6",
|
||||||
"@types/jest": "29.5.14",
|
"@types/jest": "29.5.14",
|
||||||
"@types/lodash": "4.17.23",
|
"@types/lodash": "4.17.23",
|
||||||
"@types/react": "19.1.17",
|
"@types/react": "19.1.17",
|
||||||
"@types/react-test-renderer": "19.1.0",
|
"@types/react-test-renderer": "19.1.0",
|
||||||
"cross-env": "10.1.0",
|
"cross-env": "10.1.0",
|
||||||
"expo-doctor": "1.17.14",
|
"expo-doctor": "1.18.22",
|
||||||
"husky": "9.1.7",
|
"husky": "9.1.7",
|
||||||
"lint-staged": "16.2.7",
|
"lint-staged": "17.0.5",
|
||||||
"react-test-renderer": "19.2.3",
|
"react-test-renderer": "19.2.3",
|
||||||
"typescript": "5.9.3",
|
"typescript": "5.9.3",
|
||||||
},
|
},
|
||||||
@@ -404,7 +404,7 @@
|
|||||||
|
|
||||||
"@isaacs/brace-expansion": ["@isaacs/brace-expansion@5.0.0", "", { "dependencies": { "@isaacs/balanced-match": "^4.0.1" } }, "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA=="],
|
"@isaacs/brace-expansion": ["@isaacs/brace-expansion@5.0.0", "", { "dependencies": { "@isaacs/balanced-match": "^4.0.1" } }, "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA=="],
|
||||||
|
|
||||||
"@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
|
"@isaacs/cliui": ["@isaacs/cliui@9.0.0", "", {}, "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg=="],
|
||||||
|
|
||||||
"@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="],
|
"@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="],
|
||||||
|
|
||||||
@@ -462,6 +462,8 @@
|
|||||||
|
|
||||||
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
||||||
|
|
||||||
|
"@nodable/entities": ["@nodable/entities@2.1.0", "", {}, "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA=="],
|
||||||
|
|
||||||
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
||||||
|
|
||||||
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
|
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
|
||||||
@@ -512,33 +514,33 @@
|
|||||||
|
|
||||||
"@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
|
"@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="],
|
||||||
|
|
||||||
"@react-native-community/cli": ["@react-native-community/cli@20.1.1", "", { "dependencies": { "@react-native-community/cli-clean": "20.1.1", "@react-native-community/cli-config": "20.1.1", "@react-native-community/cli-doctor": "20.1.1", "@react-native-community/cli-server-api": "20.1.1", "@react-native-community/cli-tools": "20.1.1", "@react-native-community/cli-types": "20.1.1", "commander": "^9.4.1", "deepmerge": "^4.3.0", "execa": "^5.0.0", "find-up": "^5.0.0", "fs-extra": "^8.1.0", "graceful-fs": "^4.1.3", "picocolors": "^1.1.1", "prompts": "^2.4.2", "semver": "^7.5.2" }, "bin": { "rnc-cli": "build/bin.js" } }, "sha512-aLPUx43+WSeTOaUepR2FBD5a1V0OAZ1QB2DOlRlW4fOEjtBXgv40eM/ho8g3WCvAOKfPvTvx4fZdcuovTyV81Q=="],
|
"@react-native-community/cli": ["@react-native-community/cli@20.1.3", "", { "dependencies": { "@react-native-community/cli-clean": "20.1.3", "@react-native-community/cli-config": "20.1.3", "@react-native-community/cli-doctor": "20.1.3", "@react-native-community/cli-server-api": "20.1.3", "@react-native-community/cli-tools": "20.1.3", "@react-native-community/cli-types": "20.1.3", "commander": "^9.4.1", "deepmerge": "^4.3.0", "execa": "^5.0.0", "find-up": "^5.0.0", "fs-extra": "^8.1.0", "graceful-fs": "^4.1.3", "picocolors": "^1.1.1", "prompts": "^2.4.2", "semver": "^7.5.2" }, "bin": { "rnc-cli": "build/bin.js" } }, "sha512-sLo8cu9JyFNfuuF1C+8NJ4DHE/PEFaXGd4enkcxi/OJjGG8+sOQrdjNQ4i+cVh/2c+ah1mEMwsYjc3z0+/MqSg=="],
|
||||||
|
|
||||||
"@react-native-community/cli-clean": ["@react-native-community/cli-clean@20.1.1", "", { "dependencies": { "@react-native-community/cli-tools": "20.1.1", "execa": "^5.0.0", "fast-glob": "^3.3.2", "picocolors": "^1.1.1" } }, "sha512-6nGQ08w2+EcDwTFC4JFiW/wI2pLwzMrk9thz4um7tKRNW8sADX0IyCsfM2F4rHS720C0UNKYBZE9nAsfp8Vkcw=="],
|
"@react-native-community/cli-clean": ["@react-native-community/cli-clean@20.1.3", "", { "dependencies": { "@react-native-community/cli-tools": "20.1.3", "execa": "^5.0.0", "fast-glob": "^3.3.2", "picocolors": "^1.1.1" } }, "sha512-sFLdLzapfC0scjgzBJJWYDY2RhHPjuuPkA5r6q0gc/UQH/izXpMpLrhh1DW84cMDraNACK0U62tU7ebNaQ1LMQ=="],
|
||||||
|
|
||||||
"@react-native-community/cli-config": ["@react-native-community/cli-config@20.1.1", "", { "dependencies": { "@react-native-community/cli-tools": "20.1.1", "cosmiconfig": "^9.0.0", "deepmerge": "^4.3.0", "fast-glob": "^3.3.2", "joi": "^17.2.1", "picocolors": "^1.1.1" } }, "sha512-ajs2i56MANie/v0bMQ1BmRcrOb6MEvLT2rh/I1CA62NXGqF1Rxv6QwsN84LrADMXHRg8QiEMAIADkyDeQHt7Kg=="],
|
"@react-native-community/cli-config": ["@react-native-community/cli-config@20.1.3", "", { "dependencies": { "@react-native-community/cli-tools": "20.1.3", "cosmiconfig": "^9.0.0", "deepmerge": "^4.3.0", "fast-glob": "^3.3.2", "joi": "^17.2.1", "picocolors": "^1.1.1" } }, "sha512-n73nW0cG92oNF0r994pPqm0DjAShOm3F8LSffDYhJqNAno+h/csmv/37iL4NtSpmKIO8xqsG3uVTXz9X/hzNaQ=="],
|
||||||
|
|
||||||
"@react-native-community/cli-config-android": ["@react-native-community/cli-config-android@20.1.1", "", { "dependencies": { "@react-native-community/cli-tools": "20.1.1", "fast-glob": "^3.3.2", "fast-xml-parser": "^4.4.1", "picocolors": "^1.1.1" } }, "sha512-1iUV2rPAyoWPo8EceAFC2vZTF+pEd9YqS87c0aqpbGOFE0gs1rHEB+auVR8CdjzftR4U9sq6m2jrdst0rvpIkg=="],
|
"@react-native-community/cli-config-android": ["@react-native-community/cli-config-android@20.1.3", "", { "dependencies": { "@react-native-community/cli-tools": "20.1.3", "fast-glob": "^3.3.2", "fast-xml-parser": "^5.3.6", "picocolors": "^1.1.1" } }, "sha512-DNHDP+OWLyhKShGciBqPcxhxfp1Z/7GQcb4F+TGyCeKQAr+JdnUjRXN3X+YCU/v+g2kbYYyRJKlGabzkVvdrAw=="],
|
||||||
|
|
||||||
"@react-native-community/cli-config-apple": ["@react-native-community/cli-config-apple@20.1.1", "", { "dependencies": { "@react-native-community/cli-tools": "20.1.1", "execa": "^5.0.0", "fast-glob": "^3.3.2", "picocolors": "^1.1.1" } }, "sha512-doepJgLJVqeJb5tNoP9hyFIcoZ1OMGO7QN/YMuCCIjbThUQe/J87XdwPol3Qrjr58KRt9xeBVz+kHeW5mtSutw=="],
|
"@react-native-community/cli-config-apple": ["@react-native-community/cli-config-apple@20.1.3", "", { "dependencies": { "@react-native-community/cli-tools": "20.1.3", "execa": "^5.0.0", "fast-glob": "^3.3.2", "picocolors": "^1.1.1" } }, "sha512-QX9B83nAfCPs0KiaYz61kAEHWr9sttooxzRzNdQwvZTwnsIpvWOT9GvMMj/19OeXiQzMJBzZX0Pgt6+spiUsDQ=="],
|
||||||
|
|
||||||
"@react-native-community/cli-doctor": ["@react-native-community/cli-doctor@20.1.1", "", { "dependencies": { "@react-native-community/cli-config": "20.1.1", "@react-native-community/cli-platform-android": "20.1.1", "@react-native-community/cli-platform-apple": "20.1.1", "@react-native-community/cli-platform-ios": "20.1.1", "@react-native-community/cli-tools": "20.1.1", "command-exists": "^1.2.8", "deepmerge": "^4.3.0", "envinfo": "^7.13.0", "execa": "^5.0.0", "node-stream-zip": "^1.9.1", "ora": "^5.4.1", "picocolors": "^1.1.1", "semver": "^7.5.2", "wcwidth": "^1.0.1", "yaml": "^2.2.1" } }, "sha512-eFpg5wWnV7uGqvLemshpgj2trPD8cckqxBuI4nT7sxKF/YpA/e3nnnyytHxPP5EnYfWbMcqfaq8hDJoOnJinGQ=="],
|
"@react-native-community/cli-doctor": ["@react-native-community/cli-doctor@20.1.3", "", { "dependencies": { "@react-native-community/cli-config": "20.1.3", "@react-native-community/cli-platform-android": "20.1.3", "@react-native-community/cli-platform-apple": "20.1.3", "@react-native-community/cli-platform-ios": "20.1.3", "@react-native-community/cli-tools": "20.1.3", "command-exists": "^1.2.8", "deepmerge": "^4.3.0", "envinfo": "^7.13.0", "execa": "^5.0.0", "node-stream-zip": "^1.9.1", "ora": "^5.4.1", "picocolors": "^1.1.1", "semver": "^7.5.2", "wcwidth": "^1.0.1", "yaml": "^2.2.1" } }, "sha512-EI+mAPWn255/WZ4CQohy1I049yiaxVr41C3BeQ2BCyhxODIDR8XRsLzYb1t9MfqK/C3ZncUN2mPSRXFeKPPI1w=="],
|
||||||
|
|
||||||
"@react-native-community/cli-platform-android": ["@react-native-community/cli-platform-android@20.1.1", "", { "dependencies": { "@react-native-community/cli-config-android": "20.1.1", "@react-native-community/cli-tools": "20.1.1", "execa": "^5.0.0", "logkitty": "^0.7.1", "picocolors": "^1.1.1" } }, "sha512-KPheizJQI0tVvBLy9owzpo+A9qDsDAa87e7a8xNaHnwqGpExnIzFPrbdvrltiZjstU2eB/+/UgNQxYIEd4Oc+g=="],
|
"@react-native-community/cli-platform-android": ["@react-native-community/cli-platform-android@20.1.3", "", { "dependencies": { "@react-native-community/cli-config-android": "20.1.3", "@react-native-community/cli-tools": "20.1.3", "execa": "^5.0.0", "logkitty": "^0.7.1", "picocolors": "^1.1.1" } }, "sha512-bzB9ELPOISuqgtDZXFPQlkuxx1YFkNx3cNgslc5ElCrk+5LeCLQLIBh/dmIuK8rwUrPcrramjeBj++Noc+TaAA=="],
|
||||||
|
|
||||||
"@react-native-community/cli-platform-apple": ["@react-native-community/cli-platform-apple@20.1.1", "", { "dependencies": { "@react-native-community/cli-config-apple": "20.1.1", "@react-native-community/cli-tools": "20.1.1", "execa": "^5.0.0", "fast-xml-parser": "^4.4.1", "picocolors": "^1.1.1" } }, "sha512-mQEjOzRFCcQTrCt73Q/+5WWTfUg6U2vLZv5rPuFiNrLbrwRqxVH3OLaXg5gilJkDTJC80z8iOSsdd8MRxONOig=="],
|
"@react-native-community/cli-platform-apple": ["@react-native-community/cli-platform-apple@20.1.3", "", { "dependencies": { "@react-native-community/cli-config-apple": "20.1.3", "@react-native-community/cli-tools": "20.1.3", "execa": "^5.0.0", "fast-xml-parser": "^5.3.6", "picocolors": "^1.1.1" } }, "sha512-XJ+DqAD4hkplWVXK5AMgN7pP9+4yRSe5KfZ/b42+ofkDBI55ALlUmX+9HWE3fMuRjcotTCoNZqX2ov97cFDXpQ=="],
|
||||||
|
|
||||||
"@react-native-community/cli-platform-ios": ["@react-native-community/cli-platform-ios@20.1.1", "", { "dependencies": { "@react-native-community/cli-platform-apple": "20.1.1" } }, "sha512-6vr10/oSjKkZO/BBgfFJNQTC/0CDF4WrN8iW9ss+Kt6ZL2QrBXLYz7fobrrboOlHwqqs5EyQadlEaNii7gKRJg=="],
|
"@react-native-community/cli-platform-ios": ["@react-native-community/cli-platform-ios@20.1.3", "", { "dependencies": { "@react-native-community/cli-platform-apple": "20.1.3" } }, "sha512-2qL48SINotuHbZO73cgqSwqd/OWNx0xTbFSdujhpogV4p8BNwYYypfjh4vJY5qJEB5PxuoVkMXT+aCADpg9nBg=="],
|
||||||
|
|
||||||
"@react-native-community/cli-server-api": ["@react-native-community/cli-server-api@20.1.1", "", { "dependencies": { "@react-native-community/cli-tools": "20.1.1", "body-parser": "^1.20.3", "compression": "^1.7.1", "connect": "^3.6.5", "errorhandler": "^1.5.1", "nocache": "^3.0.1", "open": "^6.2.0", "pretty-format": "^29.7.0", "serve-static": "^1.13.1", "strict-url-sanitise": "0.0.1", "ws": "^6.2.3" } }, "sha512-phHfiCa4WqfKfaoV2vGVR3ZrYQDQTpI1k+C+i6rXAxFGxPuy8IgFFVOSL543qjKPpHBVwLcA+/xAJCVpdyCtVQ=="],
|
"@react-native-community/cli-server-api": ["@react-native-community/cli-server-api@20.1.3", "", { "dependencies": { "@react-native-community/cli-tools": "20.1.3", "body-parser": "^2.2.2", "compression": "^1.7.1", "connect": "^3.6.5", "errorhandler": "^1.5.1", "nocache": "^3.0.1", "open": "^6.2.0", "pretty-format": "^29.7.0", "serve-static": "^1.13.1", "strict-url-sanitise": "0.0.1", "ws": "^6.2.3" } }, "sha512-hsNsdUKZDd2T99OuNuiXz4VuvLa1UN0zcxefmPjXQgI0byrBLzzDr+o7p03sKuODSzKi2h+BMnUxiS07HACQLA=="],
|
||||||
|
|
||||||
"@react-native-community/cli-tools": ["@react-native-community/cli-tools@20.1.1", "", { "dependencies": { "@vscode/sudo-prompt": "^9.0.0", "appdirsjs": "^1.2.4", "execa": "^5.0.0", "find-up": "^5.0.0", "launch-editor": "^2.9.1", "mime": "^2.4.1", "ora": "^5.4.1", "picocolors": "^1.1.1", "prompts": "^2.4.2", "semver": "^7.5.2" } }, "sha512-j+zX/H2X+6ZGneIDj56tZ1Hbnip5nSfnq7yGlMyF/zm3U1hKp3G1jN5v0YEfnz/zEmjr7zruh4Y06KmZrF1lrA=="],
|
"@react-native-community/cli-tools": ["@react-native-community/cli-tools@20.1.3", "", { "dependencies": { "@vscode/sudo-prompt": "^9.0.0", "appdirsjs": "^1.2.4", "execa": "^5.0.0", "find-up": "^5.0.0", "launch-editor": "^2.9.1", "mime": "^2.4.1", "ora": "^5.4.1", "picocolors": "^1.1.1", "prompts": "^2.4.2", "semver": "^7.5.2" } }, "sha512-EAn0vPCMxtHhfWk2UwLmSUfPfLUnFgC7NjiVJVTKJyVk5qGnkPfoT8te/1IUXFTysUB0F0RIi+NgDB4usFOLeA=="],
|
||||||
|
|
||||||
"@react-native-community/cli-types": ["@react-native-community/cli-types@20.1.1", "", { "dependencies": { "joi": "^17.2.1" } }, "sha512-Tp+s27I/RDONrGvWVj4IzEmga2HhJhXi8ZlZTfycMMyAcv4LG/CTPira+BUZs8nzLAJNrlJ79pVVPJPqQAe+aw=="],
|
"@react-native-community/cli-types": ["@react-native-community/cli-types@20.1.3", "", { "dependencies": { "joi": "^17.2.1" } }, "sha512-IdAcegf0pH1hVraxWTG1ACLkYC0LDQfqtaEf42ESyLIF3Xap70JzL/9tAlxw7lSCPZPFWhrcgU0TBc4SkC/ecw=="],
|
||||||
|
|
||||||
"@react-native-community/netinfo": ["@react-native-community/netinfo@11.4.1", "", { "peerDependencies": { "react-native": ">=0.59" } }, "sha512-B0BYAkghz3Q2V09BF88RA601XursIEA111tnc2JOaN7axJWmNefmfjZqw/KdSxKZp7CZUuPpjBmz/WCR9uaHYg=="],
|
"@react-native-community/netinfo": ["@react-native-community/netinfo@11.4.1", "", { "peerDependencies": { "react-native": ">=0.59" } }, "sha512-B0BYAkghz3Q2V09BF88RA601XursIEA111tnc2JOaN7axJWmNefmfjZqw/KdSxKZp7CZUuPpjBmz/WCR9uaHYg=="],
|
||||||
|
|
||||||
"@react-native-tvos/config-tv": ["@react-native-tvos/config-tv@0.1.4", "", { "dependencies": { "getenv": "^1.0.0" }, "peerDependencies": { "expo": ">=52.0.0" } }, "sha512-xfVDqSFjEUsb+xcMk0hE2Z/M6QZH0QzAJOSQZwo7W/ZRaLrd+xFQnx0LaXqt3kxlR3P7wskKHByDP/FSoUZnbA=="],
|
"@react-native-tvos/config-tv": ["@react-native-tvos/config-tv@0.1.6", "", { "dependencies": { "getenv": "^1.0.0", "glob": "^11.0.0" }, "peerDependencies": { "expo": ">=52.0.0" } }, "sha512-VxMSIcro+U1EVb64pYShZsc+uE3HNGhfHppoUhTyGwx9ELQkhWvReRTOI4gpb/qeRWEcT+UbUc9Gd9Zlwm572w=="],
|
||||||
|
|
||||||
"@react-native/assets-registry": ["@react-native/assets-registry@0.81.5", "", {}, "sha512-705B6x/5Kxm1RKRvSv0ADYWm5JOnoiQ1ufW7h8uu2E6G9Of/eE6hP/Ivw3U5jI16ERqZxiKQwk34VJbB0niX9w=="],
|
"@react-native/assets-registry": ["@react-native/assets-registry@0.81.5", "", {}, "sha512-705B6x/5Kxm1RKRvSv0ADYWm5JOnoiQ1ufW7h8uu2E6G9Of/eE6hP/Ivw3U5jI16ERqZxiKQwk34VJbB0niX9w=="],
|
||||||
|
|
||||||
@@ -774,7 +776,7 @@
|
|||||||
|
|
||||||
"bmp-js": ["bmp-js@0.1.0", "", {}, "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw=="],
|
"bmp-js": ["bmp-js@0.1.0", "", {}, "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw=="],
|
||||||
|
|
||||||
"body-parser": ["body-parser@1.20.3", "", { "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" } }, "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g=="],
|
"body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="],
|
||||||
|
|
||||||
"boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
|
"boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
|
||||||
|
|
||||||
@@ -828,7 +830,7 @@
|
|||||||
|
|
||||||
"cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="],
|
"cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="],
|
||||||
|
|
||||||
"cli-truncate": ["cli-truncate@5.1.1", "", { "dependencies": { "slice-ansi": "^7.1.0", "string-width": "^8.0.0" } }, "sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A=="],
|
"cli-truncate": ["cli-truncate@5.2.0", "", { "dependencies": { "slice-ansi": "^8.0.0", "string-width": "^8.2.0" } }, "sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw=="],
|
||||||
|
|
||||||
"client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="],
|
"client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="],
|
||||||
|
|
||||||
@@ -844,7 +846,7 @@
|
|||||||
|
|
||||||
"color-string": ["color-string@2.1.2", "", { "dependencies": { "color-name": "^2.0.0" } }, "sha512-RxmjYxbWemV9gKu4zPgiZagUxbH3RQpEIO77XoSSX0ivgABDZ+h8Zuash/EMFLTI4N9QgFPOJ6JQpPZKFxa+dA=="],
|
"color-string": ["color-string@2.1.2", "", { "dependencies": { "color-name": "^2.0.0" } }, "sha512-RxmjYxbWemV9gKu4zPgiZagUxbH3RQpEIO77XoSSX0ivgABDZ+h8Zuash/EMFLTI4N9QgFPOJ6JQpPZKFxa+dA=="],
|
||||||
|
|
||||||
"colorette": ["colorette@2.0.20", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="],
|
"colorette": ["colorette@1.4.0", "", {}, "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g=="],
|
||||||
|
|
||||||
"combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
|
"combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
|
||||||
|
|
||||||
@@ -990,7 +992,7 @@
|
|||||||
|
|
||||||
"event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="],
|
"event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="],
|
||||||
|
|
||||||
"eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="],
|
"eventemitter3": ["eventemitter3@5.0.4", "", {}, "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw=="],
|
||||||
|
|
||||||
"events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="],
|
"events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="],
|
||||||
|
|
||||||
@@ -1030,7 +1032,7 @@
|
|||||||
|
|
||||||
"expo-device": ["expo-device@8.0.10", "", { "dependencies": { "ua-parser-js": "^0.7.33" }, "peerDependencies": { "expo": "*" } }, "sha512-jd5BxjaF7382JkDMaC+P04aXXknB2UhWaVx5WiQKA05ugm/8GH5uaz9P9ckWdMKZGQVVEOC8MHaUADoT26KmFA=="],
|
"expo-device": ["expo-device@8.0.10", "", { "dependencies": { "ua-parser-js": "^0.7.33" }, "peerDependencies": { "expo": "*" } }, "sha512-jd5BxjaF7382JkDMaC+P04aXXknB2UhWaVx5WiQKA05ugm/8GH5uaz9P9ckWdMKZGQVVEOC8MHaUADoT26KmFA=="],
|
||||||
|
|
||||||
"expo-doctor": ["expo-doctor@1.17.14", "", { "bin": { "expo-doctor": "build/index.js" } }, "sha512-+UsXFP5ZTVobDuGS5Du8aKU6O6s2sa49QOdGHdzP8UEjQKH8gPb59uw6hxEQmo6YtVboLwQd13QEdcSolBMvLw=="],
|
"expo-doctor": ["expo-doctor@1.18.22", "", { "bin": { "expo-doctor": "build/index.js" } }, "sha512-AEGwceidWxyYpWEfIf3XnUvc+FbI3OjjyBaXctuoZg10x9An+utrdRf6go/3UFRAG5EkpMOWgUT0j1TKcYDsSQ=="],
|
||||||
|
|
||||||
"expo-file-system": ["expo-file-system@19.0.21", "", { "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-s3DlrDdiscBHtab/6W1osrjGL+C2bvoInPJD7sOwmxfJ5Woynv2oc+Fz1/xVXaE/V7HE/+xrHC/H45tu6lZzzg=="],
|
"expo-file-system": ["expo-file-system@19.0.21", "", { "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-s3DlrDdiscBHtab/6W1osrjGL+C2bvoInPJD7sOwmxfJ5Woynv2oc+Fz1/xVXaE/V7HE/+xrHC/H45tu6lZzzg=="],
|
||||||
|
|
||||||
@@ -1094,7 +1096,9 @@
|
|||||||
|
|
||||||
"fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="],
|
"fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="],
|
||||||
|
|
||||||
"fast-xml-parser": ["fast-xml-parser@4.5.3", "", { "dependencies": { "strnum": "^1.1.1" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig=="],
|
"fast-xml-builder": ["fast-xml-builder@1.2.0", "", { "dependencies": { "path-expression-matcher": "^1.5.0", "xml-naming": "^0.1.0" } }, "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q=="],
|
||||||
|
|
||||||
|
"fast-xml-parser": ["fast-xml-parser@5.8.0", "", { "dependencies": { "@nodable/entities": "^2.1.0", "fast-xml-builder": "^1.2.0", "path-expression-matcher": "^1.5.0", "strnum": "^2.3.0", "xml-naming": "^0.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-6bIM7fsJxeo3uXv7OncQYsBAMPJ7V16Slahl/6M98C/i2q+vB1+4a0MtrvYwDFEUrwDSbAmeLDRXsOBwrL7yAg=="],
|
||||||
|
|
||||||
"fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="],
|
"fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="],
|
||||||
|
|
||||||
@@ -1148,7 +1152,7 @@
|
|||||||
|
|
||||||
"get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
|
"get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="],
|
||||||
|
|
||||||
"get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="],
|
"get-east-asian-width": ["get-east-asian-width@1.6.0", "", {}, "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA=="],
|
||||||
|
|
||||||
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
||||||
|
|
||||||
@@ -1164,7 +1168,7 @@
|
|||||||
|
|
||||||
"gifwrap": ["gifwrap@0.10.1", "", { "dependencies": { "image-q": "^4.0.0", "omggif": "^1.0.10" } }, "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw=="],
|
"gifwrap": ["gifwrap@0.10.1", "", { "dependencies": { "image-q": "^4.0.0", "omggif": "^1.0.10" } }, "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw=="],
|
||||||
|
|
||||||
"glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
|
"glob": ["glob@11.1.0", "", { "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw=="],
|
||||||
|
|
||||||
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
||||||
|
|
||||||
@@ -1204,9 +1208,9 @@
|
|||||||
|
|
||||||
"hyphenate-style-name": ["hyphenate-style-name@1.1.0", "", {}, "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw=="],
|
"hyphenate-style-name": ["hyphenate-style-name@1.1.0", "", {}, "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw=="],
|
||||||
|
|
||||||
"i18next": ["i18next@25.6.1", "", { "dependencies": { "@babel/runtime": "^7.27.6" }, "peerDependencies": { "typescript": "^5" }, "optionalPeers": ["typescript"] }, "sha512-yUWvdXtalZztmKrKw3yz/AvSP3yKyqIkVPx/wyvoYy9lkLmwzItLxp0iHZLG5hfVQ539Jor4XLO+U+NHIXg7pw=="],
|
"i18next": ["i18next@26.2.0", "", { "peerDependencies": { "typescript": "^5 || ^6" }, "optionalPeers": ["typescript"] }, "sha512-zwBHldHdTmwN7r6UNc7lC6GWNN+YYg3DrRSeHR5PRRBf5QnJZcYHrQc0uaU26qZeYxR7iFZD+Y315dPnKP47wA=="],
|
||||||
|
|
||||||
"iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
"iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
||||||
|
|
||||||
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
||||||
|
|
||||||
@@ -1276,7 +1280,7 @@
|
|||||||
|
|
||||||
"istanbul-lib-instrument": ["istanbul-lib-instrument@5.2.1", "", { "dependencies": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.2.0", "semver": "^6.3.0" } }, "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg=="],
|
"istanbul-lib-instrument": ["istanbul-lib-instrument@5.2.1", "", { "dependencies": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.2.0", "semver": "^6.3.0" } }, "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg=="],
|
||||||
|
|
||||||
"jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
|
"jackspeak": ["jackspeak@4.2.3", "", { "dependencies": { "@isaacs/cliui": "^9.0.0" } }, "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg=="],
|
||||||
|
|
||||||
"jest-diff": ["jest-diff@29.7.0", "", { "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^29.6.3", "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" } }, "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw=="],
|
"jest-diff": ["jest-diff@29.7.0", "", { "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^29.6.3", "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" } }, "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw=="],
|
||||||
|
|
||||||
@@ -1370,9 +1374,9 @@
|
|||||||
|
|
||||||
"lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="],
|
"lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="],
|
||||||
|
|
||||||
"lint-staged": ["lint-staged@16.2.7", "", { "dependencies": { "commander": "^14.0.2", "listr2": "^9.0.5", "micromatch": "^4.0.8", "nano-spawn": "^2.0.0", "pidtree": "^0.6.0", "string-argv": "^0.3.2", "yaml": "^2.8.1" }, "bin": { "lint-staged": "bin/lint-staged.js" } }, "sha512-lDIj4RnYmK7/kXMya+qJsmkRFkGolciXjrsZ6PC25GdTfWOAWetR0ZbsNXRAj1EHHImRSalc+whZFg56F5DVow=="],
|
"lint-staged": ["lint-staged@17.0.5", "", { "dependencies": { "listr2": "^10.2.1", "picomatch": "^4.0.4", "string-argv": "^0.3.2", "tinyexec": "^1.1.2" }, "optionalDependencies": { "yaml": "^2.8.4" }, "bin": { "lint-staged": "bin/lint-staged.js" } }, "sha512-d12yC+/e8RhBjZtaxZn71FyrgU/P5e+uAPifhCLwdosQZP/zamSdKRWDC30ocVIbzDKiFG1McHc/LUgB92GIPw=="],
|
||||||
|
|
||||||
"listr2": ["listr2@9.0.5", "", { "dependencies": { "cli-truncate": "^5.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.1.0", "rfdc": "^1.4.1", "wrap-ansi": "^9.0.0" } }, "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g=="],
|
"listr2": ["listr2@10.2.1", "", { "dependencies": { "cli-truncate": "^5.2.0", "eventemitter3": "^5.0.4", "log-update": "^6.1.0", "rfdc": "^1.4.1", "wrap-ansi": "^10.0.0" } }, "sha512-7I5knELsJKTUjXG+A6BkKAiGkW1i25fNa/xlUl9hFtk15WbE9jndA89xu5FzQKrY5llajE1hfZZFMILXkDHk/Q=="],
|
||||||
|
|
||||||
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
|
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
|
||||||
|
|
||||||
@@ -1400,7 +1404,7 @@
|
|||||||
|
|
||||||
"mdn-data": ["mdn-data@2.0.14", "", {}, "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow=="],
|
"mdn-data": ["mdn-data@2.0.14", "", {}, "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow=="],
|
||||||
|
|
||||||
"media-typer": ["media-typer@0.3.0", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="],
|
"media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
|
||||||
|
|
||||||
"memoize-one": ["memoize-one@5.2.1", "", {}, "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="],
|
"memoize-one": ["memoize-one@5.2.1", "", {}, "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="],
|
||||||
|
|
||||||
@@ -1448,7 +1452,7 @@
|
|||||||
|
|
||||||
"mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="],
|
"mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="],
|
||||||
|
|
||||||
"minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
"minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="],
|
||||||
|
|
||||||
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
|
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
|
||||||
|
|
||||||
@@ -1462,8 +1466,6 @@
|
|||||||
|
|
||||||
"mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="],
|
"mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="],
|
||||||
|
|
||||||
"nano-spawn": ["nano-spawn@2.0.0", "", {}, "sha512-tacvGzUY5o2D8CBh2rrwxyNojUsZNU2zjNTzKQrkgGJQTbGAfArVWXSKMBokBeeg6C7OLRGUEyoFlYbfeWQIqw=="],
|
|
||||||
|
|
||||||
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
||||||
|
|
||||||
"nativewind": ["nativewind@2.0.11", "", { "dependencies": { "@babel/generator": "^7.18.7", "@babel/helper-module-imports": "7.18.6", "@babel/types": "7.19.0", "css-mediaquery": "^0.1.2", "css-to-react-native": "^3.0.0", "micromatch": "^4.0.5", "postcss": "^8.4.12", "postcss-calc": "^8.2.4", "postcss-color-functional-notation": "^4.2.2", "postcss-css-variables": "^0.18.0", "postcss-nested": "^5.0.6", "react-is": "^18.1.0", "use-sync-external-store": "^1.1.0" }, "peerDependencies": { "tailwindcss": "~3" } }, "sha512-qCEXUwKW21RYJ33KRAJl3zXq2bCq82WoI564fI21D/TiqhfmstZOqPN53RF8qK1NDK6PGl56b2xaTxgObEePEg=="],
|
"nativewind": ["nativewind@2.0.11", "", { "dependencies": { "@babel/generator": "^7.18.7", "@babel/helper-module-imports": "7.18.6", "@babel/types": "7.19.0", "css-mediaquery": "^0.1.2", "css-to-react-native": "^3.0.0", "micromatch": "^4.0.5", "postcss": "^8.4.12", "postcss-calc": "^8.2.4", "postcss-color-functional-notation": "^4.2.2", "postcss-css-variables": "^0.18.0", "postcss-nested": "^5.0.6", "react-is": "^18.1.0", "use-sync-external-store": "^1.1.0" }, "peerDependencies": { "tailwindcss": "~3" } }, "sha512-qCEXUwKW21RYJ33KRAJl3zXq2bCq82WoI564fI21D/TiqhfmstZOqPN53RF8qK1NDK6PGl56b2xaTxgObEePEg=="],
|
||||||
@@ -1546,6 +1548,8 @@
|
|||||||
|
|
||||||
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
|
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
|
||||||
|
|
||||||
|
"path-expression-matcher": ["path-expression-matcher@1.5.0", "", {}, "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ=="],
|
||||||
|
|
||||||
"path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="],
|
"path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="],
|
||||||
|
|
||||||
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
||||||
@@ -1558,9 +1562,7 @@
|
|||||||
|
|
||||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||||
|
|
||||||
"picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
"picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="],
|
||||||
|
|
||||||
"pidtree": ["pidtree@0.6.0", "", { "bin": { "pidtree": "bin/pidtree.js" } }, "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g=="],
|
|
||||||
|
|
||||||
"pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="],
|
"pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="],
|
||||||
|
|
||||||
@@ -1616,7 +1618,7 @@
|
|||||||
|
|
||||||
"qrcode-terminal": ["qrcode-terminal@0.11.0", "", { "bin": { "qrcode-terminal": "./bin/qrcode-terminal.js" } }, "sha512-Uu7ii+FQy4Qf82G4xu7ShHhjhGahEpCWc3x8UavY3CTcWV+ufmmCtwkr7ZKsX42jdL0kr1B5FKUeqJvAn51jzQ=="],
|
"qrcode-terminal": ["qrcode-terminal@0.11.0", "", { "bin": { "qrcode-terminal": "./bin/qrcode-terminal.js" } }, "sha512-Uu7ii+FQy4Qf82G4xu7ShHhjhGahEpCWc3x8UavY3CTcWV+ufmmCtwkr7ZKsX42jdL0kr1B5FKUeqJvAn51jzQ=="],
|
||||||
|
|
||||||
"qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg=="],
|
"qs": ["qs@6.15.2", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw=="],
|
||||||
|
|
||||||
"query-string": ["query-string@7.1.3", "", { "dependencies": { "decode-uri-component": "^0.2.2", "filter-obj": "^1.1.0", "split-on-first": "^1.0.0", "strict-uri-encode": "^2.0.0" } }, "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg=="],
|
"query-string": ["query-string@7.1.3", "", { "dependencies": { "decode-uri-component": "^0.2.2", "filter-obj": "^1.1.0", "split-on-first": "^1.0.0", "strict-uri-encode": "^2.0.0" } }, "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg=="],
|
||||||
|
|
||||||
@@ -1626,7 +1628,7 @@
|
|||||||
|
|
||||||
"range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
|
"range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="],
|
||||||
|
|
||||||
"raw-body": ["raw-body@2.5.2", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" } }, "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA=="],
|
"raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="],
|
||||||
|
|
||||||
"rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="],
|
"rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="],
|
||||||
|
|
||||||
@@ -1640,7 +1642,7 @@
|
|||||||
|
|
||||||
"react-freeze": ["react-freeze@1.0.4", "", { "peerDependencies": { "react": ">=17.0.0" } }, "sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA=="],
|
"react-freeze": ["react-freeze@1.0.4", "", { "peerDependencies": { "react": ">=17.0.0" } }, "sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA=="],
|
||||||
|
|
||||||
"react-i18next": ["react-i18next@16.5.4", "", { "dependencies": { "@babel/runtime": "^7.28.4", "html-parse-stringify": "^3.0.1", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "i18next": ">= 25.6.2", "react": ">= 16.8.0", "typescript": "^5" }, "optionalPeers": ["typescript"] }, "sha512-6yj+dcfMncEC21QPhOTsW8mOSO+pzFmT6uvU7XXdvM/Cp38zJkmTeMeKmTrmCMD5ToT79FmiE/mRWiYWcJYW4g=="],
|
"react-i18next": ["react-i18next@17.0.8", "", { "dependencies": { "@babel/runtime": "^7.29.2", "html-parse-stringify": "^3.0.1", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "i18next": ">= 26.2.0", "react": ">= 16.8.0", "react-dom": "*", "react-native": "*", "typescript": "^5 || ^6" }, "optionalPeers": ["react-dom", "react-native", "typescript"] }, "sha512-0ooKbGLU8JXhe1zwpQUWIeXSgLPOfwJmgheWRIUpcoA0CpyabpGhayjdG+/eA5esC1AQ8h2jWpXjJfzQzeDOCw=="],
|
||||||
|
|
||||||
"react-is": ["react-is@19.2.3", "", {}, "sha512-qJNJfu81ByyabuG7hPFEbXqNcWSU3+eVus+KJs+0ncpGfMyYdvSmxiJxbWR65lYi1I+/0HBcliO029gc4F+PnA=="],
|
"react-is": ["react-is@19.2.3", "", {}, "sha512-qJNJfu81ByyabuG7hPFEbXqNcWSU3+eVus+KJs+0ncpGfMyYdvSmxiJxbWR65lYi1I+/0HBcliO029gc4F+PnA=="],
|
||||||
|
|
||||||
@@ -1696,7 +1698,7 @@
|
|||||||
|
|
||||||
"react-native-text-ticker": ["react-native-text-ticker@1.15.0", "", {}, "sha512-d/uK+PIOhsYMy1r8h825iq/nADiHsabz3WMbRJSnkpQYn+K9aykUAXRRhu8ZbTAzk4CgnUWajJEFxS5ZDygsdg=="],
|
"react-native-text-ticker": ["react-native-text-ticker@1.15.0", "", {}, "sha512-d/uK+PIOhsYMy1r8h825iq/nADiHsabz3WMbRJSnkpQYn+K9aykUAXRRhu8ZbTAzk4CgnUWajJEFxS5ZDygsdg=="],
|
||||||
|
|
||||||
"react-native-track-player": ["react-native-track-player@github:lovegaoshi/react-native-track-player#003afd0", { "peerDependencies": { "react": "*", "react-native": "*", "react-native-windows": "*", "shaka-player": "^4.7.9" }, "optionalPeers": ["react-native-windows", "shaka-player"] }, "lovegaoshi-react-native-track-player-003afd0"],
|
"react-native-track-player": ["react-native-track-player@github:lovegaoshi/react-native-track-player#003afd0", { "peerDependencies": { "react": "*", "react-native": "*", "react-native-windows": "*", "shaka-player": "^4.7.9" }, "optionalPeers": ["react-native-windows", "shaka-player"] }, "lovegaoshi-react-native-track-player-003afd0", "sha512-HR7BaMDMBhQ4xAy5XeQEkT0fBosWw2x9+X2QOD4buocxuX03D770LaRKm5rgQ/qDzchr92KW7+d8fISI14fRLA=="],
|
||||||
|
|
||||||
"react-native-udp": ["react-native-udp@4.1.7", "", { "dependencies": { "buffer": "^5.6.0", "events": "^3.1.0" } }, "sha512-NUE3zewu61NCdSsLlj+l0ad6qojcVEZPT4hVG/x6DU9U4iCzwtfZSASh9vm7teAcVzLkdD+cO3411LHshAi/wA=="],
|
"react-native-udp": ["react-native-udp@4.1.7", "", { "dependencies": { "buffer": "^5.6.0", "events": "^3.1.0" } }, "sha512-NUE3zewu61NCdSsLlj+l0ad6qojcVEZPT4hVG/x6DU9U4iCzwtfZSASh9vm7teAcVzLkdD+cO3411LHshAi/wA=="],
|
||||||
|
|
||||||
@@ -1826,7 +1828,7 @@
|
|||||||
|
|
||||||
"slash": ["slash@2.0.0", "", {}, "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A=="],
|
"slash": ["slash@2.0.0", "", {}, "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A=="],
|
||||||
|
|
||||||
"slice-ansi": ["slice-ansi@7.1.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w=="],
|
"slice-ansi": ["slice-ansi@8.0.0", "", { "dependencies": { "ansi-styles": "^6.2.3", "is-fullwidth-code-point": "^5.1.0" } }, "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg=="],
|
||||||
|
|
||||||
"slugify": ["slugify@1.6.6", "", {}, "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw=="],
|
"slugify": ["slugify@1.6.6", "", {}, "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw=="],
|
||||||
|
|
||||||
@@ -1872,7 +1874,7 @@
|
|||||||
|
|
||||||
"strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="],
|
"strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="],
|
||||||
|
|
||||||
"strnum": ["strnum@1.1.2", "", {}, "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA=="],
|
"strnum": ["strnum@2.3.0", "", {}, "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q=="],
|
||||||
|
|
||||||
"strtok3": ["strtok3@6.3.0", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "peek-readable": "^4.1.0" } }, "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw=="],
|
"strtok3": ["strtok3@6.3.0", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "peek-readable": "^4.1.0" } }, "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw=="],
|
||||||
|
|
||||||
@@ -1910,6 +1912,8 @@
|
|||||||
|
|
||||||
"tinycolor2": ["tinycolor2@1.6.0", "", {}, "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="],
|
"tinycolor2": ["tinycolor2@1.6.0", "", {}, "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="],
|
||||||
|
|
||||||
|
"tinyexec": ["tinyexec@1.1.2", "", {}, "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA=="],
|
||||||
|
|
||||||
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
||||||
|
|
||||||
"tmp": ["tmp@0.2.5", "", {}, "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow=="],
|
"tmp": ["tmp@0.2.5", "", {}, "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow=="],
|
||||||
@@ -1934,7 +1938,7 @@
|
|||||||
|
|
||||||
"type-fest": ["type-fest@0.7.1", "", {}, "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg=="],
|
"type-fest": ["type-fest@0.7.1", "", {}, "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg=="],
|
||||||
|
|
||||||
"type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="],
|
"type-is": ["type-is@2.1.0", "", { "dependencies": { "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA=="],
|
||||||
|
|
||||||
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||||
|
|
||||||
@@ -2014,7 +2018,7 @@
|
|||||||
|
|
||||||
"wonka": ["wonka@6.3.5", "", {}, "sha512-SSil+ecw6B4/Dm7Pf2sAshKQ5hWFvfyGlfPbEd6A14dOH6VDjrmbY86u6nZvy9omGwwIPFR8V41+of1EezgoUw=="],
|
"wonka": ["wonka@6.3.5", "", {}, "sha512-SSil+ecw6B4/Dm7Pf2sAshKQ5hWFvfyGlfPbEd6A14dOH6VDjrmbY86u6nZvy9omGwwIPFR8V41+of1EezgoUw=="],
|
||||||
|
|
||||||
"wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="],
|
"wrap-ansi": ["wrap-ansi@10.0.0", "", { "dependencies": { "ansi-styles": "^6.2.3", "string-width": "^8.2.0", "strip-ansi": "^7.1.2" } }, "sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ=="],
|
||||||
|
|
||||||
"wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
"wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
||||||
|
|
||||||
@@ -2026,6 +2030,8 @@
|
|||||||
|
|
||||||
"xcode": ["xcode@3.0.1", "", { "dependencies": { "simple-plist": "^1.1.0", "uuid": "^7.0.3" } }, "sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA=="],
|
"xcode": ["xcode@3.0.1", "", { "dependencies": { "simple-plist": "^1.1.0", "uuid": "^7.0.3" } }, "sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA=="],
|
||||||
|
|
||||||
|
"xml-naming": ["xml-naming@0.1.0", "", {}, "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw=="],
|
||||||
|
|
||||||
"xml2js": ["xml2js@0.6.0", "", { "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" } }, "sha512-eLTh0kA8uHceqesPqSE+VvO1CDDJWMwlQfB6LuN6T8w6MaDJ8Txm8P7s5cHD0miF0V+GGTZrDQfxPZQVsur33w=="],
|
"xml2js": ["xml2js@0.6.0", "", { "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" } }, "sha512-eLTh0kA8uHceqesPqSE+VvO1CDDJWMwlQfB6LuN6T8w6MaDJ8Txm8P7s5cHD0miF0V+GGTZrDQfxPZQVsur33w=="],
|
||||||
|
|
||||||
"xmlbuilder": ["xmlbuilder@15.1.1", "", {}, "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg=="],
|
"xmlbuilder": ["xmlbuilder@15.1.1", "", {}, "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg=="],
|
||||||
@@ -2034,7 +2040,7 @@
|
|||||||
|
|
||||||
"yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
"yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
||||||
|
|
||||||
"yaml": ["yaml@2.8.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw=="],
|
"yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="],
|
||||||
|
|
||||||
"yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
|
"yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
|
||||||
|
|
||||||
@@ -2118,6 +2124,8 @@
|
|||||||
|
|
||||||
"@expo/cli/glob": ["glob@13.0.0", "", { "dependencies": { "minimatch": "^10.1.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA=="],
|
"@expo/cli/glob": ["glob@13.0.0", "", { "dependencies": { "minimatch": "^10.1.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA=="],
|
||||||
|
|
||||||
|
"@expo/cli/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||||
|
|
||||||
"@expo/cli/ora": ["ora@3.4.0", "", { "dependencies": { "chalk": "^2.4.2", "cli-cursor": "^2.1.0", "cli-spinners": "^2.0.0", "log-symbols": "^2.2.0", "strip-ansi": "^5.2.0", "wcwidth": "^1.0.1" } }, "sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg=="],
|
"@expo/cli/ora": ["ora@3.4.0", "", { "dependencies": { "chalk": "^2.4.2", "cli-cursor": "^2.1.0", "cli-spinners": "^2.0.0", "log-symbols": "^2.2.0", "strip-ansi": "^5.2.0", "wcwidth": "^1.0.1" } }, "sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg=="],
|
||||||
|
|
||||||
"@expo/cli/picomatch": ["picomatch@3.0.1", "", {}, "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag=="],
|
"@expo/cli/picomatch": ["picomatch@3.0.1", "", {}, "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag=="],
|
||||||
@@ -2154,6 +2162,8 @@
|
|||||||
|
|
||||||
"@expo/fingerprint/glob": ["glob@13.0.0", "", { "dependencies": { "minimatch": "^10.1.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA=="],
|
"@expo/fingerprint/glob": ["glob@13.0.0", "", { "dependencies": { "minimatch": "^10.1.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA=="],
|
||||||
|
|
||||||
|
"@expo/fingerprint/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||||
|
|
||||||
"@expo/fingerprint/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
"@expo/fingerprint/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
||||||
|
|
||||||
"@expo/image-utils/getenv": ["getenv@2.0.0", "", {}, "sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ=="],
|
"@expo/image-utils/getenv": ["getenv@2.0.0", "", {}, "sha512-VilgtJj/ALgGY77fiLam5iD336eSWi96Q15JSAG1zi8NRBysm3LXKdGnHb4m5cuyxvOLQQKWpBZAT6ni4FI2iQ=="],
|
||||||
@@ -2172,6 +2182,8 @@
|
|||||||
|
|
||||||
"@expo/metro-config/glob": ["glob@13.0.0", "", { "dependencies": { "minimatch": "^10.1.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA=="],
|
"@expo/metro-config/glob": ["glob@13.0.0", "", { "dependencies": { "minimatch": "^10.1.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA=="],
|
||||||
|
|
||||||
|
"@expo/metro-config/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||||
|
|
||||||
"@expo/metro-config/postcss": ["postcss@8.4.49", "", { "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA=="],
|
"@expo/metro-config/postcss": ["postcss@8.4.49", "", { "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA=="],
|
||||||
|
|
||||||
"@expo/package-manager/ora": ["ora@3.4.0", "", { "dependencies": { "chalk": "^2.4.2", "cli-cursor": "^2.1.0", "cli-spinners": "^2.0.0", "log-symbols": "^2.2.0", "strip-ansi": "^5.2.0", "wcwidth": "^1.0.1" } }, "sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg=="],
|
"@expo/package-manager/ora": ["ora@3.4.0", "", { "dependencies": { "chalk": "^2.4.2", "cli-cursor": "^2.1.0", "cli-spinners": "^2.0.0", "log-symbols": "^2.2.0", "strip-ansi": "^5.2.0", "wcwidth": "^1.0.1" } }, "sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg=="],
|
||||||
@@ -2180,12 +2192,6 @@
|
|||||||
|
|
||||||
"@expo/xcpretty/@babel/code-frame": ["@babel/code-frame@7.10.4", "", { "dependencies": { "@babel/highlight": "^7.10.4" } }, "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg=="],
|
"@expo/xcpretty/@babel/code-frame": ["@babel/code-frame@7.10.4", "", { "dependencies": { "@babel/highlight": "^7.10.4" } }, "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg=="],
|
||||||
|
|
||||||
"@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
|
|
||||||
|
|
||||||
"@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
|
|
||||||
|
|
||||||
"@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
|
|
||||||
|
|
||||||
"@istanbuljs/load-nyc-config/camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="],
|
"@istanbuljs/load-nyc-config/camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="],
|
||||||
|
|
||||||
"@istanbuljs/load-nyc-config/find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="],
|
"@istanbuljs/load-nyc-config/find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="],
|
||||||
@@ -2222,6 +2228,8 @@
|
|||||||
|
|
||||||
"@react-native/codegen/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
|
"@react-native/codegen/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
|
||||||
|
|
||||||
|
"@react-native/codegen/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
|
||||||
|
|
||||||
"@react-native/community-cli-plugin/metro": ["metro@0.83.2", "", { "dependencies": { "@babel/code-frame": "^7.24.7", "@babel/core": "^7.25.2", "@babel/generator": "^7.25.0", "@babel/parser": "^7.25.3", "@babel/template": "^7.25.0", "@babel/traverse": "^7.25.3", "@babel/types": "^7.25.2", "accepts": "^1.3.7", "chalk": "^4.0.0", "ci-info": "^2.0.0", "connect": "^3.6.5", "debug": "^4.4.0", "error-stack-parser": "^2.0.6", "flow-enums-runtime": "^0.0.6", "graceful-fs": "^4.2.4", "hermes-parser": "0.32.0", "image-size": "^1.0.2", "invariant": "^2.2.4", "jest-worker": "^29.7.0", "jsc-safe-url": "^0.2.2", "lodash.throttle": "^4.1.1", "metro-babel-transformer": "0.83.2", "metro-cache": "0.83.2", "metro-cache-key": "0.83.2", "metro-config": "0.83.2", "metro-core": "0.83.2", "metro-file-map": "0.83.2", "metro-resolver": "0.83.2", "metro-runtime": "0.83.2", "metro-source-map": "0.83.2", "metro-symbolicate": "0.83.2", "metro-transform-plugins": "0.83.2", "metro-transform-worker": "0.83.2", "mime-types": "^2.1.27", "nullthrows": "^1.1.1", "serialize-error": "^2.1.0", "source-map": "^0.5.6", "throat": "^5.0.0", "ws": "^7.5.10", "yargs": "^17.6.2" }, "bin": { "metro": "src/cli.js" } }, "sha512-HQgs9H1FyVbRptNSMy/ImchTTE5vS2MSqLoOo7hbDoBq6hPPZokwJvBMwrYSxdjQZmLXz2JFZtdvS+ZfgTc9yw=="],
|
"@react-native/community-cli-plugin/metro": ["metro@0.83.2", "", { "dependencies": { "@babel/code-frame": "^7.24.7", "@babel/core": "^7.25.2", "@babel/generator": "^7.25.0", "@babel/parser": "^7.25.3", "@babel/template": "^7.25.0", "@babel/traverse": "^7.25.3", "@babel/types": "^7.25.2", "accepts": "^1.3.7", "chalk": "^4.0.0", "ci-info": "^2.0.0", "connect": "^3.6.5", "debug": "^4.4.0", "error-stack-parser": "^2.0.6", "flow-enums-runtime": "^0.0.6", "graceful-fs": "^4.2.4", "hermes-parser": "0.32.0", "image-size": "^1.0.2", "invariant": "^2.2.4", "jest-worker": "^29.7.0", "jsc-safe-url": "^0.2.2", "lodash.throttle": "^4.1.1", "metro-babel-transformer": "0.83.2", "metro-cache": "0.83.2", "metro-cache-key": "0.83.2", "metro-config": "0.83.2", "metro-core": "0.83.2", "metro-file-map": "0.83.2", "metro-resolver": "0.83.2", "metro-runtime": "0.83.2", "metro-source-map": "0.83.2", "metro-symbolicate": "0.83.2", "metro-transform-plugins": "0.83.2", "metro-transform-worker": "0.83.2", "mime-types": "^2.1.27", "nullthrows": "^1.1.1", "serialize-error": "^2.1.0", "source-map": "^0.5.6", "throat": "^5.0.0", "ws": "^7.5.10", "yargs": "^17.6.2" }, "bin": { "metro": "src/cli.js" } }, "sha512-HQgs9H1FyVbRptNSMy/ImchTTE5vS2MSqLoOo7hbDoBq6hPPZokwJvBMwrYSxdjQZmLXz2JFZtdvS+ZfgTc9yw=="],
|
||||||
|
|
||||||
"@react-native/community-cli-plugin/metro-config": ["metro-config@0.83.2", "", { "dependencies": { "connect": "^3.6.5", "flow-enums-runtime": "^0.0.6", "jest-validate": "^29.7.0", "metro": "0.83.2", "metro-cache": "0.83.2", "metro-core": "0.83.2", "metro-runtime": "0.83.2", "yaml": "^2.6.1" } }, "sha512-1FjCcdBe3e3D08gSSiU9u3Vtxd7alGH3x/DNFqWDFf5NouX4kLgbVloDDClr1UrLz62c0fHh2Vfr9ecmrOZp+g=="],
|
"@react-native/community-cli-plugin/metro-config": ["metro-config@0.83.2", "", { "dependencies": { "connect": "^3.6.5", "flow-enums-runtime": "^0.0.6", "jest-validate": "^29.7.0", "metro": "0.83.2", "metro-cache": "0.83.2", "metro-core": "0.83.2", "metro-runtime": "0.83.2", "yaml": "^2.6.1" } }, "sha512-1FjCcdBe3e3D08gSSiU9u3Vtxd7alGH3x/DNFqWDFf5NouX4kLgbVloDDClr1UrLz62c0fHh2Vfr9ecmrOZp+g=="],
|
||||||
@@ -2260,12 +2268,12 @@
|
|||||||
|
|
||||||
"accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
|
"accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
|
||||||
|
|
||||||
"ansi-fragments/colorette": ["colorette@1.4.0", "", {}, "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g=="],
|
|
||||||
|
|
||||||
"ansi-fragments/slice-ansi": ["slice-ansi@2.1.0", "", { "dependencies": { "ansi-styles": "^3.2.0", "astral-regex": "^1.0.0", "is-fullwidth-code-point": "^2.0.0" } }, "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ=="],
|
"ansi-fragments/slice-ansi": ["slice-ansi@2.1.0", "", { "dependencies": { "ansi-styles": "^3.2.0", "astral-regex": "^1.0.0", "is-fullwidth-code-point": "^2.0.0" } }, "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ=="],
|
||||||
|
|
||||||
"ansi-fragments/strip-ansi": ["strip-ansi@5.2.0", "", { "dependencies": { "ansi-regex": "^4.1.0" } }, "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA=="],
|
"ansi-fragments/strip-ansi": ["strip-ansi@5.2.0", "", { "dependencies": { "ansi-regex": "^4.1.0" } }, "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA=="],
|
||||||
|
|
||||||
|
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||||
|
|
||||||
"babel-jest/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
|
"babel-jest/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
|
||||||
|
|
||||||
"babel-plugin-jest-hoist/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
|
"babel-plugin-jest-hoist/@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
|
||||||
@@ -2280,13 +2288,11 @@
|
|||||||
|
|
||||||
"better-opn/open": ["open@8.4.2", "", { "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", "is-wsl": "^2.2.0" } }, "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ=="],
|
"better-opn/open": ["open@8.4.2", "", { "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", "is-wsl": "^2.2.0" } }, "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ=="],
|
||||||
|
|
||||||
"body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
|
||||||
|
|
||||||
"chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
"chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||||
|
|
||||||
"chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
"chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||||
|
|
||||||
"cli-truncate/string-width": ["string-width@8.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.0", "strip-ansi": "^7.1.0" } }, "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg=="],
|
"cli-truncate/string-width": ["string-width@8.2.1", "", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA=="],
|
||||||
|
|
||||||
"cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
"cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
||||||
|
|
||||||
@@ -2322,8 +2328,6 @@
|
|||||||
|
|
||||||
"foreground-child/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
"foreground-child/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
||||||
|
|
||||||
"glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
|
||||||
|
|
||||||
"hoist-non-react-statics/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
"hoist-non-react-statics/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
||||||
|
|
||||||
"hosted-git-info/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
"hosted-git-info/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||||
@@ -2340,16 +2344,20 @@
|
|||||||
|
|
||||||
"jest-message-util/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
|
"jest-message-util/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
|
||||||
|
|
||||||
|
"jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||||
|
|
||||||
"jest-worker/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="],
|
"jest-worker/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="],
|
||||||
|
|
||||||
"lighthouse-logger/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
"lighthouse-logger/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||||
|
|
||||||
"lint-staged/commander": ["commander@14.0.2", "", {}, "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ=="],
|
|
||||||
|
|
||||||
"log-update/cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="],
|
"log-update/cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="],
|
||||||
|
|
||||||
|
"log-update/slice-ansi": ["slice-ansi@7.1.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w=="],
|
||||||
|
|
||||||
"log-update/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
|
"log-update/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
|
||||||
|
|
||||||
|
"log-update/wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="],
|
||||||
|
|
||||||
"logkitty/yargs": ["yargs@15.4.1", "", { "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", "find-up": "^4.1.0", "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", "yargs-parser": "^18.1.2" } }, "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A=="],
|
"logkitty/yargs": ["yargs@15.4.1", "", { "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", "find-up": "^4.1.0", "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", "yargs-parser": "^18.1.2" } }, "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A=="],
|
||||||
|
|
||||||
"metro/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
|
"metro/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
|
||||||
@@ -2376,6 +2384,8 @@
|
|||||||
|
|
||||||
"metro-babel-transformer/hermes-parser": ["hermes-parser@0.32.0", "", { "dependencies": { "hermes-estree": "0.32.0" } }, "sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw=="],
|
"metro-babel-transformer/hermes-parser": ["hermes-parser@0.32.0", "", { "dependencies": { "hermes-estree": "0.32.0" } }, "sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw=="],
|
||||||
|
|
||||||
|
"metro-config/yaml": ["yaml@2.8.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw=="],
|
||||||
|
|
||||||
"metro-source-map/@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
|
"metro-source-map/@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
|
||||||
|
|
||||||
"metro-source-map/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
|
"metro-source-map/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
|
||||||
@@ -2396,6 +2406,8 @@
|
|||||||
|
|
||||||
"metro-transform-worker/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
|
"metro-transform-worker/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
|
||||||
|
|
||||||
|
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||||
|
|
||||||
"nativewind/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
|
"nativewind/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
|
||||||
|
|
||||||
"nativewind/@babel/types": ["@babel/types@7.19.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.18.10", "@babel/helper-validator-identifier": "^7.18.6", "to-fast-properties": "^2.0.0" } }, "sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA=="],
|
"nativewind/@babel/types": ["@babel/types@7.19.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.18.10", "@babel/helper-validator-identifier": "^7.18.6", "to-fast-properties": "^2.0.0" } }, "sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA=="],
|
||||||
@@ -2412,22 +2424,32 @@
|
|||||||
|
|
||||||
"patch-package/semver": ["semver@7.6.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A=="],
|
"patch-package/semver": ["semver@7.6.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A=="],
|
||||||
|
|
||||||
|
"patch-package/yaml": ["yaml@2.8.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw=="],
|
||||||
|
|
||||||
"path-scurry/lru-cache": ["lru-cache@11.2.4", "", {}, "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg=="],
|
"path-scurry/lru-cache": ["lru-cache@11.2.4", "", {}, "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg=="],
|
||||||
|
|
||||||
"postcss-css-variables/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="],
|
"postcss-css-variables/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="],
|
||||||
|
|
||||||
"postcss-load-config/lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="],
|
"postcss-load-config/lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="],
|
||||||
|
|
||||||
|
"postcss-load-config/yaml": ["yaml@2.8.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw=="],
|
||||||
|
|
||||||
"pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
|
"pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
|
||||||
|
|
||||||
"prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
"prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
||||||
|
|
||||||
|
"raw-body/http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="],
|
||||||
|
|
||||||
"react-devtools-core/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="],
|
"react-devtools-core/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="],
|
||||||
|
|
||||||
"react-dom/scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="],
|
"react-dom/scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="],
|
||||||
|
|
||||||
|
"react-i18next/@babel/runtime": ["@babel/runtime@7.29.2", "", {}, "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g=="],
|
||||||
|
|
||||||
"react-native/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="],
|
"react-native/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="],
|
||||||
|
|
||||||
|
"react-native/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
|
||||||
|
|
||||||
"react-native/scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="],
|
"react-native/scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="],
|
||||||
|
|
||||||
"react-native/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
"react-native/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
||||||
@@ -2442,8 +2464,12 @@
|
|||||||
|
|
||||||
"readable-web-to-node-stream/readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="],
|
"readable-web-to-node-stream/readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="],
|
||||||
|
|
||||||
|
"readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||||
|
|
||||||
"requireg/resolve": ["resolve@1.7.1", "", { "dependencies": { "path-parse": "^1.0.5" } }, "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw=="],
|
"requireg/resolve": ["resolve@1.7.1", "", { "dependencies": { "path-parse": "^1.0.5" } }, "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw=="],
|
||||||
|
|
||||||
|
"rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
|
||||||
|
|
||||||
"send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
"send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||||
|
|
||||||
"send/mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="],
|
"send/mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="],
|
||||||
@@ -2472,15 +2498,21 @@
|
|||||||
|
|
||||||
"terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
|
"terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
|
||||||
|
|
||||||
|
"test-exclude/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
|
||||||
|
|
||||||
"test-exclude/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
"test-exclude/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
||||||
|
|
||||||
"tinyglobby/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
"tinyglobby/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
||||||
|
|
||||||
|
"type-is/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="],
|
||||||
|
|
||||||
|
"type-is/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
|
||||||
|
|
||||||
"whatwg-url/webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
|
"whatwg-url/webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
|
||||||
|
|
||||||
"wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
|
"wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
|
||||||
|
|
||||||
"wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
|
"wrap-ansi/string-width": ["string-width@8.2.1", "", { "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" } }, "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA=="],
|
||||||
|
|
||||||
"wrap-ansi/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
|
"wrap-ansi/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
|
||||||
|
|
||||||
@@ -2640,10 +2672,6 @@
|
|||||||
|
|
||||||
"@expo/cli/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
"@expo/cli/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||||
|
|
||||||
"@expo/config-plugins/glob/minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="],
|
|
||||||
|
|
||||||
"@expo/config/glob/minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="],
|
|
||||||
|
|
||||||
"@expo/config/sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="],
|
"@expo/config/sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="],
|
||||||
|
|
||||||
"@expo/fingerprint/glob/minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="],
|
"@expo/fingerprint/glob/minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="],
|
||||||
@@ -2676,12 +2704,6 @@
|
|||||||
|
|
||||||
"@expo/package-manager/ora/strip-ansi": ["strip-ansi@5.2.0", "", { "dependencies": { "ansi-regex": "^4.1.0" } }, "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA=="],
|
"@expo/package-manager/ora/strip-ansi": ["strip-ansi@5.2.0", "", { "dependencies": { "ansi-regex": "^4.1.0" } }, "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA=="],
|
||||||
|
|
||||||
"@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
|
|
||||||
|
|
||||||
"@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
|
|
||||||
|
|
||||||
"@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
|
|
||||||
|
|
||||||
"@istanbuljs/load-nyc-config/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="],
|
"@istanbuljs/load-nyc-config/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="],
|
||||||
|
|
||||||
"@istanbuljs/load-nyc-config/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="],
|
"@istanbuljs/load-nyc-config/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="],
|
||||||
@@ -2756,6 +2778,8 @@
|
|||||||
|
|
||||||
"@react-native/codegen/@babel/parser/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
|
"@react-native/codegen/@babel/parser/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
|
||||||
|
|
||||||
|
"@react-native/codegen/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
||||||
|
|
||||||
"@react-native/community-cli-plugin/metro/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
|
"@react-native/community-cli-plugin/metro/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
|
||||||
|
|
||||||
"@react-native/community-cli-plugin/metro/@babel/core": ["@babel/core@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw=="],
|
"@react-native/community-cli-plugin/metro/@babel/core": ["@babel/core@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw=="],
|
||||||
@@ -2800,6 +2824,8 @@
|
|||||||
|
|
||||||
"@react-native/community-cli-plugin/metro-config/metro-runtime": ["metro-runtime@0.83.2", "", { "dependencies": { "@babel/runtime": "^7.25.0", "flow-enums-runtime": "^0.0.6" } }, "sha512-nnsPtgRvFbNKwemqs0FuyFDzXLl+ezuFsUXDbX8o0SXOfsOPijqiQrf3kuafO1Zx1aUWf4NOrKJMAQP5EEHg9A=="],
|
"@react-native/community-cli-plugin/metro-config/metro-runtime": ["metro-runtime@0.83.2", "", { "dependencies": { "@babel/runtime": "^7.25.0", "flow-enums-runtime": "^0.0.6" } }, "sha512-nnsPtgRvFbNKwemqs0FuyFDzXLl+ezuFsUXDbX8o0SXOfsOPijqiQrf3kuafO1Zx1aUWf4NOrKJMAQP5EEHg9A=="],
|
||||||
|
|
||||||
|
"@react-native/community-cli-plugin/metro-config/yaml": ["yaml@2.8.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw=="],
|
||||||
|
|
||||||
"@react-native/community-cli-plugin/metro-core/metro-resolver": ["metro-resolver@0.83.2", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-Yf5mjyuiRE/Y+KvqfsZxrbHDA15NZxyfg8pIk0qg47LfAJhpMVEX+36e6ZRBq7KVBqy6VDX5Sq55iHGM4xSm7Q=="],
|
"@react-native/community-cli-plugin/metro-core/metro-resolver": ["metro-resolver@0.83.2", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-Yf5mjyuiRE/Y+KvqfsZxrbHDA15NZxyfg8pIk0qg47LfAJhpMVEX+36e6ZRBq7KVBqy6VDX5Sq55iHGM4xSm7Q=="],
|
||||||
|
|
||||||
"@react-navigation/bottom-tabs/color/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
"@react-navigation/bottom-tabs/color/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||||
@@ -2832,8 +2858,6 @@
|
|||||||
|
|
||||||
"babel-preset-expo/@babel/helper-module-imports/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
|
"babel-preset-expo/@babel/helper-module-imports/@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
|
||||||
|
|
||||||
"body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
|
||||||
|
|
||||||
"chalk/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
"chalk/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||||
|
|
||||||
"cli-truncate/string-width/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
|
"cli-truncate/string-width/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
|
||||||
@@ -2860,8 +2884,6 @@
|
|||||||
|
|
||||||
"finalhandler/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
"finalhandler/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||||
|
|
||||||
"glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
|
|
||||||
|
|
||||||
"istanbul-lib-instrument/@babel/core/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
|
"istanbul-lib-instrument/@babel/core/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
|
||||||
|
|
||||||
"istanbul-lib-instrument/@babel/core/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
|
"istanbul-lib-instrument/@babel/core/@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
|
||||||
@@ -2884,8 +2906,16 @@
|
|||||||
|
|
||||||
"log-update/cli-cursor/restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="],
|
"log-update/cli-cursor/restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="],
|
||||||
|
|
||||||
|
"log-update/slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
|
||||||
|
|
||||||
|
"log-update/slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="],
|
||||||
|
|
||||||
"log-update/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
|
"log-update/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
|
||||||
|
|
||||||
|
"log-update/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
|
||||||
|
|
||||||
|
"log-update/wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
|
||||||
|
|
||||||
"logkitty/yargs/cliui": ["cliui@6.0.0", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ=="],
|
"logkitty/yargs/cliui": ["cliui@6.0.0", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ=="],
|
||||||
|
|
||||||
"logkitty/yargs/find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="],
|
"logkitty/yargs/find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="],
|
||||||
@@ -2980,8 +3010,14 @@
|
|||||||
|
|
||||||
"patch-package/fs-extra/universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="],
|
"patch-package/fs-extra/universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="],
|
||||||
|
|
||||||
|
"raw-body/http-errors/statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
|
||||||
|
|
||||||
|
"react-native/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
||||||
|
|
||||||
"readable-web-to-node-stream/readable-stream/buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
|
"readable-web-to-node-stream/readable-stream/buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
|
||||||
|
|
||||||
|
"rimraf/glob/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
||||||
|
|
||||||
"send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
"send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||||
|
|
||||||
"serve-static/send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
"serve-static/send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||||
@@ -2990,15 +3026,21 @@
|
|||||||
|
|
||||||
"serve-static/send/mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="],
|
"serve-static/send/mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="],
|
||||||
|
|
||||||
|
"slice-ansi/is-fullwidth-code-point/get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="],
|
||||||
|
|
||||||
|
"sucrase/glob/jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
|
||||||
|
|
||||||
|
"sucrase/glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||||
|
|
||||||
"sucrase/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
|
"sucrase/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
|
||||||
|
|
||||||
"terminal-link/ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="],
|
"terminal-link/ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="],
|
||||||
|
|
||||||
"test-exclude/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
|
"test-exclude/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
|
||||||
|
|
||||||
"wrap-ansi-cjs/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
"type-is/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
|
||||||
|
|
||||||
"wrap-ansi/string-width/emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
|
"wrap-ansi-cjs/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||||
|
|
||||||
"wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
|
"wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
|
||||||
|
|
||||||
@@ -3082,6 +3124,8 @@
|
|||||||
|
|
||||||
"@react-native/codegen/@babel/core/@babel/helper-module-transforms/@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="],
|
"@react-native/codegen/@babel/core/@babel/helper-module-transforms/@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="],
|
||||||
|
|
||||||
|
"@react-native/codegen/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
|
||||||
|
|
||||||
"@react-native/community-cli-plugin/metro/@babel/core/@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="],
|
"@react-native/community-cli-plugin/metro/@babel/core/@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="],
|
||||||
|
|
||||||
"@react-native/community-cli-plugin/metro/@babel/core/@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw=="],
|
"@react-native/community-cli-plugin/metro/@babel/core/@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw=="],
|
||||||
@@ -3130,8 +3174,6 @@
|
|||||||
|
|
||||||
"expo-manifests/@expo/config/@expo/config-plugins/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
|
"expo-manifests/@expo/config/@expo/config-plugins/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
|
||||||
|
|
||||||
"expo-manifests/@expo/config/glob/minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="],
|
|
||||||
|
|
||||||
"expo-manifests/@expo/config/sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="],
|
"expo-manifests/@expo/config/sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="],
|
||||||
|
|
||||||
"istanbul-lib-instrument/@babel/core/@babel/helper-compilation-targets/@babel/compat-data": ["@babel/compat-data@7.28.5", "", {}, "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA=="],
|
"istanbul-lib-instrument/@babel/core/@babel/helper-compilation-targets/@babel/compat-data": ["@babel/compat-data@7.28.5", "", {}, "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA=="],
|
||||||
@@ -3142,6 +3184,12 @@
|
|||||||
|
|
||||||
"log-update/cli-cursor/restore-cursor/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
"log-update/cli-cursor/restore-cursor/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
||||||
|
|
||||||
|
"log-update/slice-ansi/is-fullwidth-code-point/get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="],
|
||||||
|
|
||||||
|
"log-update/wrap-ansi/string-width/emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="],
|
||||||
|
|
||||||
|
"log-update/wrap-ansi/string-width/get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="],
|
||||||
|
|
||||||
"logkitty/yargs/cliui/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
|
"logkitty/yargs/cliui/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
|
||||||
|
|
||||||
"logkitty/yargs/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="],
|
"logkitty/yargs/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="],
|
||||||
@@ -3164,8 +3212,14 @@
|
|||||||
|
|
||||||
"metro/@babel/core/@babel/helper-module-transforms/@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="],
|
"metro/@babel/core/@babel/helper-module-transforms/@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="],
|
||||||
|
|
||||||
|
"react-native/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
|
||||||
|
|
||||||
|
"rimraf/glob/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
|
||||||
|
|
||||||
"serve-static/send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
"serve-static/send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||||
|
|
||||||
|
"sucrase/glob/jackspeak/@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
|
||||||
|
|
||||||
"sucrase/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
"sucrase/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||||
|
|
||||||
"wrap-ansi-cjs/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
"wrap-ansi-cjs/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
||||||
@@ -3200,6 +3254,12 @@
|
|||||||
|
|
||||||
"logkitty/yargs/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="],
|
"logkitty/yargs/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="],
|
||||||
|
|
||||||
|
"sucrase/glob/jackspeak/@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
|
||||||
|
|
||||||
|
"sucrase/glob/jackspeak/@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
|
||||||
|
|
||||||
|
"sucrase/glob/jackspeak/@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
|
||||||
|
|
||||||
"@expo/cli/ora/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
|
"@expo/cli/ora/chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
|
||||||
|
|
||||||
"@expo/cli/ora/cli-cursor/restore-cursor/onetime/mimic-fn": ["mimic-fn@1.2.0", "", {}, "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="],
|
"@expo/cli/ora/cli-cursor/restore-cursor/onetime/mimic-fn": ["mimic-fn@1.2.0", "", {}, "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="],
|
||||||
@@ -3212,6 +3272,12 @@
|
|||||||
|
|
||||||
"logkitty/yargs/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
|
"logkitty/yargs/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
|
||||||
|
|
||||||
|
"sucrase/glob/jackspeak/@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
|
||||||
|
|
||||||
|
"sucrase/glob/jackspeak/@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
|
||||||
|
|
||||||
|
"sucrase/glob/jackspeak/@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
|
||||||
|
|
||||||
"logkitty/yargs/cliui/wrap-ansi/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
"logkitty/yargs/cliui/wrap-ansi/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,203 +0,0 @@
|
|||||||
/**
|
|
||||||
* Example Usage of Global Modal
|
|
||||||
*
|
|
||||||
* This file demonstrates how to use the global modal system from anywhere in your app.
|
|
||||||
* You can delete this file after understanding how it works.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
|
||||||
import { TouchableOpacity, View } from "react-native";
|
|
||||||
import { Text } from "@/components/common/Text";
|
|
||||||
import { useGlobalModal } from "@/providers/GlobalModalProvider";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Example 1: Simple Content Modal
|
|
||||||
*/
|
|
||||||
export const SimpleModalExample = () => {
|
|
||||||
const { showModal } = useGlobalModal();
|
|
||||||
|
|
||||||
const handleOpenModal = () => {
|
|
||||||
showModal(
|
|
||||||
<View className='p-6'>
|
|
||||||
<Text className='text-2xl font-bold mb-4 text-white'>Simple Modal</Text>
|
|
||||||
<Text className='text-white mb-4'>
|
|
||||||
This is a simple modal with just some text content.
|
|
||||||
</Text>
|
|
||||||
<Text className='text-neutral-400'>
|
|
||||||
Swipe down or tap outside to close.
|
|
||||||
</Text>
|
|
||||||
</View>,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TouchableOpacity
|
|
||||||
onPress={handleOpenModal}
|
|
||||||
className='bg-purple-600 px-4 py-2 rounded-lg'
|
|
||||||
>
|
|
||||||
<Text className='text-white font-semibold'>Open Simple Modal</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Example 2: Modal with Custom Snap Points
|
|
||||||
*/
|
|
||||||
export const CustomSnapPointsExample = () => {
|
|
||||||
const { showModal } = useGlobalModal();
|
|
||||||
|
|
||||||
const handleOpenModal = () => {
|
|
||||||
showModal(
|
|
||||||
<View className='p-6' style={{ minHeight: 400 }}>
|
|
||||||
<Text className='text-2xl font-bold mb-4 text-white'>
|
|
||||||
Custom Snap Points
|
|
||||||
</Text>
|
|
||||||
<Text className='text-white mb-4'>
|
|
||||||
This modal has custom snap points (25%, 50%, 90%).
|
|
||||||
</Text>
|
|
||||||
<View className='bg-neutral-800 p-4 rounded-lg'>
|
|
||||||
<Text className='text-white'>
|
|
||||||
Try dragging the modal to different heights!
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</View>,
|
|
||||||
{
|
|
||||||
snapPoints: ["25%", "50%", "90%"],
|
|
||||||
enableDynamicSizing: false,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TouchableOpacity
|
|
||||||
onPress={handleOpenModal}
|
|
||||||
className='bg-blue-600 px-4 py-2 rounded-lg'
|
|
||||||
>
|
|
||||||
<Text className='text-white font-semibold'>Custom Snap Points</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Example 3: Complex Component in Modal
|
|
||||||
*/
|
|
||||||
const SettingsModalContent = () => {
|
|
||||||
const { hideModal } = useGlobalModal();
|
|
||||||
|
|
||||||
const settings = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
title: "Notifications",
|
|
||||||
icon: "notifications-outline" as const,
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
{ id: 2, title: "Dark Mode", icon: "moon-outline" as const, enabled: true },
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
title: "Auto-play",
|
|
||||||
icon: "play-outline" as const,
|
|
||||||
enabled: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View className='p-6'>
|
|
||||||
<Text className='text-2xl font-bold mb-6 text-white'>Settings</Text>
|
|
||||||
|
|
||||||
{settings.map((setting, index) => (
|
|
||||||
<View
|
|
||||||
key={setting.id}
|
|
||||||
className={`flex-row items-center justify-between py-4 ${
|
|
||||||
index !== settings.length - 1 ? "border-b border-neutral-700" : ""
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<View className='flex-row items-center gap-3'>
|
|
||||||
<Ionicons name={setting.icon} size={24} color='white' />
|
|
||||||
<Text className='text-white text-lg'>{setting.title}</Text>
|
|
||||||
</View>
|
|
||||||
<View
|
|
||||||
className={`w-12 h-7 rounded-full ${
|
|
||||||
setting.enabled ? "bg-purple-600" : "bg-neutral-600"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
className={`w-5 h-5 rounded-full bg-white shadow-md transform ${
|
|
||||||
setting.enabled ? "translate-x-6" : "translate-x-1"
|
|
||||||
}`}
|
|
||||||
style={{ marginTop: 4 }}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
))}
|
|
||||||
|
|
||||||
<TouchableOpacity
|
|
||||||
onPress={hideModal}
|
|
||||||
className='bg-purple-600 px-4 py-3 rounded-lg mt-6'
|
|
||||||
>
|
|
||||||
<Text className='text-white font-semibold text-center'>Close</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ComplexModalExample = () => {
|
|
||||||
const { showModal } = useGlobalModal();
|
|
||||||
|
|
||||||
const handleOpenModal = () => {
|
|
||||||
showModal(<SettingsModalContent />);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TouchableOpacity
|
|
||||||
onPress={handleOpenModal}
|
|
||||||
className='bg-green-600 px-4 py-2 rounded-lg'
|
|
||||||
>
|
|
||||||
<Text className='text-white font-semibold'>Complex Component</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Example 4: Modal Triggered from Function (e.g., API response)
|
|
||||||
*/
|
|
||||||
export const useShowSuccessModal = () => {
|
|
||||||
const { showModal } = useGlobalModal();
|
|
||||||
|
|
||||||
return (message: string) => {
|
|
||||||
showModal(
|
|
||||||
<View className='p-6 items-center'>
|
|
||||||
<View className='bg-green-500 rounded-full p-4 mb-4'>
|
|
||||||
<Ionicons name='checkmark' size={48} color='white' />
|
|
||||||
</View>
|
|
||||||
<Text className='text-2xl font-bold mb-2 text-white'>Success!</Text>
|
|
||||||
<Text className='text-white text-center'>{message}</Text>
|
|
||||||
</View>,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main Demo Component
|
|
||||||
*/
|
|
||||||
export const GlobalModalDemo = () => {
|
|
||||||
const showSuccess = useShowSuccessModal();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View className='p-6 gap-4'>
|
|
||||||
<Text className='text-2xl font-bold mb-4 text-white'>
|
|
||||||
Global Modal Examples
|
|
||||||
</Text>
|
|
||||||
|
|
||||||
<SimpleModalExample />
|
|
||||||
<CustomSnapPointsExample />
|
|
||||||
<ComplexModalExample />
|
|
||||||
|
|
||||||
<TouchableOpacity
|
|
||||||
onPress={() => showSuccess("Operation completed successfully!")}
|
|
||||||
className='bg-orange-600 px-4 py-2 rounded-lg'
|
|
||||||
>
|
|
||||||
<Text className='text-white font-semibold'>Show Success Modal</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -89,16 +89,16 @@ export const IntroSheet = forwardRef<IntroSheetRef>((_, ref) => {
|
|||||||
</Text>
|
</Text>
|
||||||
<View className='flex flex-row items-center mt-4'>
|
<View className='flex flex-row items-center mt-4'>
|
||||||
<Image
|
<Image
|
||||||
source={require("@/assets/icons/jellyseerr-logo.svg")}
|
source={require("@/assets/icons/seerr-logo.svg")}
|
||||||
style={{
|
style={{
|
||||||
width: 50,
|
width: 50,
|
||||||
height: 50,
|
height: 50,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<View className='shrink ml-2'>
|
<View className='shrink ml-2'>
|
||||||
<Text className='font-bold mb-1'>Jellyseerr</Text>
|
<Text className='font-bold mb-1'>Seerr</Text>
|
||||||
<Text className='shrink text-xs'>
|
<Text className='shrink text-xs'>
|
||||||
{t("home.intro.jellyseerr_feature_description")}
|
{t("home.intro.seerr_feature_description")}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@@ -158,12 +158,12 @@ export const IntroSheet = forwardRef<IntroSheetRef>((_, ref) => {
|
|||||||
</View>
|
</View>
|
||||||
<View className='shrink ml-2'>
|
<View className='shrink ml-2'>
|
||||||
<Text className='font-bold mb-1'>
|
<Text className='font-bold mb-1'>
|
||||||
{t("home.intro.centralised_settings_plugin_title")}
|
{t("home.intro.centralized_settings_plugin_title")}
|
||||||
</Text>
|
</Text>
|
||||||
<View className='flex-row flex-wrap items-baseline'>
|
<View className='flex-row flex-wrap items-baseline'>
|
||||||
<Text className='shrink text-xs'>
|
<Text className='shrink text-xs'>
|
||||||
{t(
|
{t(
|
||||||
"home.intro.centralised_settings_plugin_description",
|
"home.intro.centralized_settings_plugin_description",
|
||||||
)}{" "}
|
)}{" "}
|
||||||
</Text>
|
</Text>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useQuery } from "@tanstack/react-query";
|
|||||||
import { Image } from "expo-image";
|
import { Image } from "expo-image";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { View, type ViewProps } from "react-native";
|
import { View, type ViewProps } from "react-native";
|
||||||
import { useJellyseerr } from "@/hooks/useJellyseerr";
|
import { useSeerr } from "@/hooks/useSeerr";
|
||||||
import { MediaType } from "@/utils/jellyseerr/server/constants/media";
|
import { MediaType } from "@/utils/jellyseerr/server/constants/media";
|
||||||
import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
|
import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
|
||||||
import type {
|
import type {
|
||||||
@@ -55,23 +55,23 @@ export const Ratings: React.FC<Props> = ({ item, ...props }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const JellyserrRatings: React.FC<{
|
export const SeerrRatings: React.FC<{
|
||||||
result: MovieResult | TvResult | TvDetails | MovieDetails;
|
result: MovieResult | TvResult | TvDetails | MovieDetails;
|
||||||
}> = ({ result }) => {
|
}> = ({ result }) => {
|
||||||
const { jellyseerrApi, getMediaType } = useJellyseerr();
|
const { seerrApi, getMediaType } = useSeerr();
|
||||||
|
|
||||||
const mediaType = useMemo(() => getMediaType(result), [result]);
|
const mediaType = useMemo(() => getMediaType(result), [result]);
|
||||||
|
|
||||||
const { data, isLoading } = useQuery({
|
const { data, isLoading } = useQuery({
|
||||||
queryKey: ["jellyseerr", result.id, mediaType, "ratings"],
|
queryKey: ["seerr", result.id, mediaType, "ratings"],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
return mediaType === MediaType.MOVIE
|
return mediaType === MediaType.MOVIE
|
||||||
? jellyseerrApi?.movieRatings(result.id)
|
? seerrApi?.movieRatings(result.id)
|
||||||
: jellyseerrApi?.tvRatings(result.id);
|
: seerrApi?.tvRatings(result.id);
|
||||||
},
|
},
|
||||||
staleTime: (5).minutesToMilliseconds(),
|
staleTime: (5).minutesToMilliseconds(),
|
||||||
retry: false,
|
retry: false,
|
||||||
enabled: !!jellyseerrApi,
|
enabled: !!seerrApi,
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export const TrackSheet: React.FC<Props> = ({
|
|||||||
|
|
||||||
const streams = useMemo(
|
const streams = useMemo(
|
||||||
() => source?.MediaStreams?.filter((x) => x.Type === streamType),
|
() => source?.MediaStreams?.filter((x) => x.Type === streamType),
|
||||||
[source],
|
[source, streamType],
|
||||||
);
|
);
|
||||||
|
|
||||||
const selectedSteam = useMemo(
|
const selectedSteam = useMemo(
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
import { Image } from "expo-image";
|
|
||||||
import { View } from "react-native";
|
|
||||||
|
|
||||||
export const LargePoster: React.FC<{ url?: string | null }> = ({ url }) => {
|
|
||||||
if (!url)
|
|
||||||
return (
|
|
||||||
<View className='p-4 rounded-xl overflow-hidden '>
|
|
||||||
<View className='w-full aspect-video rounded-xl overflow-hidden border border-neutral-800' />
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View className='p-4 rounded-xl overflow-hidden '>
|
|
||||||
<Image
|
|
||||||
source={{ uri: url }}
|
|
||||||
className='w-full aspect-video rounded-xl overflow-hidden border border-neutral-800'
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -21,7 +21,7 @@ interface Props extends TouchableOpacityProps {
|
|||||||
mediaType: MediaType;
|
mediaType: MediaType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TouchableJellyseerrRouter: React.FC<PropsWithChildren<Props>> = ({
|
export const TouchableSeerrRouter: React.FC<PropsWithChildren<Props>> = ({
|
||||||
result,
|
result,
|
||||||
mediaTitle,
|
mediaTitle,
|
||||||
releaseYear,
|
releaseYear,
|
||||||
@@ -42,18 +42,26 @@ export const TouchableJellyseerrRouter: React.FC<PropsWithChildren<Props>> = ({
|
|||||||
onPress={() => {
|
onPress={() => {
|
||||||
if (!result) return;
|
if (!result) return;
|
||||||
|
|
||||||
router.push({
|
// Build URL with query params - avoids Expo Router's strict type checking.
|
||||||
pathname: `/(auth)/(tabs)/${from}/jellyseerr/page`,
|
// Every value is coerced defensively: releaseYear/mediaType can be
|
||||||
// @ts-expect-error
|
// undefined or NaN at runtime, so `.toString()` would throw.
|
||||||
params: {
|
const params = new URLSearchParams({
|
||||||
...result,
|
...Object.fromEntries(
|
||||||
mediaTitle,
|
Object.entries(result).map(([key, value]) => [
|
||||||
releaseYear,
|
key,
|
||||||
canRequest: canRequest.toString(),
|
String(value ?? ""),
|
||||||
posterSrc,
|
]),
|
||||||
mediaType,
|
),
|
||||||
},
|
mediaTitle: mediaTitle ?? "",
|
||||||
|
releaseYear: String(releaseYear ?? ""),
|
||||||
|
canRequest: String(canRequest ?? false),
|
||||||
|
posterSrc: posterSrc ?? "",
|
||||||
|
mediaType: String(mediaType ?? ""),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.push(
|
||||||
|
`/(auth)/(tabs)/(home,libraries,search,favorites,watchlists)/seerr/page?${params.toString()}`,
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import { View, type ViewProps } from "react-native";
|
|
||||||
|
|
||||||
interface Props extends ViewProps {
|
|
||||||
index: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const VerticalSkeleton: React.FC<Props> = ({ index, ...props }) => {
|
|
||||||
return (
|
|
||||||
<View
|
|
||||||
key={index}
|
|
||||||
style={{
|
|
||||||
width: "32%",
|
|
||||||
}}
|
|
||||||
className='flex flex-col'
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
aspectRatio: "10/15",
|
|
||||||
}}
|
|
||||||
className='w-full bg-neutral-800 mb-2 rounded-lg'
|
|
||||||
/>
|
|
||||||
<View className='h-2 bg-neutral-800 rounded-full mb-1' />
|
|
||||||
<View className='h-2 bg-neutral-800 rounded-full mb-1' />
|
|
||||||
<View className='h-2 bg-neutral-800 rounded-full mb-2 w-1/2' />
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -569,29 +569,31 @@ export const HomeWithCarousel = () => {
|
|||||||
settings.streamyStatsSeriesRecommendations ||
|
settings.streamyStatsSeriesRecommendations ||
|
||||||
settings.streamyStatsPromotedWatchlists;
|
settings.streamyStatsPromotedWatchlists;
|
||||||
const streamystatsSections =
|
const streamystatsSections =
|
||||||
index === streamystatsIndex && hasStreamystatsContent ? (
|
index === streamystatsIndex && hasStreamystatsContent
|
||||||
<>
|
? [
|
||||||
{settings.streamyStatsMovieRecommendations && (
|
settings.streamyStatsMovieRecommendations && (
|
||||||
<StreamystatsRecommendations
|
<StreamystatsRecommendations
|
||||||
title={t(
|
key='movie-recommendations'
|
||||||
"home.settings.plugins.streamystats.recommended_movies",
|
title={t(
|
||||||
)}
|
"home.settings.plugins.streamystats.recommended_movies",
|
||||||
type='Movie'
|
)}
|
||||||
/>
|
type='Movie'
|
||||||
)}
|
/>
|
||||||
{settings.streamyStatsSeriesRecommendations && (
|
),
|
||||||
<StreamystatsRecommendations
|
settings.streamyStatsSeriesRecommendations && (
|
||||||
title={t(
|
<StreamystatsRecommendations
|
||||||
"home.settings.plugins.streamystats.recommended_series",
|
key='series-recommendations'
|
||||||
)}
|
title={t(
|
||||||
type='Series'
|
"home.settings.plugins.streamystats.recommended_series",
|
||||||
/>
|
)}
|
||||||
)}
|
type='Series'
|
||||||
{settings.streamyStatsPromotedWatchlists && (
|
/>
|
||||||
<StreamystatsPromotedWatchlists />
|
),
|
||||||
)}
|
settings.streamyStatsPromotedWatchlists && (
|
||||||
</>
|
<StreamystatsPromotedWatchlists key='promoted-watchlists' />
|
||||||
) : null;
|
),
|
||||||
|
].filter(Boolean)
|
||||||
|
: null;
|
||||||
|
|
||||||
if (section.type === "InfiniteScrollingCollectionList") {
|
if (section.type === "InfiniteScrollingCollectionList") {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -247,15 +247,14 @@ export const StreamystatsPromotedWatchlists: React.FC<
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<View {...props}>
|
||||||
{watchlists?.map((watchlist) => (
|
{watchlists?.map((watchlist) => (
|
||||||
<WatchlistSection
|
<WatchlistSection
|
||||||
key={watchlist.id}
|
key={watchlist.id}
|
||||||
watchlist={watchlist}
|
watchlist={watchlist}
|
||||||
jellyfinServerId={jellyfinServerId!}
|
jellyfinServerId={jellyfinServerId!}
|
||||||
{...props}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export const LibraryItemCard: React.FC<Props> = ({ library, ...props }) => {
|
|||||||
api,
|
api,
|
||||||
item: library,
|
item: library,
|
||||||
}),
|
}),
|
||||||
[library],
|
[api, library],
|
||||||
);
|
);
|
||||||
|
|
||||||
const itemType = useMemo(() => {
|
const itemType = useMemo(() => {
|
||||||
|
|||||||
@@ -23,10 +23,8 @@ export const MusicAlbumCard: React.FC<Props> = ({ album, width = 130 }) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handlePress = useCallback(() => {
|
const handlePress = useCallback(() => {
|
||||||
router.push({
|
if (!album.Id) return;
|
||||||
pathname: "/music/album/[albumId]",
|
router.push(`/music/album/${album.Id}`);
|
||||||
params: { albumId: album.Id! },
|
|
||||||
});
|
|
||||||
}, [router, album.Id]);
|
}, [router, album.Id]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -24,10 +24,8 @@ export const MusicAlbumRowCard: React.FC<Props> = ({ album }) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handlePress = useCallback(() => {
|
const handlePress = useCallback(() => {
|
||||||
router.push({
|
if (!album.Id) return;
|
||||||
pathname: "/music/album/[albumId]",
|
router.push(`/music/album/${album.Id}`);
|
||||||
params: { albumId: album.Id! },
|
|
||||||
});
|
|
||||||
}, [router, album.Id]);
|
}, [router, album.Id]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -25,10 +25,8 @@ export const MusicArtistCard: React.FC<Props> = ({ artist }) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handlePress = useCallback(() => {
|
const handlePress = useCallback(() => {
|
||||||
router.push({
|
if (!artist.Id) return;
|
||||||
pathname: "/music/artist/[artistId]",
|
router.push(`/music/artist/${artist.Id}`);
|
||||||
params: { artistId: artist.Id! },
|
|
||||||
});
|
|
||||||
}, [router, artist.Id]);
|
}, [router, artist.Id]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -61,10 +61,7 @@ export const MusicPlaylistCard: React.FC<Props> = ({ playlist }) => {
|
|||||||
const hasDownloads = downloadStatus.downloaded > 0;
|
const hasDownloads = downloadStatus.downloaded > 0;
|
||||||
|
|
||||||
const handlePress = useCallback(() => {
|
const handlePress = useCallback(() => {
|
||||||
router.push({
|
router.push(`/music/playlist/${playlist.Id}`);
|
||||||
pathname: "/music/playlist/[playlistId]",
|
|
||||||
params: { playlistId: playlist.Id! },
|
|
||||||
});
|
|
||||||
}, [router, playlist.Id]);
|
}, [router, playlist.Id]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -197,10 +197,7 @@ export const TrackOptionsSheet: React.FC<Props> = ({
|
|||||||
const artistId = track?.ArtistItems?.[0]?.Id;
|
const artistId = track?.ArtistItems?.[0]?.Id;
|
||||||
if (artistId) {
|
if (artistId) {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
router.push({
|
router.push(`/music/artist/${artistId}`);
|
||||||
pathname: "/music/artist/[artistId]",
|
|
||||||
params: { artistId },
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}, [track?.ArtistItems, router, setOpen]);
|
}, [track?.ArtistItems, router, setOpen]);
|
||||||
|
|
||||||
@@ -208,10 +205,7 @@ export const TrackOptionsSheet: React.FC<Props> = ({
|
|||||||
const albumId = track?.AlbumId || track?.ParentId;
|
const albumId = track?.AlbumId || track?.ParentId;
|
||||||
if (albumId) {
|
if (albumId) {
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
router.push({
|
router.push(`/music/album/${albumId}`);
|
||||||
pathname: "/music/album/[albumId]",
|
|
||||||
params: { albumId },
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}, [track?.AlbumId, track?.ParentId, router, setOpen]);
|
}, [track?.AlbumId, track?.ParentId, router, setOpen]);
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
// You can explore the built-in icon families and icons on the web at https://icons.expo.fyi/
|
|
||||||
|
|
||||||
import type { IconProps } from "@expo/vector-icons/build/createIconSet";
|
|
||||||
import Ionicons from "@expo/vector-icons/Ionicons";
|
|
||||||
import type { ComponentProps } from "react";
|
|
||||||
|
|
||||||
export function TabBarIcon({
|
|
||||||
style,
|
|
||||||
...rest
|
|
||||||
}: IconProps<ComponentProps<typeof Ionicons>["name"]>) {
|
|
||||||
return <Ionicons size={26} style={[{ marginBottom: -3 }, style]} {...rest} />;
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
|
||||||
import { Image } from "expo-image";
|
|
||||||
import { useAtom } from "jotai";
|
|
||||||
import { useMemo, useState } from "react";
|
|
||||||
import { View } from "react-native";
|
|
||||||
import { WatchedIndicator } from "@/components/WatchedIndicator";
|
|
||||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
|
||||||
|
|
||||||
type MoviePosterProps = {
|
|
||||||
item: BaseItemDto;
|
|
||||||
showProgress?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const EpisodePoster: React.FC<MoviePosterProps> = ({
|
|
||||||
item,
|
|
||||||
showProgress = false,
|
|
||||||
}) => {
|
|
||||||
const [api] = useAtom(apiAtom);
|
|
||||||
|
|
||||||
const url = useMemo(() => {
|
|
||||||
if (item.Type === "Episode") {
|
|
||||||
return `${api?.basePath}/Items/${item.ParentBackdropItemId}/Images/Thumb?fillHeight=389&quality=80&tag=${item.ParentThumbImageTag}`;
|
|
||||||
}
|
|
||||||
}, [item]);
|
|
||||||
|
|
||||||
const [progress, _setProgress] = useState(
|
|
||||||
item.UserData?.PlayedPercentage || 0,
|
|
||||||
);
|
|
||||||
|
|
||||||
const blurhash = useMemo(() => {
|
|
||||||
const key = item.ImageTags?.Primary as string;
|
|
||||||
return item.ImageBlurHashes?.Primary?.[key];
|
|
||||||
}, [item]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View className='relative rounded-lg overflow-hidden border border-neutral-900'>
|
|
||||||
<Image
|
|
||||||
placeholder={{
|
|
||||||
blurhash,
|
|
||||||
}}
|
|
||||||
key={item.Id}
|
|
||||||
id={item.Id}
|
|
||||||
source={
|
|
||||||
url
|
|
||||||
? {
|
|
||||||
uri: url,
|
|
||||||
}
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
cachePolicy={"memory-disk"}
|
|
||||||
contentFit='cover'
|
|
||||||
style={{
|
|
||||||
aspectRatio: "10/15",
|
|
||||||
width: "100%",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<WatchedIndicator item={item} />
|
|
||||||
{showProgress && progress > 0 && (
|
|
||||||
<View className='h-1 bg-red-600 w-full' />
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
import { Image } from "expo-image";
|
|
||||||
import { useAtom } from "jotai";
|
|
||||||
import { useMemo } from "react";
|
|
||||||
import { View } from "react-native";
|
|
||||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
|
||||||
|
|
||||||
type PosterProps = {
|
|
||||||
id?: string;
|
|
||||||
showProgress?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ParentPoster: React.FC<PosterProps> = ({ id }) => {
|
|
||||||
const [api] = useAtom(apiAtom);
|
|
||||||
|
|
||||||
const url = useMemo(
|
|
||||||
() => `${api?.basePath}/Items/${id}/Images/Primary`,
|
|
||||||
[id],
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!url || !id)
|
|
||||||
return (
|
|
||||||
<View
|
|
||||||
className='border border-neutral-900'
|
|
||||||
style={{
|
|
||||||
aspectRatio: "10/15",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View className='rounded-lg overflow-hidden border border-neutral-900'>
|
|
||||||
<Image
|
|
||||||
key={id}
|
|
||||||
id={id}
|
|
||||||
source={{
|
|
||||||
uri: url,
|
|
||||||
}}
|
|
||||||
cachePolicy={"memory-disk"}
|
|
||||||
contentFit='cover'
|
|
||||||
style={{
|
|
||||||
aspectRatio: "10/15",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ParentPoster;
|
|
||||||
@@ -7,15 +7,15 @@ import Animated, {
|
|||||||
useSharedValue,
|
useSharedValue,
|
||||||
withTiming,
|
withTiming,
|
||||||
} from "react-native-reanimated";
|
} from "react-native-reanimated";
|
||||||
import { TouchableJellyseerrRouter } from "@/components/common/JellyseerrItemRouter";
|
import { TouchableSeerrRouter } from "@/components/common/SeerrItemRouter";
|
||||||
import { Text } from "@/components/common/Text";
|
import { Text } from "@/components/common/Text";
|
||||||
import { Tag, Tags } from "@/components/GenreTags";
|
import { Tag, Tags } from "@/components/GenreTags";
|
||||||
import { textShadowStyle } from "@/components/jellyseerr/discover/GenericSlideCard";
|
import { textShadowStyle } from "@/components/seerr/discover/GenericSlideCard";
|
||||||
import JellyseerrMediaIcon from "@/components/jellyseerr/JellyseerrMediaIcon";
|
import SeerrMediaIcon from "@/components/seerr/SeerrMediaIcon";
|
||||||
import JellyseerrStatusIcon from "@/components/jellyseerr/JellyseerrStatusIcon";
|
import SeerrStatusIcon from "@/components/seerr/SeerrStatusIcon";
|
||||||
import { Colors } from "@/constants/Colors";
|
import { Colors } from "@/constants/Colors";
|
||||||
import { useJellyseerr } from "@/hooks/useJellyseerr";
|
import { useSeerr } from "@/hooks/useSeerr";
|
||||||
import { useJellyseerrCanRequest } from "@/utils/_jellyseerr/useJellyseerrCanRequest";
|
import { useSeerrCanRequest } from "@/utils/_seerr/useSeerrCanRequest";
|
||||||
import { MediaStatus } from "@/utils/jellyseerr/server/constants/media";
|
import { MediaStatus } from "@/utils/jellyseerr/server/constants/media";
|
||||||
import type MediaRequest from "@/utils/jellyseerr/server/entity/MediaRequest";
|
import type MediaRequest from "@/utils/jellyseerr/server/entity/MediaRequest";
|
||||||
import type { DownloadingItem } from "@/utils/jellyseerr/server/lib/downloadtracker";
|
import type { DownloadingItem } from "@/utils/jellyseerr/server/lib/downloadtracker";
|
||||||
@@ -34,13 +34,13 @@ interface Props extends ViewProps {
|
|||||||
mediaRequest?: MediaRequest;
|
mediaRequest?: MediaRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
const JellyseerrPoster: React.FC<Props> = ({
|
const SeerrPoster: React.FC<Props> = ({
|
||||||
item,
|
item,
|
||||||
horizontal,
|
horizontal,
|
||||||
showDownloadInfo,
|
showDownloadInfo,
|
||||||
mediaRequest,
|
mediaRequest,
|
||||||
}) => {
|
}) => {
|
||||||
const { jellyseerrApi, getTitle, getYear, getMediaType } = useJellyseerr();
|
const { seerrApi, getTitle, getYear, getMediaType } = useSeerr();
|
||||||
const loadingOpacity = useSharedValue(1);
|
const loadingOpacity = useSharedValue(1);
|
||||||
const imageOpacity = useSharedValue(0);
|
const imageOpacity = useSharedValue(0);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -56,16 +56,13 @@ const JellyseerrPoster: React.FC<Props> = ({
|
|||||||
|
|
||||||
const backdropSrc = useMemo(
|
const backdropSrc = useMemo(
|
||||||
() =>
|
() =>
|
||||||
jellyseerrApi?.imageProxy(
|
seerrApi?.imageProxy(item?.backdropPath, "w1920_and_h800_multi_faces"),
|
||||||
item?.backdropPath,
|
[item, seerrApi, horizontal],
|
||||||
"w1920_and_h800_multi_faces",
|
|
||||||
),
|
|
||||||
[item, jellyseerrApi, horizontal],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const posterSrc = useMemo(
|
const posterSrc = useMemo(
|
||||||
() => jellyseerrApi?.imageProxy(item?.posterPath, "w300_and_h450_face"),
|
() => seerrApi?.imageProxy(item?.posterPath, "w300_and_h450_face"),
|
||||||
[item, jellyseerrApi, horizontal],
|
[item, seerrApi, horizontal],
|
||||||
);
|
);
|
||||||
|
|
||||||
const title = useMemo(() => getTitle(item), [item]);
|
const title = useMemo(() => getTitle(item), [item]);
|
||||||
@@ -75,7 +72,7 @@ const JellyseerrPoster: React.FC<Props> = ({
|
|||||||
const size = useMemo(() => (horizontal ? "h-28" : "w-28"), [horizontal]);
|
const size = useMemo(() => (horizontal ? "h-28" : "w-28"), [horizontal]);
|
||||||
const ratio = useMemo(() => (horizontal ? "15/10" : "10/15"), [horizontal]);
|
const ratio = useMemo(() => (horizontal ? "15/10" : "10/15"), [horizontal]);
|
||||||
|
|
||||||
const [canRequest] = useJellyseerrCanRequest(item);
|
const [canRequest] = useSeerrCanRequest(item);
|
||||||
|
|
||||||
const is4k = useMemo(() => mediaRequest?.is4k === true, [mediaRequest]);
|
const is4k = useMemo(() => mediaRequest?.is4k === true, [mediaRequest]);
|
||||||
|
|
||||||
@@ -109,7 +106,7 @@ const JellyseerrPoster: React.FC<Props> = ({
|
|||||||
second,
|
second,
|
||||||
third,
|
third,
|
||||||
fourth,
|
fourth,
|
||||||
t("home.settings.plugins.jellyseerr.plus_n_more", { n: rest.length }),
|
t("home.settings.plugins.seerr.plus_n_more", { n: rest.length }),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
return seasons;
|
return seasons;
|
||||||
@@ -121,7 +118,7 @@ const JellyseerrPoster: React.FC<Props> = ({
|
|||||||
}, [mediaRequest, is4k]);
|
}, [mediaRequest, is4k]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TouchableJellyseerrRouter
|
<TouchableSeerrRouter
|
||||||
result={item}
|
result={item}
|
||||||
mediaTitle={title}
|
mediaTitle={title}
|
||||||
releaseYear={releaseYear}
|
releaseYear={releaseYear}
|
||||||
@@ -173,7 +170,7 @@ const JellyseerrPoster: React.FC<Props> = ({
|
|||||||
className='absolute right-1 top-1 text-right bg-black border border-neutral-800/50'
|
className='absolute right-1 top-1 text-right bg-black border border-neutral-800/50'
|
||||||
text={mediaRequest?.requestedBy.displayName}
|
text={mediaRequest?.requestedBy.displayName}
|
||||||
/>
|
/>
|
||||||
{requestedSeasons.length > 0 && (
|
{(requestedSeasons?.length ?? 0) > 0 && (
|
||||||
<Tags
|
<Tags
|
||||||
className='absolute bottom-1 left-0.5 w-32'
|
className='absolute bottom-1 left-0.5 w-32'
|
||||||
tagProps={{
|
tagProps={{
|
||||||
@@ -184,12 +181,12 @@ const JellyseerrPoster: React.FC<Props> = ({
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<JellyseerrStatusIcon
|
<SeerrStatusIcon
|
||||||
className='absolute bottom-1 right-1'
|
className='absolute bottom-1 right-1'
|
||||||
showRequestIcon={canRequest}
|
showRequestIcon={canRequest}
|
||||||
mediaStatus={mediaRequest?.media?.status || item?.mediaInfo?.status}
|
mediaStatus={mediaRequest?.media?.status || item?.mediaInfo?.status}
|
||||||
/>
|
/>
|
||||||
<JellyseerrMediaIcon
|
<SeerrMediaIcon
|
||||||
className='absolute top-1 left-1'
|
className='absolute top-1 left-1'
|
||||||
mediaType={mediaType}
|
mediaType={mediaType}
|
||||||
/>
|
/>
|
||||||
@@ -201,8 +198,8 @@ const JellyseerrPoster: React.FC<Props> = ({
|
|||||||
{releaseYear || ""}
|
{releaseYear || ""}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</TouchableJellyseerrRouter>
|
</TouchableSeerrRouter>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default JellyseerrPoster;
|
export default SeerrPoster;
|
||||||
@@ -1,19 +1,19 @@
|
|||||||
import { Button, ContextMenu, Host, Picker } from "@expo/ui/swift-ui";
|
import { Button, ContextMenu, Host, Picker } from "@expo/ui/swift-ui";
|
||||||
import { Platform, View } from "react-native";
|
import { Platform, View } from "react-native";
|
||||||
import { FilterButton } from "@/components/filters/FilterButton";
|
import { FilterButton } from "@/components/filters/FilterButton";
|
||||||
import { JellyseerrSearchSort } from "@/components/jellyseerr/JellyseerrIndexPage";
|
import { SeerrSearchSort } from "@/components/seerr/SeerrIndexPage";
|
||||||
|
|
||||||
interface DiscoverFiltersProps {
|
interface DiscoverFiltersProps {
|
||||||
searchFilterId: string;
|
searchFilterId: string;
|
||||||
orderFilterId: string;
|
orderFilterId: string;
|
||||||
jellyseerrOrderBy: JellyseerrSearchSort;
|
seerrOrderBy: SeerrSearchSort;
|
||||||
setJellyseerrOrderBy: (value: JellyseerrSearchSort) => void;
|
setSeerrOrderBy: (value: SeerrSearchSort) => void;
|
||||||
jellyseerrSortOrder: "asc" | "desc";
|
seerrSortOrder: "asc" | "desc";
|
||||||
setJellyseerrSortOrder: (value: "asc" | "desc") => void;
|
setSeerrSortOrder: (value: "asc" | "desc") => void;
|
||||||
t: (key: string) => string;
|
t: (key: string) => string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sortOptions = Object.keys(JellyseerrSearchSort).filter((v) =>
|
const sortOptions = Object.keys(SeerrSearchSort).filter((v) =>
|
||||||
Number.isNaN(Number(v)),
|
Number.isNaN(Number(v)),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -22,10 +22,10 @@ const orderOptions = ["asc", "desc"] as const;
|
|||||||
export const DiscoverFilters: React.FC<DiscoverFiltersProps> = ({
|
export const DiscoverFilters: React.FC<DiscoverFiltersProps> = ({
|
||||||
searchFilterId,
|
searchFilterId,
|
||||||
orderFilterId,
|
orderFilterId,
|
||||||
jellyseerrOrderBy,
|
seerrOrderBy,
|
||||||
setJellyseerrOrderBy,
|
setSeerrOrderBy,
|
||||||
jellyseerrSortOrder,
|
seerrSortOrder,
|
||||||
setJellyseerrSortOrder,
|
setSeerrSortOrder,
|
||||||
t,
|
t,
|
||||||
}) => {
|
}) => {
|
||||||
if (Platform.OS === "ios") {
|
if (Platform.OS === "ios") {
|
||||||
@@ -52,16 +52,16 @@ export const DiscoverFilters: React.FC<DiscoverFiltersProps> = ({
|
|||||||
<Picker
|
<Picker
|
||||||
label={t("library.filters.sort_by")}
|
label={t("library.filters.sort_by")}
|
||||||
options={sortOptions.map((item) =>
|
options={sortOptions.map((item) =>
|
||||||
t(`home.settings.plugins.jellyseerr.order_by.${item}`),
|
t(`home.settings.plugins.seerr.order_by.${item}`),
|
||||||
)}
|
)}
|
||||||
variant='menu'
|
variant='menu'
|
||||||
selectedIndex={sortOptions.indexOf(
|
selectedIndex={sortOptions.indexOf(
|
||||||
jellyseerrOrderBy as unknown as string,
|
seerrOrderBy as unknown as string,
|
||||||
)}
|
)}
|
||||||
onOptionSelected={(event: any) => {
|
onOptionSelected={(event: any) => {
|
||||||
const index = event.nativeEvent.index;
|
const index = event.nativeEvent.index;
|
||||||
setJellyseerrOrderBy(
|
setSeerrOrderBy(
|
||||||
sortOptions[index] as unknown as JellyseerrSearchSort,
|
sortOptions[index] as unknown as SeerrSearchSort,
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -69,10 +69,10 @@ export const DiscoverFilters: React.FC<DiscoverFiltersProps> = ({
|
|||||||
label={t("library.filters.sort_order")}
|
label={t("library.filters.sort_order")}
|
||||||
options={orderOptions.map((item) => t(`library.filters.${item}`))}
|
options={orderOptions.map((item) => t(`library.filters.${item}`))}
|
||||||
variant='menu'
|
variant='menu'
|
||||||
selectedIndex={orderOptions.indexOf(jellyseerrSortOrder)}
|
selectedIndex={orderOptions.indexOf(seerrSortOrder)}
|
||||||
onOptionSelected={(event: any) => {
|
onOptionSelected={(event: any) => {
|
||||||
const index = event.nativeEvent.index;
|
const index = event.nativeEvent.index;
|
||||||
setJellyseerrSortOrder(orderOptions[index]);
|
setSeerrSortOrder(orderOptions[index]);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</ContextMenu.Items>
|
</ContextMenu.Items>
|
||||||
@@ -86,17 +86,15 @@ export const DiscoverFilters: React.FC<DiscoverFiltersProps> = ({
|
|||||||
<View className='flex flex-row justify-end items-center space-x-1'>
|
<View className='flex flex-row justify-end items-center space-x-1'>
|
||||||
<FilterButton
|
<FilterButton
|
||||||
id={searchFilterId}
|
id={searchFilterId}
|
||||||
queryKey='jellyseerr_search'
|
queryKey='seerr_search'
|
||||||
queryFn={async () =>
|
queryFn={async () =>
|
||||||
Object.keys(JellyseerrSearchSort).filter((v) =>
|
Object.keys(SeerrSearchSort).filter((v) => Number.isNaN(Number(v)))
|
||||||
Number.isNaN(Number(v)),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
set={(value) => setJellyseerrOrderBy(value[0])}
|
set={(value) => setSeerrOrderBy(value[0])}
|
||||||
values={[jellyseerrOrderBy]}
|
values={[seerrOrderBy]}
|
||||||
title={t("library.filters.sort_by")}
|
title={t("library.filters.sort_by")}
|
||||||
renderItemLabel={(item) =>
|
renderItemLabel={(item) =>
|
||||||
t(`home.settings.plugins.jellyseerr.order_by.${item}`)
|
t(`home.settings.plugins.seerr.order_by.${item}`)
|
||||||
}
|
}
|
||||||
disableSearch={true}
|
disableSearch={true}
|
||||||
/>
|
/>
|
||||||
@@ -104,8 +102,8 @@ export const DiscoverFilters: React.FC<DiscoverFiltersProps> = ({
|
|||||||
id={orderFilterId}
|
id={orderFilterId}
|
||||||
queryKey='jellysearr_search'
|
queryKey='jellysearr_search'
|
||||||
queryFn={async () => ["asc", "desc"]}
|
queryFn={async () => ["asc", "desc"]}
|
||||||
set={(value) => setJellyseerrSortOrder(value[0])}
|
set={(value) => setSeerrSortOrder(value[0])}
|
||||||
values={[jellyseerrSortOrder]}
|
values={[seerrSortOrder]}
|
||||||
title={t("library.filters.sort_order")}
|
title={t("library.filters.sort_order")}
|
||||||
renderItemLabel={(item) => t(`library.filters.${item}`)}
|
renderItemLabel={(item) => t(`library.filters.${item}`)}
|
||||||
disableSearch={true}
|
disableSearch={true}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type React from "react";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { View, type ViewProps } from "react-native";
|
import { View, type ViewProps } from "react-native";
|
||||||
import { Text } from "@/components/common/Text";
|
import { Text } from "@/components/common/Text";
|
||||||
import PersonPoster from "@/components/jellyseerr/PersonPoster";
|
import PersonPoster from "@/components/seerr/PersonPoster";
|
||||||
import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
|
import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
|
||||||
import type { TvDetails } from "@/utils/jellyseerr/server/models/Tv";
|
import type { TvDetails } from "@/utils/jellyseerr/server/models/Tv";
|
||||||
|
|
||||||
@@ -15,19 +15,17 @@ const CastSlide: React.FC<
|
|||||||
details?.credits?.cast &&
|
details?.credits?.cast &&
|
||||||
details?.credits?.cast?.length > 0 && (
|
details?.credits?.cast?.length > 0 && (
|
||||||
<View {...props}>
|
<View {...props}>
|
||||||
<Text className='text-lg font-bold mb-2 px-4'>
|
<Text className='text-lg font-bold mb-2 px-4'>{t("seerr.cast")}</Text>
|
||||||
{t("jellyseerr.cast")}
|
|
||||||
</Text>
|
|
||||||
<FlashList
|
<FlashList
|
||||||
horizontal
|
horizontal
|
||||||
showsHorizontalScrollIndicator={false}
|
showsHorizontalScrollIndicator={false}
|
||||||
data={details?.credits.cast}
|
data={details?.credits.cast}
|
||||||
ItemSeparatorComponent={() => <View className='w-2' />}
|
ItemSeparatorComponent={() => <View className='w-2' />}
|
||||||
keyExtractor={(item) => item?.id?.toString()}
|
keyExtractor={(item) => item?.id?.toString() ?? ""}
|
||||||
contentContainerStyle={{ paddingHorizontal: 16 }}
|
contentContainerStyle={{ paddingHorizontal: 16 }}
|
||||||
renderItem={({ item }) => (
|
renderItem={({ item }) => (
|
||||||
<PersonPoster
|
<PersonPoster
|
||||||
id={item.id.toString()}
|
id={item?.id?.toString() ?? ""}
|
||||||
posterPath={item.profilePath}
|
posterPath={item.profilePath}
|
||||||
name={item.name}
|
name={item.name}
|
||||||
subName={item.character}
|
subName={item.character}
|
||||||
@@ -5,7 +5,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { View, type ViewProps } from "react-native";
|
import { View, type ViewProps } from "react-native";
|
||||||
import CountryFlag from "react-native-country-flag";
|
import CountryFlag from "react-native-country-flag";
|
||||||
import { Text } from "@/components/common/Text";
|
import { Text } from "@/components/common/Text";
|
||||||
import { useJellyseerr } from "@/hooks/useJellyseerr";
|
import { useSeerr } from "@/hooks/useSeerr";
|
||||||
import { ANIME_KEYWORD_ID } from "@/utils/jellyseerr/server/api/themoviedb/constants";
|
import { ANIME_KEYWORD_ID } from "@/utils/jellyseerr/server/api/themoviedb/constants";
|
||||||
import type { TmdbRelease } from "@/utils/jellyseerr/server/api/themoviedb/interfaces";
|
import type { TmdbRelease } from "@/utils/jellyseerr/server/api/themoviedb/interfaces";
|
||||||
import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
|
import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
|
||||||
@@ -50,8 +50,7 @@ const Fact: React.FC<{ title: string; fact?: string | null } & ViewProps> = ({
|
|||||||
const DetailFacts: React.FC<
|
const DetailFacts: React.FC<
|
||||||
{ details?: MovieDetails | TvDetails } & ViewProps
|
{ details?: MovieDetails | TvDetails } & ViewProps
|
||||||
> = ({ details, className, ...props }) => {
|
> = ({ details, className, ...props }) => {
|
||||||
const { jellyseerrRegion: region, jellyseerrLocale: locale } =
|
const { seerrRegion: region, seerrLocale: locale } = useSeerr();
|
||||||
useJellyseerr();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const releases = useMemo(
|
const releases = useMemo(
|
||||||
@@ -59,7 +58,7 @@ const DetailFacts: React.FC<
|
|||||||
(details as MovieDetails)?.releases?.results.find(
|
(details as MovieDetails)?.releases?.results.find(
|
||||||
(r: TmdbRelease) => r.iso_3166_1 === region,
|
(r: TmdbRelease) => r.iso_3166_1 === region,
|
||||||
)?.release_dates as TmdbRelease["release_dates"],
|
)?.release_dates as TmdbRelease["release_dates"],
|
||||||
[details],
|
[details, region],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Release date types:
|
// Release date types:
|
||||||
@@ -81,40 +80,34 @@ const DetailFacts: React.FC<
|
|||||||
const firstAirDate = useMemo(() => {
|
const firstAirDate = useMemo(() => {
|
||||||
const firstAirDate = (details as TvDetails)?.firstAirDate;
|
const firstAirDate = (details as TvDetails)?.firstAirDate;
|
||||||
if (firstAirDate) {
|
if (firstAirDate) {
|
||||||
return new Date(firstAirDate).toLocaleDateString(
|
return new Date(firstAirDate).toLocaleDateString(locale, dateOpts);
|
||||||
`${locale}-${region}`,
|
|
||||||
dateOpts,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}, [details]);
|
}, [details, locale]);
|
||||||
|
|
||||||
const nextAirDate = useMemo(() => {
|
const nextAirDate = useMemo(() => {
|
||||||
const firstAirDate = (details as TvDetails)?.firstAirDate;
|
const firstAirDate = (details as TvDetails)?.firstAirDate;
|
||||||
const nextAirDate = (details as TvDetails)?.nextEpisodeToAir?.airDate;
|
const nextAirDate = (details as TvDetails)?.nextEpisodeToAir?.airDate;
|
||||||
if (nextAirDate && firstAirDate !== nextAirDate) {
|
if (nextAirDate && firstAirDate !== nextAirDate) {
|
||||||
return new Date(nextAirDate).toLocaleDateString(
|
return new Date(nextAirDate).toLocaleDateString(locale, dateOpts);
|
||||||
`${locale}-${region}`,
|
|
||||||
dateOpts,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}, [details]);
|
}, [details, locale]);
|
||||||
|
|
||||||
const revenue = useMemo(
|
const revenue = useMemo(
|
||||||
() =>
|
() =>
|
||||||
(details as MovieDetails)?.revenue?.toLocaleString?.(
|
(details as MovieDetails)?.revenue?.toLocaleString?.(locale, {
|
||||||
`${locale}-${region}`,
|
style: "currency",
|
||||||
{ style: "currency", currency: "USD" },
|
currency: "USD",
|
||||||
),
|
}),
|
||||||
[details],
|
[details, locale],
|
||||||
);
|
);
|
||||||
|
|
||||||
const budget = useMemo(
|
const budget = useMemo(
|
||||||
() =>
|
() =>
|
||||||
(details as MovieDetails)?.budget?.toLocaleString?.(
|
(details as MovieDetails)?.budget?.toLocaleString?.(locale, {
|
||||||
`${locale}-${region}`,
|
style: "currency",
|
||||||
{ style: "currency", currency: "USD" },
|
currency: "USD",
|
||||||
),
|
}),
|
||||||
[details],
|
[details, locale],
|
||||||
);
|
);
|
||||||
|
|
||||||
const streamingProviders = useMemo(
|
const streamingProviders = useMemo(
|
||||||
@@ -122,7 +115,7 @@ const DetailFacts: React.FC<
|
|||||||
details?.watchProviders?.find(
|
details?.watchProviders?.find(
|
||||||
(provider) => provider.iso_3166_1 === region,
|
(provider) => provider.iso_3166_1 === region,
|
||||||
)?.flatrate,
|
)?.flatrate,
|
||||||
[details],
|
[details, region],
|
||||||
);
|
);
|
||||||
|
|
||||||
const networks = useMemo(() => (details as TvDetails)?.networks, [details]);
|
const networks = useMemo(() => (details as TvDetails)?.networks, [details]);
|
||||||
@@ -138,21 +131,21 @@ const DetailFacts: React.FC<
|
|||||||
return (
|
return (
|
||||||
details && (
|
details && (
|
||||||
<View className='p-4'>
|
<View className='p-4'>
|
||||||
<Text className='text-lg font-bold'>{t("jellyseerr.details")}</Text>
|
<Text className='text-lg font-bold'>{t("seerr.details")}</Text>
|
||||||
<View
|
<View
|
||||||
className={`${className} flex flex-col justify-center divide-y-2 divide-neutral-800`}
|
className={`${className} flex flex-col justify-center divide-y-2 divide-neutral-800`}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<Fact title={t("jellyseerr.status")} fact={details?.status} />
|
<Fact title={t("seerr.status")} fact={details?.status} />
|
||||||
<Fact
|
<Fact
|
||||||
title={t("jellyseerr.original_title")}
|
title={t("seerr.original_title")}
|
||||||
fact={(details as TvDetails)?.originalName}
|
fact={(details as TvDetails)?.originalName}
|
||||||
/>
|
/>
|
||||||
{details.keywords.some(
|
{details.keywords.some(
|
||||||
(keyword) => keyword.id === ANIME_KEYWORD_ID,
|
(keyword) => keyword.id === ANIME_KEYWORD_ID,
|
||||||
) && <Fact title={t("jellyseerr.series_type")} fact='Anime' />}
|
) && <Fact title={t("seerr.series_type")} fact='Anime' />}
|
||||||
<Facts
|
<Facts
|
||||||
title={t("jellyseerr.release_dates")}
|
title={t("seerr.release_dates")}
|
||||||
facts={filteredReleases?.map?.((r: Release, idx) => (
|
facts={filteredReleases?.map?.((r: Release, idx) => (
|
||||||
<View key={idx} className='flex flex-row space-x-2 items-center'>
|
<View key={idx} className='flex flex-row space-x-2 items-center'>
|
||||||
{r.type === 3 ? (
|
{r.type === 3 ? (
|
||||||
@@ -171,23 +164,20 @@ const DetailFacts: React.FC<
|
|||||||
)}
|
)}
|
||||||
<Text>
|
<Text>
|
||||||
{new Date(r.release_date).toLocaleDateString(
|
{new Date(r.release_date).toLocaleDateString(
|
||||||
`${locale}-${region}`,
|
locale,
|
||||||
dateOpts,
|
dateOpts,
|
||||||
)}
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
/>
|
/>
|
||||||
<Fact title={t("jellyseerr.first_air_date")} fact={firstAirDate} />
|
<Fact title={t("seerr.first_air_date")} fact={firstAirDate} />
|
||||||
<Fact title={t("jellyseerr.next_air_date")} fact={nextAirDate} />
|
<Fact title={t("seerr.next_air_date")} fact={nextAirDate} />
|
||||||
<Fact title={t("jellyseerr.revenue")} fact={revenue} />
|
<Fact title={t("seerr.revenue")} fact={revenue} />
|
||||||
<Fact title={t("jellyseerr.budget")} fact={budget} />
|
<Fact title={t("seerr.budget")} fact={budget} />
|
||||||
<Fact
|
<Fact title={t("seerr.original_language")} fact={spokenLanguage} />
|
||||||
title={t("jellyseerr.original_language")}
|
|
||||||
fact={spokenLanguage}
|
|
||||||
/>
|
|
||||||
<Facts
|
<Facts
|
||||||
title={t("jellyseerr.production_country")}
|
title={t("seerr.production_country")}
|
||||||
facts={details?.productionCountries?.map((n, idx) => (
|
facts={details?.productionCountries?.map((n, idx) => (
|
||||||
<View key={idx} className='flex flex-row items-center space-x-2'>
|
<View key={idx} className='flex flex-row items-center space-x-2'>
|
||||||
<CountryFlag isoCode={n.iso_3166_1} size={10} />
|
<CountryFlag isoCode={n.iso_3166_1} size={10} />
|
||||||
@@ -196,17 +186,17 @@ const DetailFacts: React.FC<
|
|||||||
))}
|
))}
|
||||||
/>
|
/>
|
||||||
<Facts
|
<Facts
|
||||||
title={t("jellyseerr.studios")}
|
title={t("seerr.studios")}
|
||||||
facts={uniqBy(details?.productionCompanies, "name")?.map(
|
facts={uniqBy(details?.productionCompanies, "name")?.map(
|
||||||
(n) => n.name,
|
(n) => n.name,
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Facts
|
<Facts
|
||||||
title={t("jellyseerr.network")}
|
title={t("seerr.network")}
|
||||||
facts={networks?.map((n) => n.name)}
|
facts={networks?.map((n) => n.name)}
|
||||||
/>
|
/>
|
||||||
<Facts
|
<Facts
|
||||||
title={t("jellyseerr.currently_streaming_on")}
|
title={t("seerr.currently_streaming_on")}
|
||||||
facts={streamingProviders?.map((s) => s.name)}
|
facts={streamingProviders?.map((s) => s.name)}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
@@ -1,16 +1,10 @@
|
|||||||
|
import React from "react";
|
||||||
import { View } from "react-native";
|
import { View } from "react-native";
|
||||||
|
|
||||||
interface Props {
|
|
||||||
index: number;
|
|
||||||
}
|
|
||||||
// Dev note might be a good idea to standardize skeletons across the app and have one "file" for it.
|
// Dev note might be a good idea to standardize skeletons across the app and have one "file" for it.
|
||||||
export const GridSkeleton: React.FC<Props> = ({ index }) => {
|
export const GridSkeleton = React.memo(() => {
|
||||||
return (
|
return (
|
||||||
<View
|
<View className='flex flex-col mr-2 h-auto' style={{ width: "30.5%" }}>
|
||||||
key={index}
|
|
||||||
className='flex flex-col mr-2 h-auto'
|
|
||||||
style={{ width: "30.5%" }}
|
|
||||||
>
|
|
||||||
<View className='relative rounded-lg overflow-hidden border border-neutral-900 w-full mt-4 aspect-[10/15] bg-neutral-800' />
|
<View className='relative rounded-lg overflow-hidden border border-neutral-900 w-full mt-4 aspect-[10/15] bg-neutral-800' />
|
||||||
<View className='mt-2 flex flex-col w-full'>
|
<View className='mt-2 flex flex-col w-full'>
|
||||||
<View className='h-4 bg-neutral-800 rounded mb-1' />
|
<View className='h-4 bg-neutral-800 rounded mb-1' />
|
||||||
@@ -18,4 +12,4 @@ export const GridSkeleton: React.FC<Props> = ({ index }) => {
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
@@ -133,7 +133,7 @@ const ParallaxSlideShow = <T,>({
|
|||||||
<View className='px-4'>
|
<View className='px-4'>
|
||||||
<View className='flex flex-row flex-wrap'>
|
<View className='flex flex-row flex-wrap'>
|
||||||
{Array.from({ length: 9 }, (_, i) => (
|
{Array.from({ length: 9 }, (_, i) => (
|
||||||
<GridSkeleton key={i} index={i} />
|
<GridSkeleton key={i} />
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@@ -4,7 +4,7 @@ import { TouchableOpacity, View, type ViewProps } from "react-native";
|
|||||||
import { Text } from "@/components/common/Text";
|
import { Text } from "@/components/common/Text";
|
||||||
import Poster from "@/components/posters/Poster";
|
import Poster from "@/components/posters/Poster";
|
||||||
import useRouter from "@/hooks/useAppRouter";
|
import useRouter from "@/hooks/useAppRouter";
|
||||||
import { useJellyseerr } from "@/hooks/useJellyseerr";
|
import { useSeerr } from "@/hooks/useSeerr";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -20,7 +20,7 @@ const PersonPoster: React.FC<Props & ViewProps> = ({
|
|||||||
subName,
|
subName,
|
||||||
...props
|
...props
|
||||||
}) => {
|
}) => {
|
||||||
const { jellyseerrApi } = useJellyseerr();
|
const { seerrApi } = useSeerr();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const segments = useSegments();
|
const segments = useSegments();
|
||||||
const from = (segments as string[])[2] || "(home)";
|
const from = (segments as string[])[2] || "(home)";
|
||||||
@@ -28,20 +28,20 @@ const PersonPoster: React.FC<Props & ViewProps> = ({
|
|||||||
if (from === "(home)" || from === "(search)" || from === "(libraries)")
|
if (from === "(home)" || from === "(search)" || from === "(libraries)")
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
onPress={() =>
|
onPress={() => router.push(`/(auth)/(tabs)/${from}/seerr/person/${id}`)}
|
||||||
router.push(`/(auth)/(tabs)/${from}/jellyseerr/person/${id}`)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<View className='flex flex-col w-28' {...props}>
|
<View className='flex flex-col w-28' {...props}>
|
||||||
<Poster
|
<Poster
|
||||||
id={id}
|
id={id}
|
||||||
url={jellyseerrApi?.imageProxy(posterPath, "w600_and_h900_bestv2")}
|
url={seerrApi?.imageProxy(posterPath, "w600_and_h900_bestv2")}
|
||||||
/>
|
/>
|
||||||
<Text className='mt-2'>{name}</Text>
|
<Text className='mt-2'>{name}</Text>
|
||||||
{subName && <Text className='text-xs opacity-50'>{subName}</Text>}
|
{subName && <Text className='text-xs opacity-50'>{subName}</Text>}
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PersonPoster;
|
export default PersonPoster;
|
||||||
@@ -12,7 +12,7 @@ import { View, type ViewProps } from "react-native";
|
|||||||
import { Button } from "@/components/Button";
|
import { Button } from "@/components/Button";
|
||||||
import { Text } from "@/components/common/Text";
|
import { Text } from "@/components/common/Text";
|
||||||
import { PlatformDropdown } from "@/components/PlatformDropdown";
|
import { PlatformDropdown } from "@/components/PlatformDropdown";
|
||||||
import { useJellyseerr } from "@/hooks/useJellyseerr";
|
import { useSeerr } from "@/hooks/useSeerr";
|
||||||
import type {
|
import type {
|
||||||
QualityProfile,
|
QualityProfile,
|
||||||
RootFolder,
|
RootFolder,
|
||||||
@@ -38,14 +38,23 @@ const RequestModal = forwardRef<
|
|||||||
Props & Omit<ViewProps, "id">
|
Props & Omit<ViewProps, "id">
|
||||||
>(
|
>(
|
||||||
(
|
(
|
||||||
{ id, title, requestBody, type, isAnime = false, onRequested, onDismiss },
|
{
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
requestBody,
|
||||||
|
type,
|
||||||
|
isAnime = false,
|
||||||
|
is4k,
|
||||||
|
onRequested,
|
||||||
|
onDismiss,
|
||||||
|
},
|
||||||
ref,
|
ref,
|
||||||
) => {
|
) => {
|
||||||
const { jellyseerrApi, jellyseerrUser, requestMedia } = useJellyseerr();
|
const { seerrApi, seerrUser, requestMedia } = useSeerr();
|
||||||
const [requestOverrides, setRequestOverrides] = useState<MediaRequestBody>({
|
const [requestOverrides, setRequestOverrides] = useState<MediaRequestBody>({
|
||||||
mediaId: Number(id),
|
mediaId: Number(id),
|
||||||
mediaType: type,
|
mediaType: type,
|
||||||
userId: jellyseerrUser?.id,
|
userId: seerrUser?.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
const [qualityProfileOpen, setQualityProfileOpen] = useState(false);
|
const [qualityProfileOpen, setQualityProfileOpen] = useState(false);
|
||||||
@@ -65,18 +74,17 @@ const RequestModal = forwardRef<
|
|||||||
}, [onDismiss]);
|
}, [onDismiss]);
|
||||||
|
|
||||||
const { data: serviceSettings } = useQuery({
|
const { data: serviceSettings } = useQuery({
|
||||||
queryKey: ["jellyseerr", "request", type, "service"],
|
queryKey: ["seerr", "request", type, "service"],
|
||||||
queryFn: async () =>
|
queryFn: async () =>
|
||||||
jellyseerrApi?.service(type === "movie" ? "radarr" : "sonarr"),
|
seerrApi?.service(type === "movie" ? "radarr" : "sonarr"),
|
||||||
enabled: !!jellyseerrApi && !!jellyseerrUser,
|
enabled: !!seerrApi && !!seerrUser,
|
||||||
refetchOnMount: "always",
|
refetchOnMount: "always",
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: users } = useQuery({
|
const { data: users } = useQuery({
|
||||||
queryKey: ["jellyseerr", "users"],
|
queryKey: ["seerr", "users"],
|
||||||
queryFn: async () =>
|
queryFn: async () => seerrApi?.user({ take: 1000, sort: "displayname" }),
|
||||||
jellyseerrApi?.user({ take: 1000, sort: "displayname" }),
|
enabled: !!seerrApi && !!seerrUser,
|
||||||
enabled: !!jellyseerrApi && !!jellyseerrUser,
|
|
||||||
refetchOnMount: "always",
|
refetchOnMount: "always",
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -87,7 +95,7 @@ const RequestModal = forwardRef<
|
|||||||
|
|
||||||
const { data: defaultServiceDetails } = useQuery({
|
const { data: defaultServiceDetails } = useQuery({
|
||||||
queryKey: [
|
queryKey: [
|
||||||
"jellyseerr",
|
"seerr",
|
||||||
"request",
|
"request",
|
||||||
type,
|
type,
|
||||||
"service",
|
"service",
|
||||||
@@ -99,12 +107,12 @@ const RequestModal = forwardRef<
|
|||||||
...prev,
|
...prev,
|
||||||
serverId: defaultService?.id,
|
serverId: defaultService?.id,
|
||||||
}));
|
}));
|
||||||
return jellyseerrApi?.serviceDetails(
|
return seerrApi?.serviceDetails(
|
||||||
type === "movie" ? "radarr" : "sonarr",
|
type === "movie" ? "radarr" : "sonarr",
|
||||||
defaultService!.id,
|
defaultService!.id,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
enabled: !!jellyseerrApi && !!jellyseerrUser && !!defaultService,
|
enabled: !!seerrApi && !!seerrUser && !!defaultService,
|
||||||
refetchOnMount: "always",
|
refetchOnMount: "always",
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -148,9 +156,9 @@ const RequestModal = forwardRef<
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
if (requestBody.seasons.length > 1) {
|
if (requestBody.seasons.length > 1) {
|
||||||
return t("jellyseerr.season_all");
|
return t("seerr.season_all");
|
||||||
}
|
}
|
||||||
return t("jellyseerr.season_number", {
|
return t("seerr.season_number", {
|
||||||
season_number: requestBody.seasons[0],
|
season_number: requestBody.seasons[0],
|
||||||
});
|
});
|
||||||
}, [requestBody?.seasons]);
|
}, [requestBody?.seasons]);
|
||||||
@@ -245,8 +253,7 @@ const RequestModal = forwardRef<
|
|||||||
type: "radio" as const,
|
type: "radio" as const,
|
||||||
label: user.displayName,
|
label: user.displayName,
|
||||||
value: user.id.toString(),
|
value: user.id.toString(),
|
||||||
selected:
|
selected: (requestOverrides.userId || seerrUser?.id) === user.id,
|
||||||
(requestOverrides.userId || jellyseerrUser?.id) === user.id,
|
|
||||||
onPress: () =>
|
onPress: () =>
|
||||||
setRequestOverrides((prev) => ({
|
setRequestOverrides((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
@@ -255,12 +262,13 @@ const RequestModal = forwardRef<
|
|||||||
})) || [],
|
})) || [],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[users, jellyseerrUser, requestOverrides.userId],
|
[users, seerrUser, requestOverrides.userId],
|
||||||
);
|
);
|
||||||
|
|
||||||
const request = useCallback(() => {
|
const request = useCallback(() => {
|
||||||
const body = {
|
const body = {
|
||||||
is4k: defaultService?.is4k || defaultServiceDetails?.server.is4k,
|
is4k:
|
||||||
|
is4k ?? defaultService?.is4k ?? defaultServiceDetails?.server.is4k,
|
||||||
profileId: defaultProfile?.id,
|
profileId: defaultProfile?.id,
|
||||||
rootFolder: defaultFolder?.path,
|
rootFolder: defaultFolder?.path,
|
||||||
tags: defaultTags.map((t) => t.id),
|
tags: defaultTags.map((t) => t.id),
|
||||||
@@ -268,7 +276,7 @@ const RequestModal = forwardRef<
|
|||||||
...requestOverrides,
|
...requestOverrides,
|
||||||
};
|
};
|
||||||
|
|
||||||
writeDebugLog("Sending Jellyseerr advanced request", body);
|
writeDebugLog("Sending Seerr advanced request", body);
|
||||||
|
|
||||||
requestMedia(
|
requestMedia(
|
||||||
seasonTitle ? `${title}, ${seasonTitle}` : title,
|
seasonTitle ? `${title}, ${seasonTitle}` : title,
|
||||||
@@ -276,11 +284,18 @@ const RequestModal = forwardRef<
|
|||||||
onRequested,
|
onRequested,
|
||||||
);
|
);
|
||||||
}, [
|
}, [
|
||||||
|
is4k,
|
||||||
|
defaultService?.is4k,
|
||||||
|
defaultServiceDetails?.server.is4k,
|
||||||
requestBody,
|
requestBody,
|
||||||
requestOverrides,
|
requestOverrides,
|
||||||
defaultProfile,
|
defaultProfile,
|
||||||
defaultFolder,
|
defaultFolder,
|
||||||
defaultTags,
|
defaultTags,
|
||||||
|
requestMedia,
|
||||||
|
seasonTitle,
|
||||||
|
title,
|
||||||
|
onRequested,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -308,7 +323,7 @@ const RequestModal = forwardRef<
|
|||||||
<View className='flex flex-col space-y-4 px-4 pb-8 pt-2'>
|
<View className='flex flex-col space-y-4 px-4 pb-8 pt-2'>
|
||||||
<View>
|
<View>
|
||||||
<Text className='font-bold text-2xl text-neutral-100'>
|
<Text className='font-bold text-2xl text-neutral-100'>
|
||||||
{t("jellyseerr.advanced")}
|
{t("seerr.advanced")}
|
||||||
</Text>
|
</Text>
|
||||||
{seasonTitle && (
|
{seasonTitle && (
|
||||||
<Text className='text-neutral-300'>{seasonTitle}</Text>
|
<Text className='text-neutral-300'>{seasonTitle}</Text>
|
||||||
@@ -319,7 +334,7 @@ const RequestModal = forwardRef<
|
|||||||
<>
|
<>
|
||||||
<View className='flex flex-col'>
|
<View className='flex flex-col'>
|
||||||
<Text className='opacity-50 mb-1 text-xs'>
|
<Text className='opacity-50 mb-1 text-xs'>
|
||||||
{t("jellyseerr.quality_profile")}
|
{t("seerr.quality_profile")}
|
||||||
</Text>
|
</Text>
|
||||||
<PlatformDropdown
|
<PlatformDropdown
|
||||||
groups={qualityProfileOptions}
|
groups={qualityProfileOptions}
|
||||||
@@ -335,7 +350,7 @@ const RequestModal = forwardRef<
|
|||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
}
|
}
|
||||||
title={t("jellyseerr.quality_profile")}
|
title={t("seerr.quality_profile")}
|
||||||
open={qualityProfileOpen}
|
open={qualityProfileOpen}
|
||||||
onOpenChange={setQualityProfileOpen}
|
onOpenChange={setQualityProfileOpen}
|
||||||
/>
|
/>
|
||||||
@@ -343,7 +358,7 @@ const RequestModal = forwardRef<
|
|||||||
|
|
||||||
<View className='flex flex-col'>
|
<View className='flex flex-col'>
|
||||||
<Text className='opacity-50 mb-1 text-xs'>
|
<Text className='opacity-50 mb-1 text-xs'>
|
||||||
{t("jellyseerr.root_folder")}
|
{t("seerr.root_folder")}
|
||||||
</Text>
|
</Text>
|
||||||
<PlatformDropdown
|
<PlatformDropdown
|
||||||
groups={rootFolderOptions}
|
groups={rootFolderOptions}
|
||||||
@@ -368,42 +383,45 @@ const RequestModal = forwardRef<
|
|||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
}
|
}
|
||||||
title={t("jellyseerr.root_folder")}
|
title={t("seerr.root_folder")}
|
||||||
open={rootFolderOpen}
|
open={rootFolderOpen}
|
||||||
onOpenChange={setRootFolderOpen}
|
onOpenChange={setRootFolderOpen}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View className='flex flex-col'>
|
{defaultServiceDetails?.tags &&
|
||||||
<Text className='opacity-50 mb-1 text-xs'>
|
defaultServiceDetails.tags.length > 0 && (
|
||||||
{t("jellyseerr.tags")}
|
<View className='flex flex-col'>
|
||||||
</Text>
|
<Text className='opacity-50 mb-1 text-xs'>
|
||||||
<PlatformDropdown
|
{t("seerr.tags")}
|
||||||
groups={tagsOptions}
|
</Text>
|
||||||
trigger={
|
<PlatformDropdown
|
||||||
<View className='bg-neutral-900 h-10 rounded-xl border-neutral-800 border px-3 py-2 flex flex-row items-center justify-between'>
|
groups={tagsOptions}
|
||||||
<Text numberOfLines={1}>
|
trigger={
|
||||||
{requestOverrides.tags
|
<View className='bg-neutral-900 h-10 rounded-xl border-neutral-800 border px-3 py-2 flex flex-row items-center justify-between'>
|
||||||
? defaultServiceDetails.tags
|
<Text numberOfLines={1}>
|
||||||
.filter((t) =>
|
{requestOverrides.tags
|
||||||
requestOverrides.tags!.includes(t.id),
|
? defaultServiceDetails.tags
|
||||||
)
|
.filter((t) =>
|
||||||
.map((t) => t.label)
|
requestOverrides.tags!.includes(t.id),
|
||||||
.join(", ") ||
|
)
|
||||||
defaultTags.map((t) => t.label).join(", ")
|
.map((t) => t.label)
|
||||||
: defaultTags.map((t) => t.label).join(", ")}
|
.join(", ") ||
|
||||||
</Text>
|
defaultTags.map((t) => t.label).join(", ")
|
||||||
</View>
|
: defaultTags.map((t) => t.label).join(", ")}
|
||||||
}
|
</Text>
|
||||||
title={t("jellyseerr.tags")}
|
</View>
|
||||||
open={tagsOpen}
|
}
|
||||||
onOpenChange={setTagsOpen}
|
title={t("seerr.tags")}
|
||||||
/>
|
open={tagsOpen}
|
||||||
</View>
|
onOpenChange={setTagsOpen}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
<View className='flex flex-col'>
|
<View className='flex flex-col'>
|
||||||
<Text className='opacity-50 mb-1 text-xs'>
|
<Text className='opacity-50 mb-1 text-xs'>
|
||||||
{t("jellyseerr.request_as")}
|
{t("seerr.request_as")}
|
||||||
</Text>
|
</Text>
|
||||||
<PlatformDropdown
|
<PlatformDropdown
|
||||||
groups={usersOptions}
|
groups={usersOptions}
|
||||||
@@ -413,12 +431,12 @@ const RequestModal = forwardRef<
|
|||||||
{users.find(
|
{users.find(
|
||||||
(u) =>
|
(u) =>
|
||||||
u.id ===
|
u.id ===
|
||||||
(requestOverrides.userId || jellyseerrUser?.id),
|
(requestOverrides.userId || seerrUser?.id),
|
||||||
)?.displayName || jellyseerrUser!.displayName}
|
)?.displayName || seerrUser!.displayName}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
}
|
}
|
||||||
title={t("jellyseerr.request_as")}
|
title={t("seerr.request_as")}
|
||||||
open={usersOpen}
|
open={usersOpen}
|
||||||
onOpenChange={setUsersOpen}
|
onOpenChange={setUsersOpen}
|
||||||
/>
|
/>
|
||||||
@@ -427,7 +445,7 @@ const RequestModal = forwardRef<
|
|||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
<Button className='mt-auto' onPress={request} color='purple'>
|
<Button className='mt-auto' onPress={request} color='purple'>
|
||||||
{t("jellyseerr.request_button")}
|
{t("seerr.request_button")}
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
</BottomSheetView>
|
</BottomSheetView>
|
||||||
@@ -8,8 +8,8 @@ import {
|
|||||||
useSharedValue,
|
useSharedValue,
|
||||||
withTiming,
|
withTiming,
|
||||||
} from "react-native-reanimated";
|
} from "react-native-reanimated";
|
||||||
import Discover from "@/components/jellyseerr/discover/Discover";
|
import Discover from "@/components/seerr/discover/Discover";
|
||||||
import { useJellyseerr } from "@/hooks/useJellyseerr";
|
import { useSeerr } from "@/hooks/useSeerr";
|
||||||
import { MediaType } from "@/utils/jellyseerr/server/constants/media";
|
import { MediaType } from "@/utils/jellyseerr/server/constants/media";
|
||||||
import type {
|
import type {
|
||||||
MovieResult,
|
MovieResult,
|
||||||
@@ -18,57 +18,57 @@ import type {
|
|||||||
} from "@/utils/jellyseerr/server/models/Search";
|
} from "@/utils/jellyseerr/server/models/Search";
|
||||||
import { useReactNavigationQuery } from "@/utils/useReactNavigationQuery";
|
import { useReactNavigationQuery } from "@/utils/useReactNavigationQuery";
|
||||||
import { Text } from "../common/Text";
|
import { Text } from "../common/Text";
|
||||||
import JellyseerrPoster from "../posters/JellyseerrPoster";
|
import SeerrPoster from "../posters/SeerrPoster";
|
||||||
import { LoadingSkeleton } from "../search/LoadingSkeleton";
|
import { LoadingSkeleton } from "../search/LoadingSkeleton";
|
||||||
import { SearchItemWrapper } from "../search/SearchItemWrapper";
|
import { SearchItemWrapper } from "../search/SearchItemWrapper";
|
||||||
import PersonPoster from "./PersonPoster";
|
import PersonPoster from "./PersonPoster";
|
||||||
|
|
||||||
interface Props extends ViewProps {
|
interface Props extends ViewProps {
|
||||||
searchQuery: string;
|
searchQuery: string;
|
||||||
sortType?: JellyseerrSearchSort;
|
sortType?: SeerrSearchSort;
|
||||||
order?: "asc" | "desc";
|
order?: "asc" | "desc";
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum JellyseerrSearchSort {
|
export enum SeerrSearchSort {
|
||||||
DEFAULT = 0,
|
DEFAULT = 0,
|
||||||
VOTE_COUNT_AND_AVERAGE = 1,
|
VOTE_COUNT_AND_AVERAGE = 1,
|
||||||
POPULARITY = 2,
|
POPULARITY = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const JellyserrIndexPage: React.FC<Props> = ({
|
export const SeerrIndexPage: React.FC<Props> = ({
|
||||||
searchQuery,
|
searchQuery,
|
||||||
sortType,
|
sortType,
|
||||||
order,
|
order,
|
||||||
}) => {
|
}) => {
|
||||||
const { jellyseerrApi } = useJellyseerr();
|
const { seerrApi } = useSeerr();
|
||||||
const opacity = useSharedValue(1);
|
const opacity = useSharedValue(1);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: jellyseerrDiscoverSettings,
|
data: seerrDiscoverSettings,
|
||||||
isFetching: f1,
|
isFetching: f1,
|
||||||
isLoading: l1,
|
isLoading: l1,
|
||||||
} = useReactNavigationQuery({
|
} = useReactNavigationQuery({
|
||||||
queryKey: ["search", "jellyseerr", "discoverSettings", searchQuery],
|
queryKey: ["search", "seerr", "discoverSettings", searchQuery],
|
||||||
queryFn: async () => jellyseerrApi?.discoverSettings(),
|
queryFn: async () => seerrApi?.discoverSettings(),
|
||||||
enabled: !!jellyseerrApi && searchQuery.length === 0,
|
enabled: !!seerrApi && searchQuery.length === 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: jellyseerrResults,
|
data: seerrResults,
|
||||||
isFetching: f2,
|
isFetching: f2,
|
||||||
isLoading: l2,
|
isLoading: l2,
|
||||||
} = useReactNavigationQuery({
|
} = useReactNavigationQuery({
|
||||||
queryKey: ["search", "jellyseerr", "results", searchQuery],
|
queryKey: ["search", "seerr", "results", searchQuery],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const params = {
|
const params = {
|
||||||
query: new URLSearchParams(searchQuery || "").toString(),
|
query: new URLSearchParams(searchQuery || "").toString(),
|
||||||
};
|
};
|
||||||
return await Promise.all([
|
return await Promise.all([
|
||||||
jellyseerrApi?.search({ ...params, page: 1 }),
|
seerrApi?.search({ ...params, page: 1 }),
|
||||||
jellyseerrApi?.search({ ...params, page: 2 }),
|
seerrApi?.search({ ...params, page: 2 }),
|
||||||
jellyseerrApi?.search({ ...params, page: 3 }),
|
seerrApi?.search({ ...params, page: 3 }),
|
||||||
jellyseerrApi?.search({ ...params, page: 4 }),
|
seerrApi?.search({ ...params, page: 4 }),
|
||||||
]).then((all) =>
|
]).then((all) =>
|
||||||
uniqBy(
|
uniqBy(
|
||||||
all.flatMap((v) => v?.results || []),
|
all.flatMap((v) => v?.results || []),
|
||||||
@@ -76,7 +76,7 @@ export const JellyserrIndexPage: React.FC<Props> = ({
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
enabled: !!jellyseerrApi && searchQuery.length > 0,
|
enabled: !!seerrApi && searchQuery.length > 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
useAnimatedReaction(
|
useAnimatedReaction(
|
||||||
@@ -92,20 +92,20 @@ export const JellyserrIndexPage: React.FC<Props> = ({
|
|||||||
|
|
||||||
const sortingType = useMemo(() => {
|
const sortingType = useMemo(() => {
|
||||||
if (!sortType) return;
|
if (!sortType) return;
|
||||||
switch (Number(JellyseerrSearchSort[sortType])) {
|
switch (Number(SeerrSearchSort[sortType])) {
|
||||||
case JellyseerrSearchSort.VOTE_COUNT_AND_AVERAGE:
|
case SeerrSearchSort.VOTE_COUNT_AND_AVERAGE:
|
||||||
return ["voteCount", "voteAverage"];
|
return ["voteCount", "voteAverage"];
|
||||||
case JellyseerrSearchSort.POPULARITY:
|
case SeerrSearchSort.POPULARITY:
|
||||||
return ["voteCount", "popularity"];
|
return ["voteCount", "popularity"];
|
||||||
default:
|
default:
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}, [sortType, order]);
|
}, [sortType, order]);
|
||||||
|
|
||||||
const jellyseerrMovieResults = useMemo(
|
const seerrMovieResults = useMemo(
|
||||||
() =>
|
() =>
|
||||||
orderBy(
|
orderBy(
|
||||||
jellyseerrResults?.filter(
|
seerrResults?.filter(
|
||||||
(r) => r.mediaType === MediaType.MOVIE,
|
(r) => r.mediaType === MediaType.MOVIE,
|
||||||
) as MovieResult[],
|
) as MovieResult[],
|
||||||
sortingType || [
|
sortingType || [
|
||||||
@@ -113,41 +113,37 @@ export const JellyserrIndexPage: React.FC<Props> = ({
|
|||||||
],
|
],
|
||||||
order || "desc",
|
order || "desc",
|
||||||
),
|
),
|
||||||
[jellyseerrResults, sortingType, order],
|
[seerrResults, sortingType, order, searchQuery],
|
||||||
);
|
);
|
||||||
|
|
||||||
const jellyseerrTvResults = useMemo(
|
const seerrTvResults = useMemo(
|
||||||
() =>
|
() =>
|
||||||
orderBy(
|
orderBy(
|
||||||
jellyseerrResults?.filter(
|
seerrResults?.filter((r) => r.mediaType === MediaType.TV) as TvResult[],
|
||||||
(r) => r.mediaType === MediaType.TV,
|
|
||||||
) as TvResult[],
|
|
||||||
sortingType || [
|
sortingType || [
|
||||||
(t) => t.name.toLowerCase() === searchQuery.toLowerCase(),
|
(t) => t.name.toLowerCase() === searchQuery.toLowerCase(),
|
||||||
],
|
],
|
||||||
order || "desc",
|
order || "desc",
|
||||||
),
|
),
|
||||||
[jellyseerrResults, sortingType, order],
|
[seerrResults, sortingType, order, searchQuery],
|
||||||
);
|
);
|
||||||
|
|
||||||
const jellyseerrPersonResults = useMemo(
|
const seerrPersonResults = useMemo(
|
||||||
() =>
|
() =>
|
||||||
orderBy(
|
orderBy(
|
||||||
jellyseerrResults?.filter(
|
seerrResults?.filter((r) => r.mediaType === "person") as PersonResult[],
|
||||||
(r) => r.mediaType === "person",
|
|
||||||
) as PersonResult[],
|
|
||||||
sortingType || [
|
sortingType || [
|
||||||
(p) => p.name.toLowerCase() === searchQuery.toLowerCase(),
|
(p) => p.name.toLowerCase() === searchQuery.toLowerCase(),
|
||||||
],
|
],
|
||||||
order || "desc",
|
order || "desc",
|
||||||
),
|
),
|
||||||
[jellyseerrResults, sortingType, order],
|
[seerrResults, sortingType, order, searchQuery],
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!searchQuery.length)
|
if (!searchQuery.length)
|
||||||
return (
|
return (
|
||||||
<View className='flex flex-col'>
|
<View className='flex flex-col'>
|
||||||
<Discover sliders={jellyseerrDiscoverSettings} />
|
<Discover sliders={seerrDiscoverSettings} />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -155,9 +151,9 @@ export const JellyserrIndexPage: React.FC<Props> = ({
|
|||||||
<View>
|
<View>
|
||||||
<LoadingSkeleton isLoading={f1 || f2 || l1 || l2} />
|
<LoadingSkeleton isLoading={f1 || f2 || l1 || l2} />
|
||||||
|
|
||||||
{!jellyseerrMovieResults?.length &&
|
{!seerrMovieResults?.length &&
|
||||||
!jellyseerrTvResults?.length &&
|
!seerrTvResults?.length &&
|
||||||
!jellyseerrPersonResults?.length &&
|
!seerrPersonResults?.length &&
|
||||||
!f1 &&
|
!f1 &&
|
||||||
!f2 &&
|
!f2 &&
|
||||||
!l1 &&
|
!l1 &&
|
||||||
@@ -175,21 +171,21 @@ export const JellyserrIndexPage: React.FC<Props> = ({
|
|||||||
<View className={f1 || f2 || l1 || l2 ? "opacity-0" : "opacity-100"}>
|
<View className={f1 || f2 || l1 || l2 ? "opacity-0" : "opacity-100"}>
|
||||||
<SearchItemWrapper
|
<SearchItemWrapper
|
||||||
header={t("search.request_movies")}
|
header={t("search.request_movies")}
|
||||||
items={jellyseerrMovieResults}
|
items={seerrMovieResults}
|
||||||
renderItem={(item: MovieResult) => (
|
renderItem={(item: MovieResult) => (
|
||||||
<JellyseerrPoster item={item} key={item.id} />
|
<SeerrPoster item={item} key={item.id} />
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<SearchItemWrapper
|
<SearchItemWrapper
|
||||||
header={t("search.request_series")}
|
header={t("search.request_series")}
|
||||||
items={jellyseerrTvResults}
|
items={seerrTvResults}
|
||||||
renderItem={(item: TvResult) => (
|
renderItem={(item: TvResult) => (
|
||||||
<JellyseerrPoster item={item} key={item.id} />
|
<SeerrPoster item={item} key={item.id} />
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<SearchItemWrapper
|
<SearchItemWrapper
|
||||||
header={t("search.actors")}
|
header={t("search.actors")}
|
||||||
items={jellyseerrPersonResults}
|
items={seerrPersonResults}
|
||||||
renderItem={(item: PersonResult) => (
|
renderItem={(item: PersonResult) => (
|
||||||
<PersonPoster
|
<PersonPoster
|
||||||
className='mr-2'
|
className='mr-2'
|
||||||
@@ -3,9 +3,11 @@ import { useMemo } from "react";
|
|||||||
import { View, type ViewProps } from "react-native";
|
import { View, type ViewProps } from "react-native";
|
||||||
import { MediaType } from "@/utils/jellyseerr/server/constants/media";
|
import { MediaType } from "@/utils/jellyseerr/server/constants/media";
|
||||||
|
|
||||||
const JellyseerrMediaIcon: React.FC<
|
const SeerrMediaIcon: React.FC<{ mediaType: "tv" | "movie" } & ViewProps> = ({
|
||||||
{ mediaType: "tv" | "movie" } & ViewProps
|
mediaType,
|
||||||
> = ({ mediaType, className, ...props }) => {
|
className,
|
||||||
|
...props
|
||||||
|
}) => {
|
||||||
const style = useMemo(
|
const style = useMemo(
|
||||||
() =>
|
() =>
|
||||||
mediaType === MediaType.MOVIE
|
mediaType === MediaType.MOVIE
|
||||||
@@ -29,4 +31,4 @@ const JellyseerrMediaIcon: React.FC<
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default JellyseerrMediaIcon;
|
export default SeerrMediaIcon;
|
||||||
@@ -9,7 +9,7 @@ interface Props {
|
|||||||
onPress?: () => void;
|
onPress?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const JellyseerrStatusIcon: React.FC<Props & ViewProps> = ({
|
const SeerrStatusIcon: React.FC<Props & ViewProps> = ({
|
||||||
mediaStatus,
|
mediaStatus,
|
||||||
showRequestIcon,
|
showRequestIcon,
|
||||||
onPress,
|
onPress,
|
||||||
@@ -74,4 +74,4 @@ const JellyseerrStatusIcon: React.FC<Props & ViewProps> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default JellyseerrStatusIcon;
|
export default SeerrStatusIcon;
|
||||||
@@ -2,10 +2,10 @@ import { useSegments } from "expo-router";
|
|||||||
import type React from "react";
|
import type React from "react";
|
||||||
import { useCallback } from "react";
|
import { useCallback } from "react";
|
||||||
import { TouchableOpacity, type ViewProps } from "react-native";
|
import { TouchableOpacity, type ViewProps } from "react-native";
|
||||||
import GenericSlideCard from "@/components/jellyseerr/discover/GenericSlideCard";
|
import GenericSlideCard from "@/components/seerr/discover/GenericSlideCard";
|
||||||
import Slide, { type SlideProps } from "@/components/jellyseerr/discover/Slide";
|
import Slide, { type SlideProps } from "@/components/seerr/discover/Slide";
|
||||||
import useRouter from "@/hooks/useAppRouter";
|
import useRouter from "@/hooks/useAppRouter";
|
||||||
import { useJellyseerr } from "@/hooks/useJellyseerr";
|
import { useSeerr } from "@/hooks/useSeerr";
|
||||||
import {
|
import {
|
||||||
COMPANY_LOGO_IMAGE_FILTER,
|
COMPANY_LOGO_IMAGE_FILTER,
|
||||||
type Network,
|
type Network,
|
||||||
@@ -16,17 +16,17 @@ const CompanySlide: React.FC<
|
|||||||
{ data: Network[] | Studio[] } & SlideProps & ViewProps
|
{ data: Network[] | Studio[] } & SlideProps & ViewProps
|
||||||
> = ({ slide, data, ...props }) => {
|
> = ({ slide, data, ...props }) => {
|
||||||
const segments = useSegments();
|
const segments = useSegments();
|
||||||
const { jellyseerrApi } = useJellyseerr();
|
const { seerrApi } = useSeerr();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const from = (segments as string[])[2] || "(home)";
|
const from = (segments as string[])[2] || "(home)";
|
||||||
|
|
||||||
const navigate = useCallback(
|
const navigate = useCallback(
|
||||||
({ id, image, name }: Network | Studio) =>
|
({ id, image, name }: Network | Studio) =>
|
||||||
router.push({
|
router.push({
|
||||||
pathname: `/(auth)/(tabs)/${from}/jellyseerr/company/${id}` as any,
|
pathname: `/(auth)/(tabs)/${from}/seerr/company/${id}` as any,
|
||||||
params: { id, image, name, type: slide.type },
|
params: { id, image, name, type: slide.type },
|
||||||
}),
|
}),
|
||||||
[slide],
|
[router, from, slide.type],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -40,10 +40,7 @@ const CompanySlide: React.FC<
|
|||||||
<GenericSlideCard
|
<GenericSlideCard
|
||||||
className='w-28 rounded-lg overflow-hidden border border-neutral-900 p-4'
|
className='w-28 rounded-lg overflow-hidden border border-neutral-900 p-4'
|
||||||
id={item.id.toString()}
|
id={item.id.toString()}
|
||||||
url={jellyseerrApi?.imageProxy(
|
url={seerrApi?.imageProxy(item.image, COMPANY_LOGO_IMAGE_FILTER)}
|
||||||
item.image,
|
|
||||||
COMPANY_LOGO_IMAGE_FILTER,
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)}
|
)}
|
||||||
@@ -2,10 +2,10 @@ import { sortBy } from "lodash";
|
|||||||
import type React from "react";
|
import type React from "react";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { View } from "react-native";
|
import { View } from "react-native";
|
||||||
import CompanySlide from "@/components/jellyseerr/discover/CompanySlide";
|
import CompanySlide from "@/components/seerr/discover/CompanySlide";
|
||||||
import GenreSlide from "@/components/jellyseerr/discover/GenreSlide";
|
import GenreSlide from "@/components/seerr/discover/GenreSlide";
|
||||||
import MovieTvSlide from "@/components/jellyseerr/discover/MovieTvSlide";
|
import MovieTvSlide from "@/components/seerr/discover/MovieTvSlide";
|
||||||
import RecentRequestsSlide from "@/components/jellyseerr/discover/RecentRequestsSlide";
|
import RecentRequestsSlide from "@/components/seerr/discover/RecentRequestsSlide";
|
||||||
import { DiscoverSliderType } from "@/utils/jellyseerr/server/constants/discover";
|
import { DiscoverSliderType } from "@/utils/jellyseerr/server/constants/discover";
|
||||||
import type DiscoverSlider from "@/utils/jellyseerr/server/entity/DiscoverSlider";
|
import type DiscoverSlider from "@/utils/jellyseerr/server/entity/DiscoverSlider";
|
||||||
import { networks } from "@/utils/jellyseerr/src/components/Discover/NetworkSlider";
|
import { networks } from "@/utils/jellyseerr/src/components/Discover/NetworkSlider";
|
||||||
@@ -23,7 +23,6 @@ const Discover: React.FC<Props> = ({ sliders }) => {
|
|||||||
sortBy(
|
sortBy(
|
||||||
(sliders ?? []).filter((s) => s.enabled),
|
(sliders ?? []).filter((s) => s.enabled),
|
||||||
"order",
|
"order",
|
||||||
"asc",
|
|
||||||
),
|
),
|
||||||
[sliders],
|
[sliders],
|
||||||
);
|
);
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Image, type ImageContentFit } from "expo-image";
|
import { Image, type ImageContentFit } from "expo-image";
|
||||||
import { LinearGradient } from "expo-linear-gradient";
|
import { LinearGradient } from "expo-linear-gradient";
|
||||||
import type React from "react";
|
import React from "react";
|
||||||
import { StyleSheet, View, type ViewProps } from "react-native";
|
import { StyleSheet, View, type ViewProps } from "react-native";
|
||||||
import { Text } from "@/components/common/Text";
|
import { Text } from "@/components/common/Text";
|
||||||
|
|
||||||
@@ -67,4 +67,4 @@ const GenericSlideCard: React.FC<
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default GenericSlideCard;
|
export default React.memo(GenericSlideCard);
|
||||||
@@ -3,39 +3,38 @@ import { useSegments } from "expo-router";
|
|||||||
import type React from "react";
|
import type React from "react";
|
||||||
import { useCallback } from "react";
|
import { useCallback } from "react";
|
||||||
import { TouchableOpacity, type ViewProps } from "react-native";
|
import { TouchableOpacity, type ViewProps } from "react-native";
|
||||||
import GenericSlideCard from "@/components/jellyseerr/discover/GenericSlideCard";
|
import GenericSlideCard from "@/components/seerr/discover/GenericSlideCard";
|
||||||
import Slide, { type SlideProps } from "@/components/jellyseerr/discover/Slide";
|
import Slide, { type SlideProps } from "@/components/seerr/discover/Slide";
|
||||||
import useRouter from "@/hooks/useAppRouter";
|
import useRouter from "@/hooks/useAppRouter";
|
||||||
import { Endpoints, useJellyseerr } from "@/hooks/useJellyseerr";
|
import { Endpoints, useSeerr } from "@/hooks/useSeerr";
|
||||||
import { DiscoverSliderType } from "@/utils/jellyseerr/server/constants/discover";
|
import { DiscoverSliderType } from "@/utils/jellyseerr/server/constants/discover";
|
||||||
import type { GenreSliderItem } from "@/utils/jellyseerr/server/interfaces/api/discoverInterfaces";
|
import type { GenreSliderItem } from "@/utils/jellyseerr/server/interfaces/api/discoverInterfaces";
|
||||||
import { genreColorMap } from "@/utils/jellyseerr/src/components/Discover/constants";
|
import { genreColorMap } from "@/utils/jellyseerr/src/components/Discover/constants";
|
||||||
|
|
||||||
const GenreSlide: React.FC<SlideProps & ViewProps> = ({ slide, ...props }) => {
|
const GenreSlide: React.FC<SlideProps & ViewProps> = ({ slide, ...props }) => {
|
||||||
const segments = useSegments();
|
const segments = useSegments();
|
||||||
const { jellyseerrApi } = useJellyseerr();
|
const { seerrApi } = useSeerr();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const from = (segments as string[])[2] || "(home)";
|
const from = (segments as string[])[2] || "(home)";
|
||||||
|
|
||||||
const navigate = useCallback(
|
const navigate = useCallback(
|
||||||
(genre: GenreSliderItem) =>
|
(genre: GenreSliderItem) =>
|
||||||
router.push({
|
router.push({
|
||||||
pathname: `/(auth)/(tabs)/${from}/jellyseerr/genre/${genre.id}` as any,
|
pathname: `/(auth)/(tabs)/${from}/seerr/genre/${genre.id}` as any,
|
||||||
params: { type: slide.type, name: genre.name },
|
params: { type: slide.type, name: genre.name },
|
||||||
}),
|
}),
|
||||||
[slide],
|
[router, from, slide.type],
|
||||||
);
|
);
|
||||||
|
|
||||||
const { data } = useQuery({
|
const { data } = useQuery({
|
||||||
queryKey: ["jellyseerr", "discover", slide.type, slide.id],
|
queryKey: ["seerr", "discover", slide.type, slide.id],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
return jellyseerrApi?.getGenreSliders(
|
return seerrApi?.getGenreSliders(
|
||||||
slide.type === DiscoverSliderType.MOVIE_GENRES
|
slide.type === DiscoverSliderType.MOVIE_GENRES
|
||||||
? Endpoints.MOVIE
|
? Endpoints.MOVIE
|
||||||
: Endpoints.TV,
|
: Endpoints.TV,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
enabled: !!jellyseerrApi,
|
enabled: !!seerrApi,
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -53,7 +52,7 @@ const GenreSlide: React.FC<SlideProps & ViewProps> = ({ slide, ...props }) => {
|
|||||||
title={item.name}
|
title={item.name}
|
||||||
colors={["transparent", "transparent"]}
|
colors={["transparent", "transparent"]}
|
||||||
contentFit={"cover"}
|
contentFit={"cover"}
|
||||||
url={jellyseerrApi?.imageProxy(
|
url={seerrApi?.imageProxy(
|
||||||
item.backdrops?.[0],
|
item.backdrops?.[0],
|
||||||
`w780_filter(duotone,${
|
`w780_filter(duotone,${
|
||||||
genreColorMap[item.id] ?? genreColorMap[0]
|
genreColorMap[item.id] ?? genreColorMap[0]
|
||||||
@@ -3,23 +3,19 @@ import { uniqBy } from "lodash";
|
|||||||
import type React from "react";
|
import type React from "react";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import type { ViewProps } from "react-native";
|
import type { ViewProps } from "react-native";
|
||||||
import Slide, { type SlideProps } from "@/components/jellyseerr/discover/Slide";
|
import SeerrPoster from "@/components/posters/SeerrPoster";
|
||||||
import JellyseerrPoster from "@/components/posters/JellyseerrPoster";
|
import Slide, { type SlideProps } from "@/components/seerr/discover/Slide";
|
||||||
import {
|
import { type DiscoverEndpoint, Endpoints, useSeerr } from "@/hooks/useSeerr";
|
||||||
type DiscoverEndpoint,
|
|
||||||
Endpoints,
|
|
||||||
useJellyseerr,
|
|
||||||
} from "@/hooks/useJellyseerr";
|
|
||||||
import { DiscoverSliderType } from "@/utils/jellyseerr/server/constants/discover";
|
import { DiscoverSliderType } from "@/utils/jellyseerr/server/constants/discover";
|
||||||
|
|
||||||
const MovieTvSlide: React.FC<SlideProps & ViewProps> = ({
|
const MovieTvSlide: React.FC<SlideProps & ViewProps> = ({
|
||||||
slide,
|
slide,
|
||||||
...props
|
...props
|
||||||
}) => {
|
}) => {
|
||||||
const { jellyseerrApi, isJellyseerrMovieOrTvResult } = useJellyseerr();
|
const { seerrApi, isSeerrMovieOrTvResult } = useSeerr();
|
||||||
|
|
||||||
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
|
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
|
||||||
queryKey: ["jellyseerr", "discover", slide.id],
|
queryKey: ["seerr", "discover", slide.id],
|
||||||
queryFn: async ({ pageParam }) => {
|
queryFn: async ({ pageParam }) => {
|
||||||
let endpoint: DiscoverEndpoint | undefined;
|
let endpoint: DiscoverEndpoint | undefined;
|
||||||
let params: any = {
|
let params: any = {
|
||||||
@@ -50,13 +46,13 @@ const MovieTvSlide: React.FC<SlideProps & ViewProps> = ({
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return endpoint ? jellyseerrApi?.discover(endpoint, params) : null;
|
return endpoint ? seerrApi?.discover(endpoint, params) : null;
|
||||||
},
|
},
|
||||||
initialPageParam: 1,
|
initialPageParam: 1,
|
||||||
getNextPageParam: (lastPage, pages) =>
|
getNextPageParam: (lastPage, pages) =>
|
||||||
(lastPage?.page || pages?.findLast((p) => p?.results.length)?.page || 1) +
|
(lastPage?.page || pages?.findLast((p) => p?.results.length)?.page || 1) +
|
||||||
1,
|
1,
|
||||||
enabled: !!jellyseerrApi,
|
enabled: !!seerrApi,
|
||||||
staleTime: 0,
|
staleTime: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -65,12 +61,10 @@ const MovieTvSlide: React.FC<SlideProps & ViewProps> = ({
|
|||||||
uniqBy(
|
uniqBy(
|
||||||
data?.pages
|
data?.pages
|
||||||
?.filter((p) => p?.results.length)
|
?.filter((p) => p?.results.length)
|
||||||
.flatMap((p) =>
|
.flatMap((p) => p?.results.filter((r) => isSeerrMovieOrTvResult(r))),
|
||||||
p?.results.filter((r) => isJellyseerrMovieOrTvResult(r)),
|
|
||||||
),
|
|
||||||
"id",
|
"id",
|
||||||
),
|
),
|
||||||
[data],
|
[data, isSeerrMovieOrTvResult],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -84,7 +78,7 @@ const MovieTvSlide: React.FC<SlideProps & ViewProps> = ({
|
|||||||
onEndReached={() => {
|
onEndReached={() => {
|
||||||
if (hasNextPage) fetchNextPage();
|
if (hasNextPage) fetchNextPage();
|
||||||
}}
|
}}
|
||||||
renderItem={(item) => <JellyseerrPoster item={item} key={item?.id} />}
|
renderItem={(item) => <SeerrPoster item={item} key={item?.id} />}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import type React from "react";
|
import type React from "react";
|
||||||
import type { ViewProps } from "react-native";
|
import type { ViewProps } from "react-native";
|
||||||
import Slide, { type SlideProps } from "@/components/jellyseerr/discover/Slide";
|
import SeerrPoster from "@/components/posters/SeerrPoster";
|
||||||
import JellyseerrPoster from "@/components/posters/JellyseerrPoster";
|
import Slide, { type SlideProps } from "@/components/seerr/discover/Slide";
|
||||||
import { useJellyseerr } from "@/hooks/useJellyseerr";
|
import { useSeerr } from "@/hooks/useSeerr";
|
||||||
import { MediaType } from "@/utils/jellyseerr/server/constants/media";
|
import { MediaType } from "@/utils/jellyseerr/server/constants/media";
|
||||||
import type MediaRequest from "@/utils/jellyseerr/server/entity/MediaRequest";
|
import type MediaRequest from "@/utils/jellyseerr/server/entity/MediaRequest";
|
||||||
import type { NonFunctionProperties } from "@/utils/jellyseerr/server/interfaces/api/common";
|
import type { NonFunctionProperties } from "@/utils/jellyseerr/server/interfaces/api/common";
|
||||||
@@ -16,36 +16,36 @@ type ExtendedMediaRequest = NonFunctionProperties<MediaRequest> & {
|
|||||||
const RequestCard: React.FC<{ request: ExtendedMediaRequest }> = ({
|
const RequestCard: React.FC<{ request: ExtendedMediaRequest }> = ({
|
||||||
request,
|
request,
|
||||||
}) => {
|
}) => {
|
||||||
const { jellyseerrApi } = useJellyseerr();
|
const { seerrApi } = useSeerr();
|
||||||
|
|
||||||
const { data: details } = useQuery({
|
const { data: details } = useQuery({
|
||||||
queryKey: [
|
queryKey: [
|
||||||
"jellyseerr",
|
"seerr",
|
||||||
"detail",
|
"detail",
|
||||||
request.media.mediaType,
|
request.media.mediaType,
|
||||||
request.media.tmdbId,
|
request.media.tmdbId,
|
||||||
],
|
],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
return request.media.mediaType === MediaType.MOVIE
|
return request.media.mediaType === MediaType.MOVIE
|
||||||
? jellyseerrApi?.movieDetails(request.media.tmdbId)
|
? seerrApi?.movieDetails(request.media.tmdbId)
|
||||||
: jellyseerrApi?.tvDetails(request.media.tmdbId);
|
: seerrApi?.tvDetails(request.media.tmdbId);
|
||||||
},
|
},
|
||||||
enabled: !!jellyseerrApi,
|
enabled: !!seerrApi,
|
||||||
refetchOnMount: true,
|
refetchOnMount: true,
|
||||||
staleTime: 0,
|
staleTime: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data: refreshedRequest } = useQuery({
|
const { data: refreshedRequest } = useQuery({
|
||||||
queryKey: ["jellyseerr", "requests", request.media.mediaType, request.id],
|
queryKey: ["seerr", "requests", request.media.mediaType, request.id],
|
||||||
queryFn: async () => jellyseerrApi?.getRequest(request.id),
|
queryFn: async () => seerrApi?.getRequest(request.id),
|
||||||
enabled: !!jellyseerrApi,
|
enabled: !!seerrApi,
|
||||||
refetchOnMount: true,
|
refetchOnMount: true,
|
||||||
refetchInterval: 5000,
|
refetchInterval: 5000,
|
||||||
staleTime: 0,
|
staleTime: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<JellyseerrPoster
|
<SeerrPoster
|
||||||
horizontal
|
horizontal
|
||||||
showDownloadInfo
|
showDownloadInfo
|
||||||
item={details}
|
item={details}
|
||||||
@@ -58,12 +58,12 @@ const RecentRequestsSlide: React.FC<SlideProps & ViewProps> = ({
|
|||||||
slide,
|
slide,
|
||||||
...props
|
...props
|
||||||
}) => {
|
}) => {
|
||||||
const { jellyseerrApi } = useJellyseerr();
|
const { seerrApi } = useSeerr();
|
||||||
|
|
||||||
const { data: requests } = useQuery({
|
const { data: requests } = useQuery({
|
||||||
queryKey: ["jellyseerr", "recent_requests"],
|
queryKey: ["seerr", "recent_requests"],
|
||||||
queryFn: async () => jellyseerrApi?.requests(),
|
queryFn: async () => seerrApi?.requests(),
|
||||||
enabled: !!jellyseerrApi,
|
enabled: !!seerrApi,
|
||||||
refetchOnMount: true,
|
refetchOnMount: true,
|
||||||
staleTime: 0,
|
staleTime: 0,
|
||||||
});
|
});
|
||||||
@@ -14,10 +14,7 @@ export interface SlideProps {
|
|||||||
|
|
||||||
interface Props<T> extends SlideProps {
|
interface Props<T> extends SlideProps {
|
||||||
data: T[];
|
data: T[];
|
||||||
renderItem: (
|
renderItem: (item: T, index: number) => React.ReactElement | null;
|
||||||
item: T,
|
|
||||||
index: number,
|
|
||||||
) => React.ComponentType<any> | React.ReactElement | null | undefined;
|
|
||||||
keyExtractor: (item: T) => string;
|
keyExtractor: (item: T) => string;
|
||||||
onEndReached?: (() => void) | null | undefined;
|
onEndReached?: (() => void) | null | undefined;
|
||||||
}
|
}
|
||||||
@@ -47,7 +44,6 @@ const Slide = <T,>({
|
|||||||
data={data}
|
data={data}
|
||||||
onEndReachedThreshold={1}
|
onEndReachedThreshold={1}
|
||||||
onEndReached={onEndReached}
|
onEndReached={onEndReached}
|
||||||
//@ts-expect-error
|
|
||||||
renderItem={({ item, index }) =>
|
renderItem={({ item, index }) =>
|
||||||
item ? renderItem(item, index) : null
|
item ? renderItem(item, index) : null
|
||||||
}
|
}
|
||||||
@@ -14,11 +14,11 @@ import { Alert, TouchableOpacity, View } from "react-native";
|
|||||||
import { HorizontalScroll } from "@/components/common/HorizontalScroll";
|
import { HorizontalScroll } from "@/components/common/HorizontalScroll";
|
||||||
import { Text } from "@/components/common/Text";
|
import { Text } from "@/components/common/Text";
|
||||||
import { Tags } from "@/components/GenreTags";
|
import { Tags } from "@/components/GenreTags";
|
||||||
import { dateOpts } from "@/components/jellyseerr/DetailFacts";
|
|
||||||
import { textShadowStyle } from "@/components/jellyseerr/discover/GenericSlideCard";
|
|
||||||
import JellyseerrStatusIcon from "@/components/jellyseerr/JellyseerrStatusIcon";
|
|
||||||
import { RoundButton } from "@/components/RoundButton";
|
import { RoundButton } from "@/components/RoundButton";
|
||||||
import { useJellyseerr } from "@/hooks/useJellyseerr";
|
import { dateOpts } from "@/components/seerr/DetailFacts";
|
||||||
|
import { textShadowStyle } from "@/components/seerr/discover/GenericSlideCard";
|
||||||
|
import SeerrStatusIcon from "@/components/seerr/SeerrStatusIcon";
|
||||||
|
import { useSeerr } from "@/hooks/useSeerr";
|
||||||
import {
|
import {
|
||||||
MediaStatus,
|
MediaStatus,
|
||||||
MediaType,
|
MediaType,
|
||||||
@@ -30,15 +30,15 @@ import type { MovieDetails } from "@/utils/jellyseerr/server/models/Movie";
|
|||||||
import type { TvDetails } from "@/utils/jellyseerr/server/models/Tv";
|
import type { TvDetails } from "@/utils/jellyseerr/server/models/Tv";
|
||||||
import { Loader } from "../Loader";
|
import { Loader } from "../Loader";
|
||||||
|
|
||||||
const JellyseerrSeasonEpisodes: React.FC<{
|
const SeerrSeasonEpisodes: React.FC<{
|
||||||
details: TvDetails;
|
details: TvDetails;
|
||||||
seasonNumber: number;
|
seasonNumber: number;
|
||||||
}> = ({ details, seasonNumber }) => {
|
}> = ({ details, seasonNumber }) => {
|
||||||
const { jellyseerrApi } = useJellyseerr();
|
const { seerrApi } = useSeerr();
|
||||||
|
|
||||||
const { data: seasonWithEpisodes, isLoading } = useQuery({
|
const { data: seasonWithEpisodes, isLoading } = useQuery({
|
||||||
queryKey: ["jellyseerr", details.id, "season", seasonNumber],
|
queryKey: ["seerr", details.id, "season", seasonNumber],
|
||||||
queryFn: async () => jellyseerrApi?.tvSeason(details.id, seasonNumber),
|
queryFn: async () => seerrApi?.tvSeason(details.id, seasonNumber),
|
||||||
enabled: details.seasons.filter((s) => s.seasonNumber !== 0).length > 0,
|
enabled: details.seasons.filter((s) => s.seasonNumber !== 0).length > 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -57,11 +57,7 @@ const JellyseerrSeasonEpisodes: React.FC<{
|
|||||||
};
|
};
|
||||||
|
|
||||||
const RenderItem = ({ item }: any) => {
|
const RenderItem = ({ item }: any) => {
|
||||||
const {
|
const { seerrApi, seerrRegion: region, seerrLocale: locale } = useSeerr();
|
||||||
jellyseerrApi,
|
|
||||||
jellyseerrRegion: region,
|
|
||||||
jellyseerrLocale: locale,
|
|
||||||
} = useJellyseerr();
|
|
||||||
const [imageError, setImageError] = useState(false);
|
const [imageError, setImageError] = useState(false);
|
||||||
|
|
||||||
const upcomingAirDate = useMemo(() => {
|
const upcomingAirDate = useMemo(() => {
|
||||||
@@ -69,7 +65,7 @@ const RenderItem = ({ item }: any) => {
|
|||||||
if (airDate) {
|
if (airDate) {
|
||||||
const airDateObj = new Date(airDate);
|
const airDateObj = new Date(airDate);
|
||||||
if (new Date() < airDateObj) {
|
if (new Date() < airDateObj) {
|
||||||
return airDateObj.toLocaleDateString(`${locale}-${region}`, dateOpts);
|
return airDateObj.toLocaleDateString(locale, dateOpts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [item, locale, region]);
|
}, [item, locale, region]);
|
||||||
@@ -83,7 +79,7 @@ const RenderItem = ({ item }: any) => {
|
|||||||
key={item.id}
|
key={item.id}
|
||||||
id={item.id}
|
id={item.id}
|
||||||
source={{
|
source={{
|
||||||
uri: jellyseerrApi?.imageProxy(item.stillPath),
|
uri: seerrApi?.imageProxy(item.stillPath),
|
||||||
}}
|
}}
|
||||||
cachePolicy={"memory-disk"}
|
cachePolicy={"memory-disk"}
|
||||||
contentFit='cover'
|
contentFit='cover'
|
||||||
@@ -131,7 +127,7 @@ const RenderItem = ({ item }: any) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const JellyseerrSeasons: React.FC<{
|
const SeerrSeasons: React.FC<{
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
details?: TvDetails;
|
details?: TvDetails;
|
||||||
hasAdvancedRequest?: boolean;
|
hasAdvancedRequest?: boolean;
|
||||||
@@ -148,7 +144,7 @@ const JellyseerrSeasons: React.FC<{
|
|||||||
hasAdvancedRequest,
|
hasAdvancedRequest,
|
||||||
onAdvancedRequest,
|
onAdvancedRequest,
|
||||||
}) => {
|
}) => {
|
||||||
const { jellyseerrApi, requestMedia } = useJellyseerr();
|
const { seerrApi, requestMedia } = useSeerr();
|
||||||
const [seasonStates, setSeasonStates] = useState<{ [key: number]: boolean }>(
|
const [seasonStates, setSeasonStates] = useState<{ [key: number]: boolean }>(
|
||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
@@ -181,7 +177,7 @@ const JellyseerrSeasons: React.FC<{
|
|||||||
);
|
);
|
||||||
|
|
||||||
const requestAll = useCallback(() => {
|
const requestAll = useCallback(() => {
|
||||||
if (details && jellyseerrApi) {
|
if (details && seerrApi) {
|
||||||
const body: MediaRequestBody = {
|
const body: MediaRequestBody = {
|
||||||
mediaId: details.id,
|
mediaId: details.id,
|
||||||
mediaType: MediaType.TV,
|
mediaType: MediaType.TV,
|
||||||
@@ -198,7 +194,7 @@ const JellyseerrSeasons: React.FC<{
|
|||||||
requestMedia(details.name, body, refetch);
|
requestMedia(details.name, body, refetch);
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
jellyseerrApi,
|
seerrApi,
|
||||||
seasons,
|
seasons,
|
||||||
details,
|
details,
|
||||||
hasAdvancedRequest,
|
hasAdvancedRequest,
|
||||||
@@ -210,15 +206,15 @@ const JellyseerrSeasons: React.FC<{
|
|||||||
const promptRequestAll = useCallback(
|
const promptRequestAll = useCallback(
|
||||||
() =>
|
() =>
|
||||||
Alert.alert(
|
Alert.alert(
|
||||||
t("jellyseerr.confirm"),
|
t("seerr.confirm"),
|
||||||
t("jellyseerr.are_you_sure_you_want_to_request_all_seasons"),
|
t("seerr.are_you_sure_you_want_to_request_all_seasons"),
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
text: t("jellyseerr.cancel"),
|
text: t("seerr.cancel"),
|
||||||
style: "cancel",
|
style: "cancel",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: t("jellyseerr.yes"),
|
text: t("seerr.yes"),
|
||||||
onPress: requestAll,
|
onPress: requestAll,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -301,10 +297,10 @@ const JellyseerrSeasons: React.FC<{
|
|||||||
<Tags
|
<Tags
|
||||||
textClass=''
|
textClass=''
|
||||||
tags={[
|
tags={[
|
||||||
t("jellyseerr.season_number", {
|
t("seerr.season_number", {
|
||||||
season_number: season.seasonNumber,
|
season_number: season.seasonNumber,
|
||||||
}),
|
}),
|
||||||
t("jellyseerr.number_episodes", {
|
t("seerr.number_episodes", {
|
||||||
episode_number: season.episodeCount,
|
episode_number: season.episodeCount,
|
||||||
}),
|
}),
|
||||||
]}
|
]}
|
||||||
@@ -312,7 +308,7 @@ const JellyseerrSeasons: React.FC<{
|
|||||||
{[0].map(() => {
|
{[0].map(() => {
|
||||||
const canRequest = season.status === MediaStatus.UNKNOWN;
|
const canRequest = season.status === MediaStatus.UNKNOWN;
|
||||||
return (
|
return (
|
||||||
<JellyseerrStatusIcon
|
<SeerrStatusIcon
|
||||||
key={0}
|
key={0}
|
||||||
onPress={() =>
|
onPress={() =>
|
||||||
requestSeason(canRequest, season.seasonNumber)
|
requestSeason(canRequest, season.seasonNumber)
|
||||||
@@ -326,7 +322,7 @@ const JellyseerrSeasons: React.FC<{
|
|||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
{seasonStates?.[season.seasonNumber] && (
|
{seasonStates?.[season.seasonNumber] && (
|
||||||
<JellyseerrSeasonEpisodes
|
<SeerrSeasonEpisodes
|
||||||
key={season.seasonNumber}
|
key={season.seasonNumber}
|
||||||
details={details}
|
details={details}
|
||||||
seasonNumber={season.seasonNumber}
|
seasonNumber={season.seasonNumber}
|
||||||
@@ -338,4 +334,4 @@ const JellyseerrSeasons: React.FC<{
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default JellyseerrSeasons;
|
export default SeerrSeasons;
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { View } from "react-native";
|
|
||||||
import useRouter from "@/hooks/useAppRouter";
|
|
||||||
import { useSessions, type useSessionsProps } from "@/hooks/useSessions";
|
|
||||||
import { useSettings } from "@/utils/atoms/settings";
|
|
||||||
import { ListGroup } from "../list/ListGroup";
|
|
||||||
import { ListItem } from "../list/ListItem";
|
|
||||||
|
|
||||||
export const Dashboard = () => {
|
|
||||||
const { settings } = useSettings();
|
|
||||||
const { sessions = [] } = useSessions({} as useSessionsProps);
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
if (!settings) return null;
|
|
||||||
return (
|
|
||||||
<View>
|
|
||||||
<ListGroup title={t("home.settings.dashboard.title")} className='mt-4'>
|
|
||||||
<ListItem
|
|
||||||
className={sessions.length !== 0 ? "bg-purple-900" : ""}
|
|
||||||
onPress={() => router.push("/settings/dashboard/sessions")}
|
|
||||||
title={t("home.settings.dashboard.sessions_title")}
|
|
||||||
showArrow
|
|
||||||
/>
|
|
||||||
</ListGroup>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export default function DownloadSettings() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export default function DownloadSettings() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
@@ -1,181 +0,0 @@
|
|||||||
import { useMutation } from "@tanstack/react-query";
|
|
||||||
import { useAtom } from "jotai";
|
|
||||||
import { useState } from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { View } from "react-native";
|
|
||||||
import { toast } from "sonner-native";
|
|
||||||
import { JellyseerrApi, useJellyseerr } from "@/hooks/useJellyseerr";
|
|
||||||
import { userAtom } from "@/providers/JellyfinProvider";
|
|
||||||
import { useSettings } from "@/utils/atoms/settings";
|
|
||||||
import { Button } from "../Button";
|
|
||||||
import { Input } from "../common/Input";
|
|
||||||
import { Text } from "../common/Text";
|
|
||||||
import { ListGroup } from "../list/ListGroup";
|
|
||||||
import { ListItem } from "../list/ListItem";
|
|
||||||
|
|
||||||
export const JellyseerrSettings = () => {
|
|
||||||
const { jellyseerrUser, setJellyseerrUser, clearAllJellyseerData } =
|
|
||||||
useJellyseerr();
|
|
||||||
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const [user] = useAtom(userAtom);
|
|
||||||
const { settings, updateSettings } = useSettings();
|
|
||||||
|
|
||||||
const [jellyseerrPassword, setJellyseerrPassword] = useState<
|
|
||||||
string | undefined
|
|
||||||
>(undefined);
|
|
||||||
|
|
||||||
const [jellyseerrServerUrl, setjellyseerrServerUrl] = useState<
|
|
||||||
string | undefined
|
|
||||||
>(settings?.jellyseerrServerUrl || undefined);
|
|
||||||
|
|
||||||
const loginToJellyseerrMutation = useMutation({
|
|
||||||
mutationFn: async () => {
|
|
||||||
if (!jellyseerrServerUrl && !settings?.jellyseerrServerUrl)
|
|
||||||
throw new Error("Missing server url");
|
|
||||||
if (!user?.Name)
|
|
||||||
throw new Error("Missing required information for login");
|
|
||||||
const jellyseerrTempApi = new JellyseerrApi(
|
|
||||||
jellyseerrServerUrl || settings.jellyseerrServerUrl || "",
|
|
||||||
);
|
|
||||||
const testResult = await jellyseerrTempApi.test();
|
|
||||||
if (!testResult.isValid) throw new Error("Invalid server url");
|
|
||||||
return jellyseerrTempApi.login(user.Name, jellyseerrPassword || "");
|
|
||||||
},
|
|
||||||
onSuccess: (user) => {
|
|
||||||
setJellyseerrUser(user);
|
|
||||||
updateSettings({ jellyseerrServerUrl });
|
|
||||||
},
|
|
||||||
onError: () => {
|
|
||||||
toast.error(t("jellyseerr.failed_to_login"));
|
|
||||||
},
|
|
||||||
onSettled: () => {
|
|
||||||
setJellyseerrPassword(undefined);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const clearData = () => {
|
|
||||||
clearAllJellyseerData().finally(() => {
|
|
||||||
setJellyseerrUser(undefined);
|
|
||||||
setJellyseerrPassword(undefined);
|
|
||||||
setjellyseerrServerUrl(undefined);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View className=''>
|
|
||||||
<View>
|
|
||||||
{jellyseerrUser ? (
|
|
||||||
<>
|
|
||||||
<ListGroup title={"Jellyseerr"}>
|
|
||||||
<ListItem
|
|
||||||
title={t(
|
|
||||||
"home.settings.plugins.jellyseerr.total_media_requests",
|
|
||||||
)}
|
|
||||||
value={jellyseerrUser?.requestCount?.toString()}
|
|
||||||
/>
|
|
||||||
<ListItem
|
|
||||||
title={t("home.settings.plugins.jellyseerr.movie_quota_limit")}
|
|
||||||
value={
|
|
||||||
jellyseerrUser?.movieQuotaLimit?.toString() ??
|
|
||||||
t("home.settings.plugins.jellyseerr.unlimited")
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<ListItem
|
|
||||||
title={t("home.settings.plugins.jellyseerr.movie_quota_days")}
|
|
||||||
value={
|
|
||||||
jellyseerrUser?.movieQuotaDays?.toString() ??
|
|
||||||
t("home.settings.plugins.jellyseerr.unlimited")
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<ListItem
|
|
||||||
title={t("home.settings.plugins.jellyseerr.tv_quota_limit")}
|
|
||||||
value={
|
|
||||||
jellyseerrUser?.tvQuotaLimit?.toString() ??
|
|
||||||
t("home.settings.plugins.jellyseerr.unlimited")
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<ListItem
|
|
||||||
title={t("home.settings.plugins.jellyseerr.tv_quota_days")}
|
|
||||||
value={
|
|
||||||
jellyseerrUser?.tvQuotaDays?.toString() ??
|
|
||||||
t("home.settings.plugins.jellyseerr.unlimited")
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</ListGroup>
|
|
||||||
|
|
||||||
<View className='p-4'>
|
|
||||||
<Button color='red' onPress={clearData}>
|
|
||||||
{t(
|
|
||||||
"home.settings.plugins.jellyseerr.reset_jellyseerr_config_button",
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</View>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<View className='flex flex-col rounded-xl overflow-hidden p-4 bg-neutral-900'>
|
|
||||||
<Text className='text-xs text-red-600 mb-2'>
|
|
||||||
{t("home.settings.plugins.jellyseerr.jellyseerr_warning")}
|
|
||||||
</Text>
|
|
||||||
<Text className='font-bold mb-1'>
|
|
||||||
{t("home.settings.plugins.jellyseerr.server_url")}
|
|
||||||
</Text>
|
|
||||||
<View className='flex flex-col shrink mb-2'>
|
|
||||||
<Text className='text-xs text-gray-600'>
|
|
||||||
{t("home.settings.plugins.jellyseerr.server_url_hint")}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<Input
|
|
||||||
className='border border-neutral-800 mb-2'
|
|
||||||
placeholder={t(
|
|
||||||
"home.settings.plugins.jellyseerr.server_url_placeholder",
|
|
||||||
)}
|
|
||||||
value={jellyseerrServerUrl ?? settings?.jellyseerrServerUrl}
|
|
||||||
defaultValue={
|
|
||||||
settings?.jellyseerrServerUrl ?? jellyseerrServerUrl
|
|
||||||
}
|
|
||||||
keyboardType='url'
|
|
||||||
returnKeyType='done'
|
|
||||||
autoCapitalize='none'
|
|
||||||
textContentType='URL'
|
|
||||||
onChangeText={setjellyseerrServerUrl}
|
|
||||||
editable={!loginToJellyseerrMutation.isPending}
|
|
||||||
/>
|
|
||||||
<View>
|
|
||||||
<Text className='font-bold mb-2'>
|
|
||||||
{t("home.settings.plugins.jellyseerr.password")}
|
|
||||||
</Text>
|
|
||||||
<Input
|
|
||||||
className='border border-neutral-800'
|
|
||||||
autoFocus={true}
|
|
||||||
focusable={true}
|
|
||||||
placeholder={t(
|
|
||||||
"home.settings.plugins.jellyseerr.password_placeholder",
|
|
||||||
{ username: user?.Name },
|
|
||||||
)}
|
|
||||||
value={jellyseerrPassword}
|
|
||||||
keyboardType='default'
|
|
||||||
secureTextEntry={true}
|
|
||||||
returnKeyType='done'
|
|
||||||
autoCapitalize='none'
|
|
||||||
textContentType='password'
|
|
||||||
onChangeText={setJellyseerrPassword}
|
|
||||||
editable={!loginToJellyseerrMutation.isPending}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
loading={loginToJellyseerrMutation.isPending}
|
|
||||||
disabled={loginToJellyseerrMutation.isPending}
|
|
||||||
color='purple'
|
|
||||||
className='h-12 mt-2'
|
|
||||||
onPress={() => loginToJellyseerrMutation.mutate()}
|
|
||||||
>
|
|
||||||
{t("home.settings.plugins.jellyseerr.login_button")}
|
|
||||||
</Button>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -229,7 +229,7 @@ export const LibraryOptionsSheet: React.FC<Props> = ({
|
|||||||
/>
|
/>
|
||||||
</OptionGroup>
|
</OptionGroup>
|
||||||
|
|
||||||
<OptionGroup title='Options'>
|
<OptionGroup title={t("library.options.options_title")}>
|
||||||
<ToggleItem
|
<ToggleItem
|
||||||
label={t("library.options.show_titles")}
|
label={t("library.options.show_titles")}
|
||||||
value={settings.showTitles}
|
value={settings.showTitles}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { Platform, View, type ViewProps } from "react-native";
|
import { Platform, View, type ViewProps } from "react-native";
|
||||||
import { Stepper } from "@/components/inputs/Stepper";
|
import { Stepper } from "@/components/inputs/Stepper";
|
||||||
import { Text } from "../common/Text";
|
import { Text } from "../common/Text";
|
||||||
@@ -17,20 +18,21 @@ export const MpvSubtitleSettings: React.FC<Props> = ({ ...props }) => {
|
|||||||
const isTv = Platform.isTV;
|
const isTv = Platform.isTV;
|
||||||
const media = useMedia();
|
const media = useMedia();
|
||||||
const { settings, updateSettings } = media;
|
const { settings, updateSettings } = media;
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const alignXOptions: AlignX[] = ["left", "center", "right"];
|
const alignXOptions: AlignX[] = ["left", "center", "right"];
|
||||||
const alignYOptions: AlignY[] = ["top", "center", "bottom"];
|
const alignYOptions: AlignY[] = ["top", "center", "bottom"];
|
||||||
|
|
||||||
const alignXLabels: Record<AlignX, string> = {
|
const alignXLabels: Record<AlignX, string> = {
|
||||||
left: "Left",
|
left: t("player.alignment_left"),
|
||||||
center: "Center",
|
center: t("player.alignment_center"),
|
||||||
right: "Right",
|
right: t("player.alignment_right"),
|
||||||
};
|
};
|
||||||
|
|
||||||
const alignYLabels: Record<AlignY, string> = {
|
const alignYLabels: Record<AlignY, string> = {
|
||||||
top: "Top",
|
top: t("player.alignment_top"),
|
||||||
center: "Center",
|
center: t("player.alignment_center"),
|
||||||
bottom: "Bottom",
|
bottom: t("player.alignment_bottom"),
|
||||||
};
|
};
|
||||||
|
|
||||||
const alignXOptionGroups = useMemo(() => {
|
const alignXOptionGroups = useMemo(() => {
|
||||||
@@ -61,14 +63,14 @@ export const MpvSubtitleSettings: React.FC<Props> = ({ ...props }) => {
|
|||||||
return (
|
return (
|
||||||
<View {...props}>
|
<View {...props}>
|
||||||
<ListGroup
|
<ListGroup
|
||||||
title='MPV Subtitle Settings'
|
title={t("player.mpv_subtitle_settings_title")}
|
||||||
description={
|
description={
|
||||||
<Text className='text-[#8E8D91] text-xs'>
|
<Text className='text-[#8E8D91] text-xs'>
|
||||||
Advanced subtitle customization for MPV player
|
{t("player.mpv_subtitle_settings_description")}
|
||||||
</Text>
|
</Text>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<ListItem title='Subtitle Scale'>
|
<ListItem title={t("player.subtitle_scale")}>
|
||||||
<Stepper
|
<Stepper
|
||||||
value={settings.mpvSubtitleScale ?? 1.0}
|
value={settings.mpvSubtitleScale ?? 1.0}
|
||||||
step={0.1}
|
step={0.1}
|
||||||
@@ -80,7 +82,7 @@ export const MpvSubtitleSettings: React.FC<Props> = ({ ...props }) => {
|
|||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
<ListItem title='Vertical Margin'>
|
<ListItem title={t("player.vertical_margin")}>
|
||||||
<Stepper
|
<Stepper
|
||||||
value={settings.mpvSubtitleMarginY ?? 0}
|
value={settings.mpvSubtitleMarginY ?? 0}
|
||||||
step={5}
|
step={5}
|
||||||
@@ -90,7 +92,7 @@ export const MpvSubtitleSettings: React.FC<Props> = ({ ...props }) => {
|
|||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
<ListItem title='Horizontal Alignment'>
|
<ListItem title={t("player.horizontal_alignment")}>
|
||||||
<PlatformDropdown
|
<PlatformDropdown
|
||||||
groups={alignXOptionGroups}
|
groups={alignXOptionGroups}
|
||||||
trigger={
|
trigger={
|
||||||
@@ -105,11 +107,11 @@ export const MpvSubtitleSettings: React.FC<Props> = ({ ...props }) => {
|
|||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
}
|
}
|
||||||
title='Horizontal Alignment'
|
title={t("player.horizontal_alignment")}
|
||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
|
|
||||||
<ListItem title='Vertical Alignment'>
|
<ListItem title={t("player.vertical_alignment")}>
|
||||||
<PlatformDropdown
|
<PlatformDropdown
|
||||||
groups={alignYOptionGroups}
|
groups={alignYOptionGroups}
|
||||||
trigger={
|
trigger={
|
||||||
@@ -124,7 +126,7 @@ export const MpvSubtitleSettings: React.FC<Props> = ({ ...props }) => {
|
|||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
}
|
}
|
||||||
title='Vertical Alignment'
|
title={t("player.vertical_alignment")}
|
||||||
/>
|
/>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</ListGroup>
|
</ListGroup>
|
||||||
|
|||||||
@@ -19,23 +19,23 @@ export const PluginSettings = () => {
|
|||||||
className='mb-4'
|
className='mb-4'
|
||||||
>
|
>
|
||||||
<ListItem
|
<ListItem
|
||||||
onPress={() => router.push("/settings/plugins/jellyseerr/page")}
|
onPress={() => router.push("/settings/plugins/seerr/page")}
|
||||||
title={"Jellyseerr"}
|
title={"Seerr"}
|
||||||
showArrow
|
|
||||||
/>
|
|
||||||
<ListItem
|
|
||||||
onPress={() => router.push("/settings/plugins/marlin-search/page")}
|
|
||||||
title='Marlin Search'
|
|
||||||
showArrow
|
showArrow
|
||||||
/>
|
/>
|
||||||
<ListItem
|
<ListItem
|
||||||
onPress={() => router.push("/settings/plugins/streamystats/page")}
|
onPress={() => router.push("/settings/plugins/streamystats/page")}
|
||||||
title='Streamystats'
|
title={"Streamystats"}
|
||||||
|
showArrow
|
||||||
|
/>
|
||||||
|
<ListItem
|
||||||
|
onPress={() => router.push("/settings/plugins/marlin-search/page")}
|
||||||
|
title={"Marlin Search"}
|
||||||
showArrow
|
showArrow
|
||||||
/>
|
/>
|
||||||
<ListItem
|
<ListItem
|
||||||
onPress={() => router.push("/settings/plugins/kefinTweaks/page")}
|
onPress={() => router.push("/settings/plugins/kefinTweaks/page")}
|
||||||
title='KefinTweaks'
|
title={"KefinTweaks"}
|
||||||
showArrow
|
showArrow
|
||||||
/>
|
/>
|
||||||
</ListGroup>
|
</ListGroup>
|
||||||
|
|||||||
174
components/settings/Seerr.tsx
Normal file
174
components/settings/Seerr.tsx
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
import { useMutation } from "@tanstack/react-query";
|
||||||
|
import { useAtom } from "jotai";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { View } from "react-native";
|
||||||
|
import { toast } from "sonner-native";
|
||||||
|
import { SeerrApi, useSeerr } from "@/hooks/useSeerr";
|
||||||
|
import { userAtom } from "@/providers/JellyfinProvider";
|
||||||
|
import { useSettings } from "@/utils/atoms/settings";
|
||||||
|
import { Button } from "../Button";
|
||||||
|
import { Input } from "../common/Input";
|
||||||
|
import { Text } from "../common/Text";
|
||||||
|
import { ListGroup } from "../list/ListGroup";
|
||||||
|
import { ListItem } from "../list/ListItem";
|
||||||
|
|
||||||
|
export const SeerrSettings = () => {
|
||||||
|
const { seerrUser, setSeerrUser, clearAllSeerrData } = useSeerr();
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const [user] = useAtom(userAtom);
|
||||||
|
const { settings, updateSettings } = useSettings();
|
||||||
|
|
||||||
|
const [seerrPassword, setSeerrPassword] = useState<string | undefined>(
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
|
||||||
|
const [seerrServerUrl, setSeerrServerUrl] = useState<string | undefined>(
|
||||||
|
settings?.seerrServerUrl || undefined,
|
||||||
|
);
|
||||||
|
|
||||||
|
const loginToSeerrMutation = useMutation({
|
||||||
|
mutationFn: async () => {
|
||||||
|
if (!seerrServerUrl && !settings?.seerrServerUrl)
|
||||||
|
throw new Error("Missing server url");
|
||||||
|
if (!user?.Name)
|
||||||
|
throw new Error("Missing required information for login");
|
||||||
|
const seerrTempApi = new SeerrApi(
|
||||||
|
seerrServerUrl || settings.seerrServerUrl || "",
|
||||||
|
);
|
||||||
|
const testResult = await seerrTempApi.test();
|
||||||
|
if (!testResult.isValid) throw new Error("Invalid server url");
|
||||||
|
return seerrTempApi.login(user.Name, seerrPassword || "");
|
||||||
|
},
|
||||||
|
onSuccess: (user) => {
|
||||||
|
setSeerrUser(user);
|
||||||
|
updateSettings({ seerrServerUrl });
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
toast.error(t("seerr.failed_to_login"));
|
||||||
|
},
|
||||||
|
onSettled: () => {
|
||||||
|
setSeerrPassword(undefined);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const clearData = () => {
|
||||||
|
clearAllSeerrData().finally(() => {
|
||||||
|
setSeerrUser(undefined);
|
||||||
|
setSeerrPassword(undefined);
|
||||||
|
setSeerrServerUrl(undefined);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className=''>
|
||||||
|
<View>
|
||||||
|
{seerrUser ? (
|
||||||
|
<>
|
||||||
|
<ListGroup title={"Seerr"}>
|
||||||
|
<ListItem
|
||||||
|
title={t("home.settings.plugins.seerr.total_media_requests")}
|
||||||
|
value={seerrUser?.requestCount?.toString()}
|
||||||
|
/>
|
||||||
|
<ListItem
|
||||||
|
title={t("home.settings.plugins.seerr.movie_quota_limit")}
|
||||||
|
value={
|
||||||
|
seerrUser?.movieQuotaLimit?.toString() ??
|
||||||
|
t("home.settings.plugins.seerr.unlimited")
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<ListItem
|
||||||
|
title={t("home.settings.plugins.seerr.movie_quota_days")}
|
||||||
|
value={
|
||||||
|
seerrUser?.movieQuotaDays?.toString() ??
|
||||||
|
t("home.settings.plugins.seerr.unlimited")
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<ListItem
|
||||||
|
title={t("home.settings.plugins.seerr.tv_quota_limit")}
|
||||||
|
value={
|
||||||
|
seerrUser?.tvQuotaLimit?.toString() ??
|
||||||
|
t("home.settings.plugins.seerr.unlimited")
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<ListItem
|
||||||
|
title={t("home.settings.plugins.seerr.tv_quota_days")}
|
||||||
|
value={
|
||||||
|
seerrUser?.tvQuotaDays?.toString() ??
|
||||||
|
t("home.settings.plugins.seerr.unlimited")
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</ListGroup>
|
||||||
|
|
||||||
|
<View className='p-4'>
|
||||||
|
<Button color='red' onPress={clearData}>
|
||||||
|
{t("home.settings.plugins.seerr.reset_seerr_config_button")}
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<View className='flex flex-col rounded-xl overflow-hidden p-4 bg-neutral-900'>
|
||||||
|
<Text className='text-xs text-red-600 mb-2'>
|
||||||
|
{t("home.settings.plugins.seerr.seerr_warning")}
|
||||||
|
</Text>
|
||||||
|
<Text className='font-bold mb-1'>
|
||||||
|
{t("home.settings.plugins.seerr.server_url")}
|
||||||
|
</Text>
|
||||||
|
<View className='flex flex-col shrink mb-2'>
|
||||||
|
<Text className='text-xs text-gray-600'>
|
||||||
|
{t("home.settings.plugins.seerr.server_url_hint")}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<Input
|
||||||
|
className='border border-neutral-800 mb-2'
|
||||||
|
placeholder={t(
|
||||||
|
"home.settings.plugins.seerr.server_url_placeholder",
|
||||||
|
)}
|
||||||
|
value={seerrServerUrl ?? settings?.seerrServerUrl}
|
||||||
|
defaultValue={settings?.seerrServerUrl ?? seerrServerUrl}
|
||||||
|
keyboardType='url'
|
||||||
|
returnKeyType='done'
|
||||||
|
autoCapitalize='none'
|
||||||
|
textContentType='URL'
|
||||||
|
onChangeText={setSeerrServerUrl}
|
||||||
|
editable={!loginToSeerrMutation.isPending}
|
||||||
|
/>
|
||||||
|
<View>
|
||||||
|
<Text className='font-bold mb-2'>
|
||||||
|
{t("home.settings.plugins.seerr.password")}
|
||||||
|
</Text>
|
||||||
|
<Input
|
||||||
|
className='border border-neutral-800'
|
||||||
|
autoFocus={true}
|
||||||
|
focusable={true}
|
||||||
|
placeholder={t(
|
||||||
|
"home.settings.plugins.seerr.password_placeholder",
|
||||||
|
{ username: user?.Name },
|
||||||
|
)}
|
||||||
|
value={seerrPassword}
|
||||||
|
keyboardType='default'
|
||||||
|
secureTextEntry={true}
|
||||||
|
returnKeyType='done'
|
||||||
|
autoCapitalize='none'
|
||||||
|
textContentType='password'
|
||||||
|
onChangeText={setSeerrPassword}
|
||||||
|
editable={!loginToSeerrMutation.isPending}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
loading={loginToSeerrMutation.isPending}
|
||||||
|
disabled={loginToSeerrMutation.isPending}
|
||||||
|
color='purple'
|
||||||
|
className='h-12 mt-2'
|
||||||
|
onPress={() => loginToSeerrMutation.mutate()}
|
||||||
|
>
|
||||||
|
{t("home.settings.plugins.seerr.login_button")}
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -40,7 +40,6 @@ import { useVideoTime } from "./hooks/useVideoTime";
|
|||||||
import { TechnicalInfoOverlay } from "./TechnicalInfoOverlay";
|
import { TechnicalInfoOverlay } from "./TechnicalInfoOverlay";
|
||||||
import { useControlsTimeout } from "./useControlsTimeout";
|
import { useControlsTimeout } from "./useControlsTimeout";
|
||||||
import { PlaybackSpeedScope } from "./utils/playback-speed-settings";
|
import { PlaybackSpeedScope } from "./utils/playback-speed-settings";
|
||||||
import { type AspectRatio } from "./VideoScalingModeSelector";
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
item: BaseItemDto;
|
item: BaseItemDto;
|
||||||
@@ -58,7 +57,6 @@ interface Props {
|
|||||||
startPictureInPicture?: () => Promise<void>;
|
startPictureInPicture?: () => Promise<void>;
|
||||||
play: () => void;
|
play: () => void;
|
||||||
pause: () => void;
|
pause: () => void;
|
||||||
aspectRatio?: AspectRatio;
|
|
||||||
isZoomedToFill?: boolean;
|
isZoomedToFill?: boolean;
|
||||||
onZoomToggle?: () => void;
|
onZoomToggle?: () => void;
|
||||||
api?: Api | null;
|
api?: Api | null;
|
||||||
@@ -89,7 +87,6 @@ export const Controls: FC<Props> = ({
|
|||||||
showControls,
|
showControls,
|
||||||
setShowControls,
|
setShowControls,
|
||||||
mediaSource,
|
mediaSource,
|
||||||
aspectRatio = "default",
|
|
||||||
isZoomedToFill = false,
|
isZoomedToFill = false,
|
||||||
onZoomToggle,
|
onZoomToggle,
|
||||||
api = null,
|
api = null,
|
||||||
@@ -498,7 +495,6 @@ export const Controls: FC<Props> = ({
|
|||||||
goToNextItem={goToNextItem}
|
goToNextItem={goToNextItem}
|
||||||
previousItem={previousItem}
|
previousItem={previousItem}
|
||||||
nextItem={nextItem}
|
nextItem={nextItem}
|
||||||
aspectRatio={aspectRatio}
|
|
||||||
isZoomedToFill={isZoomedToFill}
|
isZoomedToFill={isZoomedToFill}
|
||||||
onZoomToggle={onZoomToggle}
|
onZoomToggle={onZoomToggle}
|
||||||
playbackSpeed={playbackSpeed}
|
playbackSpeed={playbackSpeed}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import { useSettings } from "@/utils/atoms/settings";
|
|||||||
import { HEADER_LAYOUT, ICON_SIZES } from "./constants";
|
import { HEADER_LAYOUT, ICON_SIZES } from "./constants";
|
||||||
import DropdownView from "./dropdown/DropdownView";
|
import DropdownView from "./dropdown/DropdownView";
|
||||||
import { PlaybackSpeedScope } from "./utils/playback-speed-settings";
|
import { PlaybackSpeedScope } from "./utils/playback-speed-settings";
|
||||||
import { type AspectRatio } from "./VideoScalingModeSelector";
|
|
||||||
import { ZoomToggle } from "./ZoomToggle";
|
import { ZoomToggle } from "./ZoomToggle";
|
||||||
|
|
||||||
interface HeaderControlsProps {
|
interface HeaderControlsProps {
|
||||||
@@ -28,7 +27,6 @@ interface HeaderControlsProps {
|
|||||||
goToNextItem: (options: { isAutoPlay?: boolean }) => void;
|
goToNextItem: (options: { isAutoPlay?: boolean }) => void;
|
||||||
previousItem?: BaseItemDto | null;
|
previousItem?: BaseItemDto | null;
|
||||||
nextItem?: BaseItemDto | null;
|
nextItem?: BaseItemDto | null;
|
||||||
aspectRatio?: AspectRatio;
|
|
||||||
isZoomedToFill?: boolean;
|
isZoomedToFill?: boolean;
|
||||||
onZoomToggle?: () => void;
|
onZoomToggle?: () => void;
|
||||||
// Playback speed props
|
// Playback speed props
|
||||||
@@ -50,7 +48,6 @@ export const HeaderControls: FC<HeaderControlsProps> = ({
|
|||||||
goToNextItem,
|
goToNextItem,
|
||||||
previousItem,
|
previousItem,
|
||||||
nextItem,
|
nextItem,
|
||||||
aspectRatio: _aspectRatio = "default",
|
|
||||||
isZoomedToFill = false,
|
isZoomedToFill = false,
|
||||||
onZoomToggle,
|
onZoomToggle,
|
||||||
playbackSpeed = 1.0,
|
playbackSpeed = 1.0,
|
||||||
|
|||||||
@@ -1,105 +0,0 @@
|
|||||||
import { Ionicons } from "@expo/vector-icons";
|
|
||||||
import React, { useMemo } from "react";
|
|
||||||
import { Platform, View } from "react-native";
|
|
||||||
import {
|
|
||||||
type OptionGroup,
|
|
||||||
PlatformDropdown,
|
|
||||||
} from "@/components/PlatformDropdown";
|
|
||||||
import { useHaptic } from "@/hooks/useHaptic";
|
|
||||||
|
|
||||||
export type AspectRatio = "default" | "16:9" | "4:3" | "1:1" | "21:9";
|
|
||||||
|
|
||||||
interface AspectRatioSelectorProps {
|
|
||||||
currentRatio: AspectRatio;
|
|
||||||
onRatioChange: (ratio: AspectRatio) => void;
|
|
||||||
disabled?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AspectRatioOption {
|
|
||||||
id: AspectRatio;
|
|
||||||
label: string;
|
|
||||||
description: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ASPECT_RATIO_OPTIONS: AspectRatioOption[] = [
|
|
||||||
{
|
|
||||||
id: "default",
|
|
||||||
label: "Original",
|
|
||||||
description: "Use video's original aspect ratio",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "16:9",
|
|
||||||
label: "16:9",
|
|
||||||
description: "Widescreen (most common)",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "4:3",
|
|
||||||
label: "4:3",
|
|
||||||
description: "Traditional TV format",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "1:1",
|
|
||||||
label: "1:1",
|
|
||||||
description: "Square format",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "21:9",
|
|
||||||
label: "21:9",
|
|
||||||
description: "Ultra-wide cinematic",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const AspectRatioSelector: React.FC<AspectRatioSelectorProps> = ({
|
|
||||||
currentRatio,
|
|
||||||
onRatioChange,
|
|
||||||
disabled = false,
|
|
||||||
}) => {
|
|
||||||
const lightHapticFeedback = useHaptic("light");
|
|
||||||
|
|
||||||
const handleRatioSelect = (ratio: AspectRatio) => {
|
|
||||||
onRatioChange(ratio);
|
|
||||||
lightHapticFeedback();
|
|
||||||
};
|
|
||||||
|
|
||||||
const optionGroups = useMemo<OptionGroup[]>(() => {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
options: ASPECT_RATIO_OPTIONS.map((option) => ({
|
|
||||||
type: "radio" as const,
|
|
||||||
label: option.label,
|
|
||||||
value: option.id,
|
|
||||||
selected: option.id === currentRatio,
|
|
||||||
onPress: () => handleRatioSelect(option.id),
|
|
||||||
disabled,
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [currentRatio, disabled]);
|
|
||||||
|
|
||||||
const trigger = useMemo(
|
|
||||||
() => (
|
|
||||||
<View
|
|
||||||
className='aspect-square flex flex-col rounded-xl items-center justify-center p-2'
|
|
||||||
style={{ opacity: disabled ? 0.5 : 1 }}
|
|
||||||
>
|
|
||||||
<Ionicons name='crop-outline' size={24} color='white' />
|
|
||||||
</View>
|
|
||||||
),
|
|
||||||
[disabled],
|
|
||||||
);
|
|
||||||
|
|
||||||
// Hide on TV platforms
|
|
||||||
if (Platform.isTV) return null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<PlatformDropdown
|
|
||||||
title='Aspect Ratio'
|
|
||||||
groups={optionGroups}
|
|
||||||
trigger={trigger}
|
|
||||||
bottomSheetConfig={{
|
|
||||||
enablePanDownToClose: true,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import { useLocalSearchParams } from "expo-router";
|
import { useLocalSearchParams } from "expo-router";
|
||||||
import { useCallback, useMemo, useRef } from "react";
|
import { useCallback, useMemo, useRef } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { Platform, View } from "react-native";
|
import { Platform, View } from "react-native";
|
||||||
import { BITRATES } from "@/components/BitrateSelector";
|
import { BITRATES } from "@/components/BitrateSelector";
|
||||||
import {
|
import {
|
||||||
@@ -45,6 +46,7 @@ const DropdownView = ({
|
|||||||
const { settings, updateSettings } = useSettings();
|
const { settings, updateSettings } = useSettings();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const isOffline = useOfflineMode();
|
const isOffline = useOfflineMode();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const { subtitleIndex, audioIndex, bitrateValue, playbackPosition } =
|
const { subtitleIndex, audioIndex, bitrateValue, playbackPosition } =
|
||||||
useLocalSearchParams<{
|
useLocalSearchParams<{
|
||||||
@@ -215,7 +217,7 @@ const DropdownView = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PlatformDropdown
|
<PlatformDropdown
|
||||||
title='Playback Options'
|
title={t("player.playback_options_title")}
|
||||||
groups={optionGroups}
|
groups={optionGroups}
|
||||||
trigger={trigger}
|
trigger={trigger}
|
||||||
expoUIConfig={{}}
|
expoUIConfig={{}}
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
import type { DefaultLanguageOption } from "@/utils/atoms/settings";
|
|
||||||
|
|
||||||
export const LANGUAGES: DefaultLanguageOption[] = [
|
|
||||||
{ label: "English", value: "eng" },
|
|
||||||
{ label: "Spanish", value: "spa" },
|
|
||||||
{ label: "Chinese (Mandarin)", value: "cmn" },
|
|
||||||
{ label: "Hindi", value: "hin" },
|
|
||||||
{ label: "Arabic", value: "ara" },
|
|
||||||
{ label: "French", value: "fra" },
|
|
||||||
{ label: "Russian", value: "rus" },
|
|
||||||
{ label: "Portuguese", value: "por" },
|
|
||||||
{ label: "Japanese", value: "jpn" },
|
|
||||||
{ label: "German", value: "deu" },
|
|
||||||
{ label: "Italian", value: "ita" },
|
|
||||||
{ label: "Korean", value: "kor" },
|
|
||||||
{ label: "Turkish", value: "tur" },
|
|
||||||
{ label: "Dutch", value: "nld" },
|
|
||||||
{ label: "Polish", value: "pol" },
|
|
||||||
{ label: "Vietnamese", value: "vie" },
|
|
||||||
{ label: "Thai", value: "tha" },
|
|
||||||
{ label: "Indonesian", value: "ind" },
|
|
||||||
{ label: "Greek", value: "ell" },
|
|
||||||
{ label: "Swedish", value: "swe" },
|
|
||||||
{ label: "Danish", value: "dan" },
|
|
||||||
{ label: "Norwegian", value: "nor" },
|
|
||||||
{ label: "Finnish", value: "fin" },
|
|
||||||
{ label: "Czech", value: "ces" },
|
|
||||||
{ label: "Hungarian", value: "hun" },
|
|
||||||
{ label: "Romanian", value: "ron" },
|
|
||||||
{ label: "Ukrainian", value: "ukr" },
|
|
||||||
{ label: "Hebrew", value: "heb" },
|
|
||||||
{ label: "Bengali", value: "ben" },
|
|
||||||
{ label: "Punjabi", value: "pan" },
|
|
||||||
{ label: "Tagalog", value: "tgl" },
|
|
||||||
{ label: "Swahili", value: "swa" },
|
|
||||||
{ label: "Malay", value: "msa" },
|
|
||||||
{ label: "Persian", value: "fas" },
|
|
||||||
{ label: "Urdu", value: "urd" },
|
|
||||||
];
|
|
||||||
@@ -1,6 +1,2 @@
|
|||||||
import { Platform } from "react-native";
|
|
||||||
|
|
||||||
export const TAB_HEIGHT = Platform.OS === "android" ? 58 : 74;
|
|
||||||
|
|
||||||
// Matches `w-28` poster cards (approx 112px wide, 10/15 aspect ratio) + 2 lines of text.
|
// Matches `w-28` poster cards (approx 112px wide, 10/15 aspect ratio) + 2 lines of text.
|
||||||
export const POSTER_CAROUSEL_HEIGHT = 220;
|
export const POSTER_CAROUSEL_HEIGHT = 220;
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
import { useCallback, useEffect, useRef } from "react";
|
|
||||||
import { useSharedValue } from "react-native-reanimated";
|
|
||||||
|
|
||||||
export const useControlsVisibility = (timeout = 3000) => {
|
|
||||||
const opacity = useSharedValue(1);
|
|
||||||
|
|
||||||
const hideControlsTimerRef = useRef<ReturnType<typeof setTimeout> | null>(
|
|
||||||
null,
|
|
||||||
);
|
|
||||||
|
|
||||||
const showControls = useCallback(() => {
|
|
||||||
opacity.value = 1;
|
|
||||||
if (hideControlsTimerRef.current) {
|
|
||||||
clearTimeout(hideControlsTimerRef.current);
|
|
||||||
}
|
|
||||||
hideControlsTimerRef.current = setTimeout(() => {
|
|
||||||
opacity.value = 0;
|
|
||||||
}, timeout);
|
|
||||||
}, [timeout]);
|
|
||||||
|
|
||||||
const hideControls = useCallback(() => {
|
|
||||||
opacity.value = 0;
|
|
||||||
if (hideControlsTimerRef.current) {
|
|
||||||
clearTimeout(hideControlsTimerRef.current);
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
return () => {
|
|
||||||
if (hideControlsTimerRef.current) {
|
|
||||||
clearTimeout(hideControlsTimerRef.current);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return { opacity, showControls, hideControls };
|
|
||||||
};
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
|
|
||||||
import { useCallback } from "react";
|
|
||||||
import useRouter from "@/hooks/useAppRouter";
|
|
||||||
import { usePlaySettings } from "@/providers/PlaySettingsProvider";
|
|
||||||
import { writeToLog } from "@/utils/log";
|
|
||||||
|
|
||||||
export const useDownloadedFileOpener = () => {
|
|
||||||
const router = useRouter();
|
|
||||||
const { setPlayUrl, setOfflineSettings } = usePlaySettings();
|
|
||||||
|
|
||||||
const openFile = useCallback(
|
|
||||||
async (item: BaseItemDto) => {
|
|
||||||
if (!item.Id) {
|
|
||||||
writeToLog("ERROR", "Attempted to open a file without an ID.");
|
|
||||||
console.error("Attempted to open a file without an ID.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const queryParams = new URLSearchParams({
|
|
||||||
itemId: item.Id,
|
|
||||||
offline: "true",
|
|
||||||
playbackPosition:
|
|
||||||
item.UserData?.PlaybackPositionTicks?.toString() ?? "0",
|
|
||||||
});
|
|
||||||
try {
|
|
||||||
router.push(`/player/direct-player?${queryParams.toString()}`);
|
|
||||||
} catch (error) {
|
|
||||||
writeToLog("ERROR", "Error opening file", error);
|
|
||||||
console.error("Error opening file:", error);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[setOfflineSettings, setPlayUrl, router],
|
|
||||||
);
|
|
||||||
|
|
||||||
return { openFile };
|
|
||||||
};
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
import type { BaseItemDto } from "@jellyfin/sdk/lib/generated-client";
|
|
||||||
import { useAtom, useAtomValue } from "jotai";
|
|
||||||
import { useEffect, useMemo } from "react";
|
|
||||||
import { Platform } from "react-native";
|
|
||||||
import type * as ImageColorsType from "react-native-image-colors";
|
|
||||||
import { apiAtom } from "@/providers/JellyfinProvider";
|
|
||||||
|
|
||||||
// Conditionally import react-native-image-colors only on non-TV platforms
|
|
||||||
const ImageColors = Platform.isTV
|
|
||||||
? null
|
|
||||||
: (require("react-native-image-colors") as typeof ImageColorsType);
|
|
||||||
|
|
||||||
import {
|
|
||||||
adjustToNearBlack,
|
|
||||||
calculateTextColor,
|
|
||||||
isCloseToBlack,
|
|
||||||
itemThemeColorAtom,
|
|
||||||
} from "@/utils/atoms/primaryColor";
|
|
||||||
import { getItemImage } from "@/utils/getItemImage";
|
|
||||||
import { storage } from "@/utils/mmkv";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Custom hook to extract and manage image colors for a given item.
|
|
||||||
*
|
|
||||||
* @param item - The BaseItemDto object representing the item.
|
|
||||||
* @param disabled - A boolean flag to disable color extraction.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export const useImageColors = ({
|
|
||||||
item,
|
|
||||||
url,
|
|
||||||
disabled,
|
|
||||||
}: {
|
|
||||||
item?: BaseItemDto | null;
|
|
||||||
url?: string | null;
|
|
||||||
disabled?: boolean;
|
|
||||||
}) => {
|
|
||||||
const api = useAtomValue(apiAtom);
|
|
||||||
const [, setPrimaryColor] = useAtom(itemThemeColorAtom);
|
|
||||||
|
|
||||||
const isTv = Platform.isTV;
|
|
||||||
|
|
||||||
const source = useMemo(() => {
|
|
||||||
if (!api) return;
|
|
||||||
if (url) return { uri: url };
|
|
||||||
if (item)
|
|
||||||
return getItemImage({
|
|
||||||
item,
|
|
||||||
api,
|
|
||||||
variant: "Primary",
|
|
||||||
quality: 80,
|
|
||||||
width: 300,
|
|
||||||
});
|
|
||||||
return null;
|
|
||||||
}, [api, item, url]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isTv) return;
|
|
||||||
if (disabled) return;
|
|
||||||
if (source?.uri) {
|
|
||||||
const _primary = storage.getString(`${source.uri}-primary`);
|
|
||||||
const _text = storage.getString(`${source.uri}-text`);
|
|
||||||
|
|
||||||
if (_primary && _text) {
|
|
||||||
setPrimaryColor({
|
|
||||||
primary: _primary,
|
|
||||||
text: _text,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract colors from the image
|
|
||||||
if (!ImageColors?.getColors) return;
|
|
||||||
|
|
||||||
ImageColors.getColors(source.uri, {
|
|
||||||
fallback: "#fff",
|
|
||||||
cache: false,
|
|
||||||
})
|
|
||||||
.then((colors: ImageColorsType.ImageColorsResult) => {
|
|
||||||
let primary = "#fff";
|
|
||||||
let text = "#000";
|
|
||||||
let backup = "#fff";
|
|
||||||
|
|
||||||
// Select the appropriate color based on the platform
|
|
||||||
if (colors.platform === "android") {
|
|
||||||
primary = colors.dominant;
|
|
||||||
backup = colors.vibrant;
|
|
||||||
} else if (colors.platform === "ios") {
|
|
||||||
primary = colors.detail;
|
|
||||||
backup = colors.primary;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adjust the primary color if it's too close to black
|
|
||||||
if (primary && isCloseToBlack(primary)) {
|
|
||||||
if (backup && !isCloseToBlack(backup)) primary = backup;
|
|
||||||
primary = adjustToNearBlack(primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the text color based on the primary color
|
|
||||||
if (primary) text = calculateTextColor(primary);
|
|
||||||
|
|
||||||
setPrimaryColor({
|
|
||||||
primary,
|
|
||||||
text,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Cache the colors in storage
|
|
||||||
if (source.uri && primary) {
|
|
||||||
storage.set(`${source.uri}-primary`, primary);
|
|
||||||
storage.set(`${source.uri}-text`, text);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error: any) => {
|
|
||||||
console.error("Error getting colors", error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [isTv, source?.uri, setPrimaryColor, disabled]);
|
|
||||||
|
|
||||||
if (isTv) return;
|
|
||||||
};
|
|
||||||
@@ -2,7 +2,7 @@ import axios, { type AxiosError, type AxiosInstance } from "axios";
|
|||||||
import { atom } from "jotai";
|
import { atom } from "jotai";
|
||||||
import { useAtom } from "jotai/index";
|
import { useAtom } from "jotai/index";
|
||||||
import { inRange } from "lodash";
|
import { inRange } from "lodash";
|
||||||
import type { User as JellyseerrUser } from "@/utils/jellyseerr/server/entity/User";
|
import type { User as SeerrUser } from "@/utils/jellyseerr/server/entity/User";
|
||||||
import type {
|
import type {
|
||||||
MovieResult,
|
MovieResult,
|
||||||
Results,
|
Results,
|
||||||
@@ -62,12 +62,41 @@ interface SearchResults {
|
|||||||
results: Results[];
|
results: Results[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const JELLYSEERR_USER = "JELLYSEERR_USER";
|
const SEERR_USER = "SEERR_USER";
|
||||||
const JELLYSEERR_COOKIES = "JELLYSEERR_COOKIES";
|
const SEERR_COOKIES = "SEERR_COOKIES";
|
||||||
|
|
||||||
export const clearJellyseerrStorageData = () => {
|
// One-time migration of the legacy Jellyseerr storage keys to the Seerr-branded
|
||||||
storage.remove(JELLYSEERR_USER);
|
// keys. Runs at module load, before seerrUserAtom reads SEERR_USER, so logged-in
|
||||||
storage.remove(JELLYSEERR_COOKIES);
|
// users keep their session through the rename instead of being silently logged out.
|
||||||
|
const LEGACY_USER_KEY = "JELLYSEERR_USER";
|
||||||
|
const LEGACY_COOKIES_KEY = "JELLYSEERR_COOKIES";
|
||||||
|
|
||||||
|
function migrateLegacySeerrStorage() {
|
||||||
|
const legacyUser = storage.get<SeerrUser>(LEGACY_USER_KEY);
|
||||||
|
if (
|
||||||
|
legacyUser !== undefined &&
|
||||||
|
storage.get<SeerrUser>(SEERR_USER) === undefined
|
||||||
|
) {
|
||||||
|
storage.setAny(SEERR_USER, legacyUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
const legacyCookies = storage.get<string[]>(LEGACY_COOKIES_KEY);
|
||||||
|
if (
|
||||||
|
legacyCookies !== undefined &&
|
||||||
|
storage.get<string[]>(SEERR_COOKIES) === undefined
|
||||||
|
) {
|
||||||
|
storage.setAny(SEERR_COOKIES, legacyCookies);
|
||||||
|
}
|
||||||
|
|
||||||
|
storage.remove(LEGACY_USER_KEY);
|
||||||
|
storage.remove(LEGACY_COOKIES_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
migrateLegacySeerrStorage();
|
||||||
|
|
||||||
|
export const clearSeerrStorageData = () => {
|
||||||
|
storage.remove(SEERR_USER);
|
||||||
|
storage.remove(SEERR_COOKIES);
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum Endpoints {
|
export enum Endpoints {
|
||||||
@@ -111,12 +140,27 @@ export type TestResult =
|
|||||||
isValid: false;
|
isValid: false;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class JellyseerrApi {
|
/**
|
||||||
|
* Normalizes a URL by ensuring it has a protocol prefix (https:// or http://)
|
||||||
|
* @param url - The URL to normalize
|
||||||
|
* @returns The normalized URL with protocol prefix
|
||||||
|
*/
|
||||||
|
function normalizeUrl(url: string): string {
|
||||||
|
const trimmed = url.trim().replace(/\/+$/, ""); // Remove trailing slashes
|
||||||
|
if (trimmed.match(/^https?:\/\//i)) {
|
||||||
|
return trimmed;
|
||||||
|
}
|
||||||
|
// Default to https if no protocol is specified
|
||||||
|
return `https://${trimmed}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SeerrApi {
|
||||||
axios: AxiosInstance;
|
axios: AxiosInstance;
|
||||||
|
|
||||||
constructor(baseUrl: string) {
|
constructor(baseUrl: string) {
|
||||||
|
const normalizedUrl = normalizeUrl(baseUrl);
|
||||||
this.axios = axios.create({
|
this.axios = axios.create({
|
||||||
baseURL: baseUrl,
|
baseURL: normalizedUrl,
|
||||||
withCredentials: true,
|
withCredentials: true,
|
||||||
withXSRFToken: true,
|
withXSRFToken: true,
|
||||||
xsrfHeaderName: "XSRF-TOKEN",
|
xsrfHeaderName: "XSRF-TOKEN",
|
||||||
@@ -126,8 +170,8 @@ export class JellyseerrApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async test(): Promise<TestResult> {
|
async test(): Promise<TestResult> {
|
||||||
const user = storage.get<JellyseerrUser>(JELLYSEERR_USER);
|
const user = storage.get<SeerrUser>(SEERR_USER);
|
||||||
const cookies = storage.get<string[]>(JELLYSEERR_COOKIES);
|
const cookies = storage.get<string[]>(SEERR_COOKIES);
|
||||||
|
|
||||||
if (user && cookies) {
|
if (user && cookies) {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
@@ -142,15 +186,13 @@ export class JellyseerrApi {
|
|||||||
const { status, headers, data } = response;
|
const { status, headers, data } = response;
|
||||||
if (inRange(status, 200, 299)) {
|
if (inRange(status, 200, 299)) {
|
||||||
if (data.version < "2.0.0") {
|
if (data.version < "2.0.0") {
|
||||||
const error = t(
|
const error = t("seerr.toasts.seer_does_not_meet_requirements");
|
||||||
"jellyseerr.toasts.jellyseer_does_not_meet_requirements",
|
|
||||||
);
|
|
||||||
toast.error(error);
|
toast.error(error);
|
||||||
throw Error(error);
|
throw Error(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
storage.setAny(
|
storage.setAny(
|
||||||
JELLYSEERR_COOKIES,
|
SEERR_COOKIES,
|
||||||
headers["set-cookie"]?.flatMap((c) => c.split("; ")) ?? [],
|
headers["set-cookie"]?.flatMap((c) => c.split("; ")) ?? [],
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
@@ -158,9 +200,9 @@ export class JellyseerrApi {
|
|||||||
requiresPass: true,
|
requiresPass: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
toast.error(t("jellyseerr.toasts.jellyseerr_test_failed"));
|
toast.error(t("seerr.toasts.seerr_test_failed"));
|
||||||
writeErrorLog(
|
writeErrorLog(
|
||||||
`Jellyseerr returned a ${status} for url:\n${response.config.url}`,
|
`Seerr returned a ${status} for url:\n${response.config.url}`,
|
||||||
response.data,
|
response.data,
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
@@ -169,7 +211,7 @@ export class JellyseerrApi {
|
|||||||
};
|
};
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
const msg = t("jellyseerr.toasts.failed_to_test_jellyseerr_server_url");
|
const msg = t("seerr.toasts.failed_to_test_seerr_server_url");
|
||||||
toast.error(msg);
|
toast.error(msg);
|
||||||
console.error(msg, e);
|
console.error(msg, e);
|
||||||
return {
|
return {
|
||||||
@@ -179,9 +221,9 @@ export class JellyseerrApi {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async login(username: string, password: string): Promise<JellyseerrUser> {
|
async login(username: string, password: string): Promise<SeerrUser> {
|
||||||
return this.axios
|
return this.axios
|
||||||
?.post<JellyseerrUser>(Endpoints.API_V1 + Endpoints.AUTH_JELLYFIN, {
|
?.post<SeerrUser>(Endpoints.API_V1 + Endpoints.AUTH_JELLYFIN, {
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
email: username,
|
email: username,
|
||||||
@@ -189,7 +231,7 @@ export class JellyseerrApi {
|
|||||||
.then((response) => {
|
.then((response) => {
|
||||||
const user = response?.data;
|
const user = response?.data;
|
||||||
if (!user) throw Error("Login failed");
|
if (!user) throw Error("Login failed");
|
||||||
storage.setAny(JELLYSEERR_USER, user);
|
storage.setAny(SEERR_USER, user);
|
||||||
return user;
|
return user;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -364,7 +406,7 @@ export class JellyseerrApi {
|
|||||||
const issue = response.data;
|
const issue = response.data;
|
||||||
|
|
||||||
if (issue.status === IssueStatus.OPEN) {
|
if (issue.status === IssueStatus.OPEN) {
|
||||||
toast.success(t("jellyseerr.toasts.issue_submitted"));
|
toast.success(t("seerr.toasts.issue_submitted"));
|
||||||
}
|
}
|
||||||
return issue;
|
return issue;
|
||||||
});
|
});
|
||||||
@@ -392,7 +434,7 @@ export class JellyseerrApi {
|
|||||||
const cookies = response.headers["set-cookie"];
|
const cookies = response.headers["set-cookie"];
|
||||||
if (cookies) {
|
if (cookies) {
|
||||||
storage.setAny(
|
storage.setAny(
|
||||||
JELLYSEERR_COOKIES,
|
SEERR_COOKIES,
|
||||||
response.headers["set-cookie"]?.flatMap((c) => c.split("; ")),
|
response.headers["set-cookie"]?.flatMap((c) => c.split("; ")),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -400,11 +442,11 @@ export class JellyseerrApi {
|
|||||||
},
|
},
|
||||||
(error: AxiosError) => {
|
(error: AxiosError) => {
|
||||||
writeErrorLog(
|
writeErrorLog(
|
||||||
`Jellyseerr response error\nerror: ${error.toString()}\nurl: ${error?.config?.url}`,
|
`Seerr response error\nerror: ${error.toString()}\nurl: ${error?.config?.url}`,
|
||||||
error.response?.data,
|
error.response?.data,
|
||||||
);
|
);
|
||||||
if (error.response?.status === 403) {
|
if (error.response?.status === 403) {
|
||||||
clearJellyseerrStorageData();
|
clearSeerrStorageData();
|
||||||
}
|
}
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
},
|
},
|
||||||
@@ -412,7 +454,7 @@ export class JellyseerrApi {
|
|||||||
|
|
||||||
this.axios.interceptors.request.use(
|
this.axios.interceptors.request.use(
|
||||||
async (config) => {
|
async (config) => {
|
||||||
const cookies = storage.get<string[]>(JELLYSEERR_COOKIES);
|
const cookies = storage.get<string[]>(SEERR_COOKIES);
|
||||||
if (cookies) {
|
if (cookies) {
|
||||||
const headerName = this.axios.defaults.xsrfHeaderName!;
|
const headerName = this.axios.defaults.xsrfHeaderName!;
|
||||||
const xsrfToken = cookies
|
const xsrfToken = cookies
|
||||||
@@ -425,78 +467,77 @@ export class JellyseerrApi {
|
|||||||
return config;
|
return config;
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
console.error("Jellyseerr request error", error);
|
console.error("Seerr request error", error);
|
||||||
|
return Promise.reject(error);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const jellyseerrUserAtom = atom(storage.get<JellyseerrUser>(JELLYSEERR_USER));
|
const seerrUserAtom = atom(storage.get<SeerrUser>(SEERR_USER));
|
||||||
|
|
||||||
export const useJellyseerr = () => {
|
export const useSeerr = () => {
|
||||||
const { settings, updateSettings } = useSettings();
|
const { settings, updateSettings } = useSettings();
|
||||||
const [jellyseerrUser, setJellyseerrUser] = useAtom(jellyseerrUserAtom);
|
const [seerrUser, setSeerrUser] = useAtom(seerrUserAtom);
|
||||||
const queryClient = useNetworkAwareQueryClient();
|
const queryClient = useNetworkAwareQueryClient();
|
||||||
|
|
||||||
const jellyseerrApi = useMemo(() => {
|
const seerrApi = useMemo(() => {
|
||||||
const cookies = storage.get<string[]>(JELLYSEERR_COOKIES);
|
const cookies = storage.get<string[]>(SEERR_COOKIES);
|
||||||
if (settings?.jellyseerrServerUrl && cookies && jellyseerrUser) {
|
if (settings?.seerrServerUrl && cookies && seerrUser) {
|
||||||
return new JellyseerrApi(settings?.jellyseerrServerUrl);
|
return new SeerrApi(settings?.seerrServerUrl);
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}, [settings?.jellyseerrServerUrl, jellyseerrUser]);
|
}, [settings?.seerrServerUrl, seerrUser]);
|
||||||
|
|
||||||
const clearAllJellyseerData = useCallback(async () => {
|
const clearAllSeerrData = useCallback(async () => {
|
||||||
clearJellyseerrStorageData();
|
clearSeerrStorageData();
|
||||||
setJellyseerrUser(undefined);
|
setSeerrUser(undefined);
|
||||||
updateSettings({ jellyseerrServerUrl: undefined });
|
updateSettings({ seerrServerUrl: undefined });
|
||||||
}, []);
|
}, [setSeerrUser, updateSettings]);
|
||||||
|
|
||||||
const requestMedia = useCallback(
|
const requestMedia = useCallback(
|
||||||
(title: string, request: MediaRequestBody, onSuccess?: () => void) => {
|
(title: string, request: MediaRequestBody, onSuccess?: () => void) => {
|
||||||
jellyseerrApi?.request?.(request)?.then(async (mediaRequest) => {
|
seerrApi?.request?.(request)?.then(async (mediaRequest) => {
|
||||||
await queryClient.invalidateQueries({
|
await queryClient.invalidateQueries({
|
||||||
queryKey: ["search", "jellyseerr"],
|
queryKey: ["search", "seerr"],
|
||||||
});
|
});
|
||||||
|
|
||||||
switch (mediaRequest.status) {
|
switch (mediaRequest.status) {
|
||||||
case MediaRequestStatus.PENDING:
|
case MediaRequestStatus.PENDING:
|
||||||
case MediaRequestStatus.APPROVED:
|
case MediaRequestStatus.APPROVED:
|
||||||
toast.success(
|
toast.success(t("seerr.toasts.requested_item", { item: title }));
|
||||||
t("jellyseerr.toasts.requested_item", { item: title }),
|
|
||||||
);
|
|
||||||
onSuccess?.();
|
onSuccess?.();
|
||||||
break;
|
break;
|
||||||
case MediaRequestStatus.DECLINED:
|
case MediaRequestStatus.DECLINED:
|
||||||
toast.error(
|
toast.error(t("seerr.toasts.you_dont_have_permission_to_request"));
|
||||||
t("jellyseerr.toasts.you_dont_have_permission_to_request"),
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
case MediaRequestStatus.FAILED:
|
case MediaRequestStatus.FAILED:
|
||||||
toast.error(
|
toast.error(
|
||||||
t("jellyseerr.toasts.something_went_wrong_requesting_media"),
|
t("seerr.toasts.something_went_wrong_requesting_media"),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[jellyseerrApi],
|
[seerrApi, queryClient],
|
||||||
);
|
);
|
||||||
|
|
||||||
const isJellyseerrMovieOrTvResult = (
|
const isSeerrMovieOrTvResult = useCallback(
|
||||||
items: any | null | undefined,
|
(items: any | null | undefined): items is MovieResult | TvResult => {
|
||||||
): items is MovieResult | TvResult => {
|
return (
|
||||||
return (
|
items &&
|
||||||
items &&
|
Object.hasOwn(items, "mediaType") &&
|
||||||
Object.hasOwn(items, "mediaType") &&
|
(items.mediaType === MediaType.MOVIE ||
|
||||||
(items.mediaType === MediaType.MOVIE || items.mediaType === MediaType.TV)
|
items.mediaType === MediaType.TV)
|
||||||
);
|
);
|
||||||
};
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
const getTitle = (
|
const getTitle = (
|
||||||
item?: TvResult | TvDetails | MovieResult | MovieDetails | PersonCreditCast,
|
item?: TvResult | TvDetails | MovieResult | MovieDetails | PersonCreditCast,
|
||||||
) => {
|
) => {
|
||||||
return isJellyseerrMovieOrTvResult(item)
|
return isSeerrMovieOrTvResult(item)
|
||||||
? item.mediaType === MediaType.MOVIE
|
? item.mediaType === MediaType.MOVIE
|
||||||
? item?.title
|
? item?.title
|
||||||
: item?.name
|
: item?.name
|
||||||
@@ -509,7 +550,7 @@ export const useJellyseerr = () => {
|
|||||||
item?: TvResult | TvDetails | MovieResult | MovieDetails | PersonCreditCast,
|
item?: TvResult | TvDetails | MovieResult | MovieDetails | PersonCreditCast,
|
||||||
) => {
|
) => {
|
||||||
return new Date(
|
return new Date(
|
||||||
(isJellyseerrMovieOrTvResult(item)
|
(isSeerrMovieOrTvResult(item)
|
||||||
? item.mediaType === MediaType.MOVIE
|
? item.mediaType === MediaType.MOVIE
|
||||||
? item?.releaseDate
|
? item?.releaseDate
|
||||||
: item?.firstAirDate
|
: item?.firstAirDate
|
||||||
@@ -522,32 +563,35 @@ export const useJellyseerr = () => {
|
|||||||
const getMediaType = (
|
const getMediaType = (
|
||||||
item?: TvResult | TvDetails | MovieResult | MovieDetails | PersonCreditCast,
|
item?: TvResult | TvDetails | MovieResult | MovieDetails | PersonCreditCast,
|
||||||
): MediaType => {
|
): MediaType => {
|
||||||
return isJellyseerrMovieOrTvResult(item)
|
return isSeerrMovieOrTvResult(item)
|
||||||
? (item.mediaType as MediaType)
|
? (item.mediaType as MediaType)
|
||||||
: item?.mediaInfo?.mediaType;
|
: item?.mediaInfo?.mediaType;
|
||||||
};
|
};
|
||||||
|
|
||||||
const jellyseerrRegion = useMemo(
|
const seerrRegion = useMemo(
|
||||||
// streamingRegion and discoverRegion exists. region doesn't
|
// streamingRegion and discoverRegion exists. region doesn't
|
||||||
() => jellyseerrUser?.settings?.discoverRegion || "US",
|
() => seerrUser?.settings?.discoverRegion || "US",
|
||||||
[jellyseerrUser],
|
[seerrUser],
|
||||||
);
|
);
|
||||||
|
|
||||||
const jellyseerrLocale = useMemo(() => {
|
const seerrLocale = useMemo(() => {
|
||||||
return jellyseerrUser?.settings?.locale || "en";
|
const locale = seerrUser?.settings?.locale || "en";
|
||||||
}, [jellyseerrUser]);
|
// Use regex to check if locale already contains region code (e.g., zh-CN, pt-BR)
|
||||||
|
// If not, append the region to create a valid BCP 47 locale string
|
||||||
|
return /^[a-z]{2,3}-/i.test(locale) ? locale : `${locale}-${seerrRegion}`;
|
||||||
|
}, [seerrUser, seerrRegion]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
jellyseerrApi,
|
seerrApi,
|
||||||
jellyseerrUser,
|
seerrUser,
|
||||||
setJellyseerrUser,
|
setSeerrUser,
|
||||||
clearAllJellyseerData,
|
clearAllSeerrData,
|
||||||
isJellyseerrMovieOrTvResult,
|
isSeerrMovieOrTvResult,
|
||||||
getTitle,
|
getTitle,
|
||||||
getYear,
|
getYear,
|
||||||
getMediaType,
|
getMediaType,
|
||||||
jellyseerrRegion,
|
seerrRegion,
|
||||||
jellyseerrLocale,
|
seerrLocale,
|
||||||
requestMedia,
|
requestMedia,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -171,7 +171,11 @@ final class MPVLayerRenderer {
|
|||||||
// Enable composite OSD mode - renders subtitles directly onto video frames using GPU
|
// Enable composite OSD mode - renders subtitles directly onto video frames using GPU
|
||||||
// This is better for PiP as subtitles are baked into the video
|
// This is better for PiP as subtitles are baked into the video
|
||||||
// NOTE: Must be set BEFORE the #if targetEnvironment check or tvOS will freeze on player exit
|
// NOTE: Must be set BEFORE the #if targetEnvironment check or tvOS will freeze on player exit
|
||||||
|
#if targetEnvironment(simulator)
|
||||||
|
checkError(mpv_set_option_string(handle, "avfoundation-composite-osd", "no"))
|
||||||
|
#else
|
||||||
checkError(mpv_set_option_string(handle, "avfoundation-composite-osd", "yes"))
|
checkError(mpv_set_option_string(handle, "avfoundation-composite-osd", "yes"))
|
||||||
|
#endif
|
||||||
|
|
||||||
// Hardware decoding with VideoToolbox
|
// Hardware decoding with VideoToolbox
|
||||||
// On simulator, use software decoding since VideoToolbox is not available
|
// On simulator, use software decoding since VideoToolbox is not available
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import { MpvPlayerViewProps } from "./MpvPlayer.types";
|
import { MpvPlayerViewProps } from "./MpvPlayer.types";
|
||||||
|
|
||||||
export default function MpvPlayerView(props: MpvPlayerViewProps) {
|
export default function MpvPlayerView(props: MpvPlayerViewProps) {
|
||||||
const url = props.source?.url ?? "";
|
const url = props.source?.url ?? "";
|
||||||
|
const { t } = useTranslation();
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<iframe
|
<iframe
|
||||||
title='MPV Player'
|
title={t("player.mpv_player_title")}
|
||||||
style={{ flex: 1 }}
|
style={{ flex: 1 }}
|
||||||
src={url}
|
src={url}
|
||||||
onLoad={() => props.onLoad?.({ nativeEvent: { url } })}
|
onLoad={() => props.onLoad?.({ nativeEvent: { url } })}
|
||||||
|
|||||||
12
package.json
12
package.json
@@ -70,14 +70,14 @@
|
|||||||
"expo-system-ui": "~6.0.9",
|
"expo-system-ui": "~6.0.9",
|
||||||
"expo-task-manager": "14.0.9",
|
"expo-task-manager": "14.0.9",
|
||||||
"expo-web-browser": "~15.0.10",
|
"expo-web-browser": "~15.0.10",
|
||||||
"i18next": "^25.0.0",
|
"i18next": "^26.0.0",
|
||||||
"jotai": "2.16.2",
|
"jotai": "2.16.2",
|
||||||
"lodash": "4.17.23",
|
"lodash": "4.17.23",
|
||||||
"nativewind": "^2.0.11",
|
"nativewind": "^2.0.11",
|
||||||
"patch-package": "^8.0.0",
|
"patch-package": "^8.0.0",
|
||||||
"react": "19.1.0",
|
"react": "19.1.0",
|
||||||
"react-dom": "19.1.0",
|
"react-dom": "19.1.0",
|
||||||
"react-i18next": "16.5.4",
|
"react-i18next": "17.0.8",
|
||||||
"react-native": "0.81.5",
|
"react-native": "0.81.5",
|
||||||
"react-native-awesome-slider": "^2.9.0",
|
"react-native-awesome-slider": "^2.9.0",
|
||||||
"react-native-bottom-tabs": "1.1.0",
|
"react-native-bottom-tabs": "1.1.0",
|
||||||
@@ -117,16 +117,16 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.28.6",
|
"@babel/core": "7.28.6",
|
||||||
"@biomejs/biome": "2.3.11",
|
"@biomejs/biome": "2.3.11",
|
||||||
"@react-native-community/cli": "20.1.1",
|
"@react-native-community/cli": "20.1.3",
|
||||||
"@react-native-tvos/config-tv": "0.1.4",
|
"@react-native-tvos/config-tv": "0.1.6",
|
||||||
"@types/jest": "29.5.14",
|
"@types/jest": "29.5.14",
|
||||||
"@types/lodash": "4.17.23",
|
"@types/lodash": "4.17.23",
|
||||||
"@types/react": "19.1.17",
|
"@types/react": "19.1.17",
|
||||||
"@types/react-test-renderer": "19.1.0",
|
"@types/react-test-renderer": "19.1.0",
|
||||||
"cross-env": "10.1.0",
|
"cross-env": "10.1.0",
|
||||||
"expo-doctor": "1.17.14",
|
"expo-doctor": "1.18.22",
|
||||||
"husky": "9.1.7",
|
"husky": "9.1.7",
|
||||||
"lint-staged": "16.2.7",
|
"lint-staged": "17.0.5",
|
||||||
"react-test-renderer": "19.2.3",
|
"react-test-renderer": "19.2.3",
|
||||||
"typescript": "5.9.3"
|
"typescript": "5.9.3"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,10 +5,13 @@ import {
|
|||||||
type ReactNode,
|
type ReactNode,
|
||||||
useCallback,
|
useCallback,
|
||||||
useContext,
|
useContext,
|
||||||
|
useEffect,
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from "react";
|
||||||
|
|
||||||
|
import { BackHandler, Platform } from "react-native";
|
||||||
|
|
||||||
interface ModalOptions {
|
interface ModalOptions {
|
||||||
enableDynamicSizing?: boolean;
|
enableDynamicSizing?: boolean;
|
||||||
snapPoints?: (string | number)[];
|
snapPoints?: (string | number)[];
|
||||||
@@ -73,6 +76,25 @@ export const GlobalModalProvider: React.FC<GlobalModalProviderProps> = ({
|
|||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (Platform.OS !== "android") return;
|
||||||
|
|
||||||
|
const onBackPress = () => {
|
||||||
|
if (isVisible) {
|
||||||
|
hideModal();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const subscription = BackHandler.addEventListener(
|
||||||
|
"hardwareBackPress",
|
||||||
|
onBackPress,
|
||||||
|
);
|
||||||
|
|
||||||
|
return () => subscription.remove();
|
||||||
|
}, [isVisible, hideModal]);
|
||||||
|
|
||||||
const value = {
|
const value = {
|
||||||
showModal,
|
showModal,
|
||||||
hideModal,
|
hideModal,
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import { getDeviceName } from "react-native-device-info";
|
|||||||
import uuid from "react-native-uuid";
|
import uuid from "react-native-uuid";
|
||||||
import useRouter from "@/hooks/useAppRouter";
|
import useRouter from "@/hooks/useAppRouter";
|
||||||
import { useInterval } from "@/hooks/useInterval";
|
import { useInterval } from "@/hooks/useInterval";
|
||||||
import { JellyseerrApi, useJellyseerr } from "@/hooks/useJellyseerr";
|
import { SeerrApi, useSeerr } from "@/hooks/useSeerr";
|
||||||
import { useSettings } from "@/utils/atoms/settings";
|
import { useSettings } from "@/utils/atoms/settings";
|
||||||
import { writeErrorLog, writeInfoLog } from "@/utils/log";
|
import { writeErrorLog, writeInfoLog } from "@/utils/log";
|
||||||
import { storage } from "@/utils/mmkv";
|
import { storage } from "@/utils/mmkv";
|
||||||
@@ -113,7 +113,7 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
|||||||
const [isPolling, setIsPolling] = useState<boolean>(false);
|
const [isPolling, setIsPolling] = useState<boolean>(false);
|
||||||
const [secret, setSecret] = useState<string | null>(null);
|
const [secret, setSecret] = useState<string | null>(null);
|
||||||
const { setPluginSettings, refreshStreamyfinPluginSettings } = useSettings();
|
const { setPluginSettings, refreshStreamyfinPluginSettings } = useSettings();
|
||||||
const { clearAllJellyseerData, setJellyseerrUser } = useJellyseerr();
|
const { clearAllSeerrData, setSeerrUser } = useSeerr();
|
||||||
|
|
||||||
const headers = useMemo(() => {
|
const headers = useMemo(() => {
|
||||||
if (!deviceId) return {};
|
if (!deviceId) return {};
|
||||||
@@ -290,13 +290,13 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const recentPluginSettings = await refreshStreamyfinPluginSettings();
|
const recentPluginSettings = await refreshStreamyfinPluginSettings();
|
||||||
if (recentPluginSettings?.jellyseerrServerUrl?.value) {
|
if (recentPluginSettings?.seerrServerUrl?.value) {
|
||||||
const jellyseerrApi = new JellyseerrApi(
|
const seerrApi = new SeerrApi(
|
||||||
recentPluginSettings.jellyseerrServerUrl.value,
|
recentPluginSettings.seerrServerUrl.value,
|
||||||
);
|
);
|
||||||
await jellyseerrApi.test().then((result) => {
|
await seerrApi.test().then((result) => {
|
||||||
if (result.isValid && result.requiresPass) {
|
if (result.isValid && result.requiresPass) {
|
||||||
jellyseerrApi.login(username, password).then(setJellyseerrUser);
|
seerrApi.login(username, password).then(setSeerrUser);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -349,7 +349,7 @@ export const JellyfinProvider: React.FC<{ children: ReactNode }> = ({
|
|||||||
setUser(null);
|
setUser(null);
|
||||||
setApi(null);
|
setApi(null);
|
||||||
setPluginSettings(undefined);
|
setPluginSettings(undefined);
|
||||||
await clearAllJellyseerData();
|
await clearAllSeerrData();
|
||||||
// Note: We keep saved credentials for quick switching back
|
// Note: We keep saved credentials for quick switching back
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
|
|||||||
123
scripts/check-unused-translations.js
Normal file
123
scripts/check-unused-translations.js
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Check for unused translation keys in en.json
|
||||||
|
* Usage: bun run scripts/check-unused-translations.js [--remove]
|
||||||
|
*/
|
||||||
|
|
||||||
|
const fs = require("node:fs");
|
||||||
|
const path = require("node:path");
|
||||||
|
const { execSync } = require("node:child_process");
|
||||||
|
|
||||||
|
const TRANSLATION_FILE = path.join(__dirname, "../translations/en.json");
|
||||||
|
const REMOVE_UNUSED = process.argv.includes("--remove");
|
||||||
|
|
||||||
|
// Read translation file
|
||||||
|
const translations = JSON.parse(fs.readFileSync(TRANSLATION_FILE, "utf8"));
|
||||||
|
|
||||||
|
// Flatten nested keys
|
||||||
|
function flattenKeys(obj, prefix = "") {
|
||||||
|
let keys = [];
|
||||||
|
for (const [key, value] of Object.entries(obj)) {
|
||||||
|
const fullKey = prefix ? `${prefix}.${key}` : key;
|
||||||
|
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
||||||
|
keys = keys.concat(flattenKeys(value, fullKey));
|
||||||
|
} else {
|
||||||
|
keys.push(fullKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search for key usage in codebase
|
||||||
|
function isKeyUsed(key) {
|
||||||
|
try {
|
||||||
|
// Escape special regex characters in the key
|
||||||
|
const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||||
|
|
||||||
|
// Search in TypeScript/TSX files
|
||||||
|
const result = execSync(
|
||||||
|
// `2>nul` is cmd-only; omitted so this stays cross-platform (it would
|
||||||
|
// create a literal `nul` file on Unix). `|| echo ""` handles no-match.
|
||||||
|
`git grep -l "${escapedKey}" -- "*.ts" "*.tsx" || echo ""`,
|
||||||
|
{
|
||||||
|
encoding: "utf8",
|
||||||
|
cwd: path.join(__dirname, ".."),
|
||||||
|
maxBuffer: 10 * 1024 * 1024,
|
||||||
|
},
|
||||||
|
).trim();
|
||||||
|
|
||||||
|
return result.length > 0;
|
||||||
|
} catch (_error) {
|
||||||
|
// If grep fails, assume key is used to be safe
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove nested key from object
|
||||||
|
function removeNestedKey(obj, keyPath) {
|
||||||
|
const keys = keyPath.split(".");
|
||||||
|
const lastKey = keys.pop();
|
||||||
|
let current = obj;
|
||||||
|
|
||||||
|
for (const key of keys) {
|
||||||
|
if (!current[key]) return false;
|
||||||
|
current = current[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current[lastKey] !== undefined) {
|
||||||
|
delete current[lastKey];
|
||||||
|
|
||||||
|
// Clean up empty parent objects
|
||||||
|
if (Object.keys(current).length === 0 && keys.length > 0) {
|
||||||
|
removeNestedKey(obj, keys.join("."));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("🔍 Checking for unused translation keys...\n");
|
||||||
|
|
||||||
|
const allKeys = flattenKeys(translations);
|
||||||
|
const unusedKeys = [];
|
||||||
|
|
||||||
|
for (const key of allKeys) {
|
||||||
|
if (!isKeyUsed(key)) {
|
||||||
|
unusedKeys.push(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unusedKeys.length === 0) {
|
||||||
|
console.log("✅ All translation keys are being used!");
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Found ${unusedKeys.length} unused translation keys:\n`);
|
||||||
|
for (const key of unusedKeys) {
|
||||||
|
console.log(` ❌ ${key}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (REMOVE_UNUSED) {
|
||||||
|
console.log("\n🗑️ Removing unused keys...");
|
||||||
|
|
||||||
|
let removed = 0;
|
||||||
|
for (const key of unusedKeys) {
|
||||||
|
if (removeNestedKey(translations, key)) {
|
||||||
|
removed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write back to file
|
||||||
|
fs.writeFileSync(
|
||||||
|
TRANSLATION_FILE,
|
||||||
|
`${JSON.stringify(translations, null, 2)}\n`,
|
||||||
|
"utf8",
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`✅ Removed ${removed} unused translation keys from en.json`);
|
||||||
|
} else {
|
||||||
|
console.log("\n💡 Run with --remove flag to remove these keys from en.json");
|
||||||
|
console.log(
|
||||||
|
" Example: bun run scripts/check-unused-translations.js --remove",
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -176,7 +176,7 @@ function runTypeCheck() {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorOutput = (error && (error.stderr || error.stdout)) || "";
|
const errorOutput = (error && (error.stderr || error.stdout)) || "";
|
||||||
|
|
||||||
// Filter out jellyseerr utils errors - this is a third-party git submodule
|
// Filter out seerr utils errors - this is a third-party git submodule
|
||||||
// that generates a large volume of known type errors
|
// that generates a large volume of known type errors
|
||||||
const filteredLines = errorOutput.split("\n").filter((line) => {
|
const filteredLines = errorOutput.split("\n").filter((line) => {
|
||||||
const trimmedLine = line.trim();
|
const trimmedLine = line.trim();
|
||||||
@@ -227,7 +227,7 @@ function runTypeCheck() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log(
|
log(
|
||||||
`✅ ${colors.bold}TypeScript check passed${colors.reset} ${colors.gray}(jellyseerr utils errors ignored)${colors.reset}`,
|
`✅ ${colors.bold}TypeScript check passed${colors.reset} ${colors.gray}(seerr utils errors ignored)${colors.reset}`,
|
||||||
colors.green,
|
colors.green,
|
||||||
);
|
);
|
||||||
return { ok: true };
|
return { ok: true };
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
"username_placeholder": "Benutzername",
|
"username_placeholder": "Benutzername",
|
||||||
"password_placeholder": "Passwort",
|
"password_placeholder": "Passwort",
|
||||||
"login_button": "Anmelden",
|
"login_button": "Anmelden",
|
||||||
"quick_connect": "Schnellverbindung",
|
"quick_connect": "Quick Connect",
|
||||||
"enter_code_to_login": "Gib den Code {{code}} ein, um dich anzumelden",
|
"enter_code_to_login": "Gib den Code {{code}} ein, um dich anzumelden",
|
||||||
"failed_to_initiate_quick_connect": "Fehler beim Initiieren der Schnellverbindung",
|
"failed_to_initiate_quick_connect": "Fehler beim Initiieren der Schnellverbindung",
|
||||||
"got_it": "Verstanden",
|
"got_it": "Verstanden",
|
||||||
@@ -30,48 +30,48 @@
|
|||||||
"connect_button": "Verbinden",
|
"connect_button": "Verbinden",
|
||||||
"previous_servers": "Vorherige Server",
|
"previous_servers": "Vorherige Server",
|
||||||
"clear_button": "Löschen",
|
"clear_button": "Löschen",
|
||||||
"swipe_to_remove": "Swipe to remove",
|
"swipe_to_remove": "Wischen, um zu entfernen",
|
||||||
"search_for_local_servers": "Nach lokalen Servern suchen",
|
"search_for_local_servers": "Nach lokalen Servern suchen",
|
||||||
"searching": "Suche...",
|
"searching": "Suche...",
|
||||||
"servers": "Server",
|
"servers": "Server",
|
||||||
"saved": "Saved",
|
"saved": "Gespeichert",
|
||||||
"session_expired": "Session Expired",
|
"session_expired": "Sitzung abgelaufen",
|
||||||
"please_login_again": "Your saved session has expired. Please log in again.",
|
"please_login_again": "Ihre Sitzung ist abgelaufen. Bitte erneut anmelden.",
|
||||||
"remove_saved_login": "Remove Saved Login",
|
"remove_saved_login": "Gespeicherte Zugangsdaten entfernen",
|
||||||
"remove_saved_login_description": "This will remove your saved credentials for this server. You'll need to enter your username and password again next time.",
|
"remove_saved_login_description": "Hiermit werden ihre gespeicherten Zugangsdaten für diesen Server entfernt. Sie müssen sich dann erneut anmelden.",
|
||||||
"accounts_count": "{{count}} accounts",
|
"accounts_count": "{{count}} Konten",
|
||||||
"select_account": "Select Account",
|
"select_account": "Konto auswählen",
|
||||||
"add_account": "Add Account",
|
"add_account": "Konto hinzufügen",
|
||||||
"remove_account_description": "This will remove the saved credentials for {{username}}."
|
"remove_account_description": "Hiermit werden die gespeicherten Zugangsdaten für {{username}} entfernt."
|
||||||
},
|
},
|
||||||
"save_account": {
|
"save_account": {
|
||||||
"title": "Save Account",
|
"title": "Konto speichern",
|
||||||
"save_for_later": "Save this account",
|
"save_for_later": "Dieses Konto speichern",
|
||||||
"security_option": "Security Option",
|
"security_option": "Sicherheitseinstellung",
|
||||||
"no_protection": "No protection",
|
"no_protection": "Keine",
|
||||||
"no_protection_desc": "Quick login without authentication",
|
"no_protection_desc": "Schnellanmeldung ohne Authentifizierung",
|
||||||
"pin_code": "PIN code",
|
"pin_code": "PIN",
|
||||||
"pin_code_desc": "4-digit PIN required when switching",
|
"pin_code_desc": "4-stellige PIN bei Konto-Wechsel erforderlich",
|
||||||
"password": "Re-enter password",
|
"password": "Passwort wiederholen",
|
||||||
"password_desc": "Password required when switching",
|
"password_desc": "Passwort bei Konto-Wechsel erforderlich",
|
||||||
"save_button": "Save",
|
"save_button": "Speichern",
|
||||||
"cancel_button": "Cancel"
|
"cancel_button": "Abbrechen"
|
||||||
},
|
},
|
||||||
"pin": {
|
"pin": {
|
||||||
"enter_pin": "Enter PIN",
|
"enter_pin": "PIN eingeben",
|
||||||
"enter_pin_for": "Enter PIN for {{username}}",
|
"enter_pin_for": "PIN für {{username}} eingeben",
|
||||||
"enter_4_digits": "Enter 4 digits",
|
"enter_4_digits": "4 Ziffern eingeben",
|
||||||
"invalid_pin": "Invalid PIN",
|
"invalid_pin": "Ungültige PIN",
|
||||||
"setup_pin": "Set Up PIN",
|
"setup_pin": "PIN festlegen",
|
||||||
"confirm_pin": "Confirm PIN",
|
"confirm_pin": "PIN bestätigen",
|
||||||
"pins_dont_match": "PINs don't match",
|
"pins_dont_match": "PIN stimmt nicht überein",
|
||||||
"forgot_pin": "Forgot PIN?",
|
"forgot_pin": "PIN vergessen?",
|
||||||
"forgot_pin_desc": "Your saved credentials will be removed"
|
"forgot_pin_desc": "Ihre gespeicherten Zugangsdaten werden entfernt"
|
||||||
},
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"enter_password": "Enter Password",
|
"enter_password": "Passwort eingeben",
|
||||||
"enter_password_for": "Enter password for {{username}}",
|
"enter_password_for": "Passwort für {{username}} eingeben",
|
||||||
"invalid_password": "Invalid password"
|
"invalid_password": "Ungültiges Passwort"
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"checking_server_connection": "Überprüfe Serververbindung...",
|
"checking_server_connection": "Überprüfe Serververbindung...",
|
||||||
@@ -87,7 +87,7 @@
|
|||||||
"error_message": "Etwas ist schiefgelaufen.\nBitte melde dich ab und wieder an.",
|
"error_message": "Etwas ist schiefgelaufen.\nBitte melde dich ab und wieder an.",
|
||||||
"continue_watching": "Weiterschauen",
|
"continue_watching": "Weiterschauen",
|
||||||
"next_up": "Als nächstes",
|
"next_up": "Als nächstes",
|
||||||
"continue_and_next_up": "Continue & Next Up",
|
"continue_and_next_up": "\"Weiterschauen\" und \"Als Nächstes\"",
|
||||||
"recently_added_in": "Kürzlich hinzugefügt in {{libraryName}}",
|
"recently_added_in": "Kürzlich hinzugefügt in {{libraryName}}",
|
||||||
"suggested_movies": "Empfohlene Filme",
|
"suggested_movies": "Empfohlene Filme",
|
||||||
"suggested_episodes": "Empfohlene Episoden",
|
"suggested_episodes": "Empfohlene Episoden",
|
||||||
@@ -120,36 +120,36 @@
|
|||||||
},
|
},
|
||||||
"appearance": {
|
"appearance": {
|
||||||
"title": "Aussehen",
|
"title": "Aussehen",
|
||||||
"merge_next_up_continue_watching": "Merge Continue Watching & Next Up",
|
"merge_next_up_continue_watching": "\"Weiterschauen\" und \"Als Nächstes\" kombinieren",
|
||||||
"hide_remote_session_button": "Hide Remote Session Button"
|
"hide_remote_session_button": "Button für Remote-Sitzung ausblenden"
|
||||||
},
|
},
|
||||||
"network": {
|
"network": {
|
||||||
"title": "Network",
|
"title": "Netzwerk",
|
||||||
"local_network": "Local Network",
|
"local_network": "Lokales Netzwerk",
|
||||||
"auto_switch_enabled": "Auto-switch when at home",
|
"auto_switch_enabled": "Zuhause automatisch wechseln",
|
||||||
"auto_switch_description": "Automatically switch to local URL when connected to home WiFi",
|
"auto_switch_description": "Im WLAN Zuhause automatisch zu lokaler URL wechseln",
|
||||||
"local_url": "Local URL",
|
"local_url": "Lokale URL",
|
||||||
"local_url_hint": "Enter your local server address (e.g., http://192.168.1.100:8096)",
|
"local_url_hint": "Lokale Server-URL eingeben (zB. http://192.168.1.100:8096)",
|
||||||
"local_url_placeholder": "http://192.168.1.100:8096",
|
"local_url_placeholder": "http://192.168.1.100:8096",
|
||||||
"home_wifi_networks": "Home WiFi Networks",
|
"home_wifi_networks": "Private WLAN-Netze",
|
||||||
"add_current_network": "Add \"{{ssid}}\"",
|
"add_current_network": "{{ssid}} hinzufügen",
|
||||||
"not_connected_to_wifi": "Not connected to WiFi",
|
"not_connected_to_wifi": "Nicht mit WLAN verbunden",
|
||||||
"no_networks_configured": "No networks configured",
|
"no_networks_configured": "Keine Netzwerke konfiguriert",
|
||||||
"add_network_hint": "Add your home WiFi network to enable auto-switching",
|
"add_network_hint": "Füge dein privates WLAN-Netz hinzu um automatischen Wechsel zu aktivieren",
|
||||||
"current_wifi": "Current WiFi",
|
"current_wifi": "Aktuelles WLAN-Netz",
|
||||||
"using_url": "Using",
|
"using_url": "Verwendet",
|
||||||
"local": "Local URL",
|
"local": "Lokale URL",
|
||||||
"remote": "Remote URL",
|
"remote": "Remote URL",
|
||||||
"not_connected": "Not connected",
|
"not_connected": "Nicht verbunden",
|
||||||
"current_server": "Current Server",
|
"current_server": "Aktueller Server",
|
||||||
"remote_url": "Remote URL",
|
"remote_url": "Remote URL",
|
||||||
"active_url": "Active URL",
|
"active_url": "Aktive URL",
|
||||||
"not_configured": "Not configured",
|
"not_configured": "Nicht konfiguriert",
|
||||||
"network_added": "Network added",
|
"network_added": "Netzwerk hinzugefügt",
|
||||||
"network_already_added": "Network already added",
|
"network_already_added": "Netzwerk bereits hinzugefügt",
|
||||||
"no_wifi_connected": "Not connected to WiFi",
|
"no_wifi_connected": "Nicht mit WLAN verbunden",
|
||||||
"permission_denied": "Location permission denied",
|
"permission_denied": "Standortberechtigung nicht verfügbar",
|
||||||
"permission_denied_explanation": "Location permission is required to detect WiFi network for auto-switching. Please enable it in Settings."
|
"permission_denied_explanation": "Standortberechtigung ist nötig um WLAN-Netze für den automatischen Wechsel zu erkennen. Bitte in den Einstellungen aktivieren."
|
||||||
},
|
},
|
||||||
"user_info": {
|
"user_info": {
|
||||||
"user_info_title": "Benutzerinformationen",
|
"user_info_title": "Benutzerinformationen",
|
||||||
@@ -159,82 +159,82 @@
|
|||||||
"app_version": "App-Version"
|
"app_version": "App-Version"
|
||||||
},
|
},
|
||||||
"quick_connect": {
|
"quick_connect": {
|
||||||
"quick_connect_title": "Schnellverbindung",
|
"quick_connect_title": "Quick Connect",
|
||||||
"authorize_button": "Schnellverbindung autorisieren",
|
"authorize_button": "Quick Connect autorisieren",
|
||||||
"enter_the_quick_connect_code": "Gib den Schnellverbindungscode ein...",
|
"enter_the_quick_connect_code": "Quick Connect-Code eingeben...",
|
||||||
"success": "Erfolg",
|
"success": "Erfolgreich verbunden",
|
||||||
"quick_connect_autorized": "Schnellverbindung autorisiert",
|
"quick_connect_autorized": "Quick Connect autorisiert",
|
||||||
"error": "Fehler",
|
"error": "Fehler",
|
||||||
"invalid_code": "Ungültiger Code",
|
"invalid_code": "Ungültiger Code",
|
||||||
"authorize": "Autorisieren"
|
"authorize": "Autorisieren"
|
||||||
},
|
},
|
||||||
"media_controls": {
|
"media_controls": {
|
||||||
"media_controls_title": "Mediensteuerung",
|
"media_controls_title": "Mediensteuerung",
|
||||||
"forward_skip_length": "Vorspulzeit",
|
"forward_skip_length": "Vorspullänge",
|
||||||
"rewind_length": "Rückspulzeit",
|
"rewind_length": "Rückspullänge",
|
||||||
"seconds_unit": "s"
|
"seconds_unit": "s"
|
||||||
},
|
},
|
||||||
"gesture_controls": {
|
"gesture_controls": {
|
||||||
"gesture_controls_title": "Gestensteuerung",
|
"gesture_controls_title": "Gestensteuerung",
|
||||||
"horizontal_swipe_skip": "Horizontales Wischen zum Überspringen",
|
"horizontal_swipe_skip": "Horizontal Wischen zum Überspringen",
|
||||||
"horizontal_swipe_skip_description": "Wische links/rechts, wenn Steuerelemente ausgeblendet werden um zu überspringen",
|
"horizontal_swipe_skip_description": "Wische links/rechts, wenn Steuerelemente ausgeblendet sind um zu überspringen",
|
||||||
"left_side_brightness": "Helligkeitskontrolle der linken Seite",
|
"left_side_brightness": "Helligkeitsregler Links",
|
||||||
"left_side_brightness_description": "Wischen Sie auf der linken Seite nach oben/runter, um die Helligkeit anzupassen",
|
"left_side_brightness_description": "Links nach oben/unten wischen um Helligkeit anzupassen",
|
||||||
"right_side_volume": "Lautstärkeregelung der rechten Seite",
|
"right_side_volume": "Lautstärkeregler Rechts",
|
||||||
"right_side_volume_description": "Auf der rechten Seite nach oben/unten wischen, um Lautstärke anzupassen",
|
"right_side_volume_description": "Rechts nach oben/unten wischen um Lautstärke anzupassen",
|
||||||
"hide_volume_slider": "Hide Volume Slider",
|
"hide_volume_slider": "Lautstärkeregler ausblenden",
|
||||||
"hide_volume_slider_description": "Hide the volume slider in the video player",
|
"hide_volume_slider_description": "Lautstärkeregler im Videoplayer ausblenden",
|
||||||
"hide_brightness_slider": "Hide Brightness Slider",
|
"hide_brightness_slider": "Helligkeitsregler ausblenden",
|
||||||
"hide_brightness_slider_description": "Hide the brightness slider in the video player"
|
"hide_brightness_slider_description": "Helligkeitsregler im Videoplayer ausblenden"
|
||||||
},
|
},
|
||||||
"audio": {
|
"audio": {
|
||||||
"audio_title": "Audio",
|
"audio_title": "Audio",
|
||||||
"set_audio_track": "Audiospur aus dem vorherigen Element festlegen",
|
"set_audio_track": "Audiospur aus dem vorherigen Element übernehmen",
|
||||||
"audio_language": "Audio-Sprache",
|
"audio_language": "Audio-Sprache",
|
||||||
"audio_hint": "Wähl die Standardsprache für Audio aus.",
|
"audio_hint": "Standardsprache für Audio auswählen.",
|
||||||
"none": "Keine",
|
"none": "Keine",
|
||||||
"language": "Sprache",
|
"language": "Sprache",
|
||||||
"transcode_mode": {
|
"transcode_mode": {
|
||||||
"title": "Audio Transcoding",
|
"title": "Audio-Transcoding",
|
||||||
"description": "Controls how surround audio (7.1, TrueHD, DTS-HD) is handled",
|
"description": "Legt fest, wie Surround-Audio (7.1, TrueHD, DTS-HD) behandelt wird",
|
||||||
"auto": "Auto",
|
"auto": "Auto",
|
||||||
"stereo": "Force Stereo",
|
"stereo": "Stereo erzwingen",
|
||||||
"5_1": "Allow 5.1",
|
"5_1": "5.1 erlauben",
|
||||||
"passthrough": "Passthrough"
|
"passthrough": "Passthrough"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"subtitles": {
|
"subtitles": {
|
||||||
"subtitle_title": "Untertitel",
|
"subtitle_title": "Untertitel",
|
||||||
"subtitle_hint": "Konfigurier die Untertitel-Präferenzen.",
|
"subtitle_hint": "Untertitel-Erscheinungsbild und Verhalten konfigurieren.",
|
||||||
"subtitle_language": "Untertitel-Sprache",
|
"subtitle_language": "Untertitel-Sprache",
|
||||||
"subtitle_mode": "Untertitel-Modus",
|
"subtitle_mode": "Untertitel-Modus",
|
||||||
"set_subtitle_track": "Untertitel-Spur aus dem vorherigen Element festlegen",
|
"set_subtitle_track": "Untertitel-Spur aus dem vorherigen Element übernehmen",
|
||||||
"subtitle_size": "Untertitel-Größe",
|
"subtitle_size": "Untertitel-Größe",
|
||||||
"none": "Keine",
|
"none": "Keine",
|
||||||
"language": "Sprache",
|
"language": "Sprache",
|
||||||
"loading": "Lädt",
|
"loading": "Lädt",
|
||||||
"modes": {
|
"modes": {
|
||||||
"Default": "Standard",
|
"Default": "Standard",
|
||||||
"Smart": "Intelligent",
|
"Smart": "Smart",
|
||||||
"Always": "Immer",
|
"Always": "Immer",
|
||||||
"None": "Keine",
|
"None": "Keine",
|
||||||
"OnlyForced": "Nur erzwungen"
|
"OnlyForced": "Nur erzwungene"
|
||||||
},
|
},
|
||||||
"text_color": "Textfarbe",
|
"text_color": "Textfarbe",
|
||||||
"background_color": "Hintergrundfarbe",
|
"background_color": "Hintergrundfarbe",
|
||||||
"outline_color": "Konturfarbe",
|
"outline_color": "Konturfarbe",
|
||||||
"outline_thickness": "Umriss Dicke",
|
"outline_thickness": "Konturdicke",
|
||||||
"background_opacity": "Hintergrundtransparenz",
|
"background_opacity": "Hintergrundtransparenz",
|
||||||
"outline_opacity": "Kontur-Deckkraft",
|
"outline_opacity": "Konturtransparenz",
|
||||||
"bold_text": "Bold Text",
|
"bold_text": "Fettgedruckter Text",
|
||||||
"colors": {
|
"colors": {
|
||||||
"Black": "Schwarz",
|
"Black": "Schwarz",
|
||||||
"Gray": "Grau",
|
"Gray": "Grau",
|
||||||
"Silver": "Silber",
|
"Silver": "Silber",
|
||||||
"White": "Weiß",
|
"White": "Weiß",
|
||||||
"Maroon": "Marotte",
|
"Maroon": "Rotbraun",
|
||||||
"Red": "Rot",
|
"Red": "Rot",
|
||||||
"Fuchsia": "Fuchsia",
|
"Fuchsia": "Magenta",
|
||||||
"Yellow": "Gelb",
|
"Yellow": "Gelb",
|
||||||
"Olive": "Olivgrün",
|
"Olive": "Olivgrün",
|
||||||
"Green": "Grün",
|
"Green": "Grün",
|
||||||
@@ -251,29 +251,29 @@
|
|||||||
"Normal": "Normal",
|
"Normal": "Normal",
|
||||||
"Thick": "Dick"
|
"Thick": "Dick"
|
||||||
},
|
},
|
||||||
"subtitle_color": "Subtitle Color",
|
"subtitle_color": "Untertitelfarbe",
|
||||||
"subtitle_background_color": "Background Color",
|
"subtitle_background_color": "Hintergrundfarbe",
|
||||||
"subtitle_font": "Subtitle Font",
|
"subtitle_font": "Untertitel-Schriftart",
|
||||||
"ksplayer_title": "KSPlayer Settings",
|
"ksplayer_title": "KSPlayer Einstellungen",
|
||||||
"hardware_decode": "Hardware Decoding",
|
"hardware_decode": "Hardware Decoding",
|
||||||
"hardware_decode_description": "Use hardware acceleration for video decoding. Disable if you experience playback issues."
|
"hardware_decode_description": "Hardwarebeschleunigung für Video Decoding verwenden. Deaktivieren wenn Wiedergabeprobleme auftreten."
|
||||||
},
|
},
|
||||||
"vlc_subtitles": {
|
"vlc_subtitles": {
|
||||||
"title": "VLC Subtitle Settings",
|
"title": "VLC Untertitel-Einstellungen",
|
||||||
"hint": "Customize subtitle appearance for VLC player. Changes take effect on next playback.",
|
"hint": "Anpassen des Untertitel-Erscheinungsbildes für VLC. Änderungen werden bei der nächsten Wiedergabe übernommen.",
|
||||||
"text_color": "Text Color",
|
"text_color": "Schriftfarbe",
|
||||||
"background_color": "Background Color",
|
"background_color": "Hintergrundfarbe",
|
||||||
"background_opacity": "Background Opacity",
|
"background_opacity": "Hintergrundtransparenz",
|
||||||
"outline_color": "Outline Color",
|
"outline_color": "Konturfarbe",
|
||||||
"outline_opacity": "Outline Opacity",
|
"outline_opacity": "Konturtransparenz",
|
||||||
"outline_thickness": "Outline Thickness",
|
"outline_thickness": "Konturdicke",
|
||||||
"bold": "Bold Text",
|
"bold": "Fettgedruckter Text",
|
||||||
"margin": "Bottom Margin"
|
"margin": "Unterer Abstand"
|
||||||
},
|
},
|
||||||
"video_player": {
|
"video_player": {
|
||||||
"title": "Video Player",
|
"title": "Videoplayer",
|
||||||
"video_player": "Video Player",
|
"video_player": "Videoplayer",
|
||||||
"video_player_description": "Choose which video player to use on iOS.",
|
"video_player_description": "Videoplayer auf iOS auswählen.",
|
||||||
"ksplayer": "KSPlayer",
|
"ksplayer": "KSPlayer",
|
||||||
"vlc": "VLC"
|
"vlc": "VLC"
|
||||||
},
|
},
|
||||||
@@ -282,7 +282,7 @@
|
|||||||
"video_orientation": "Videoausrichtung",
|
"video_orientation": "Videoausrichtung",
|
||||||
"orientation": "Ausrichtung",
|
"orientation": "Ausrichtung",
|
||||||
"orientations": {
|
"orientations": {
|
||||||
"DEFAULT": "Standard",
|
"DEFAULT": "Geräteausrichtung folgen",
|
||||||
"ALL": "Alle",
|
"ALL": "Alle",
|
||||||
"PORTRAIT": "Hochformat",
|
"PORTRAIT": "Hochformat",
|
||||||
"PORTRAIT_UP": "Hochformat oben",
|
"PORTRAIT_UP": "Hochformat oben",
|
||||||
@@ -294,54 +294,54 @@
|
|||||||
"UNKNOWN": "Unbekannt"
|
"UNKNOWN": "Unbekannt"
|
||||||
},
|
},
|
||||||
"safe_area_in_controls": "Sicherer Bereich in den Steuerungen",
|
"safe_area_in_controls": "Sicherer Bereich in den Steuerungen",
|
||||||
"video_player": "Video player",
|
"video_player": "Videoplayer",
|
||||||
"video_players": {
|
"video_players": {
|
||||||
"VLC_3": "VLC 3",
|
"VLC_3": "VLC 3",
|
||||||
"VLC_4": "VLC 4 (Experimentell + PiP)"
|
"VLC_4": "VLC 4 (Experimentell + PiP)"
|
||||||
},
|
},
|
||||||
"show_custom_menu_links": "Benutzerdefinierte Menülinks anzeigen",
|
"show_custom_menu_links": "Benutzerdefinierte Menülinks anzeigen",
|
||||||
"show_large_home_carousel": "Zeige Großes Heimkarussell (Beta)",
|
"show_large_home_carousel": "Zeige große Startseiten-Übersicht (Beta)",
|
||||||
"hide_libraries": "Bibliotheken ausblenden",
|
"hide_libraries": "Bibliotheken ausblenden",
|
||||||
"select_liraries_you_want_to_hide": "Wähl die Bibliotheken aus, die du im Bibliothekstab und auf der Startseite ausblenden möchtest.",
|
"select_liraries_you_want_to_hide": "Bibliotheken auswählen die aus dem Bibliothekstab und der Startseite ausgeblendet werden sollen.",
|
||||||
"disable_haptic_feedback": "Haptisches Feedback deaktivieren",
|
"disable_haptic_feedback": "Haptisches Feedback deaktivieren",
|
||||||
"default_quality": "Standardqualität",
|
"default_quality": "Standardqualität",
|
||||||
"default_playback_speed": "Default Playback Speed",
|
"default_playback_speed": "Standard-Wiedergabegeschwindigkeit",
|
||||||
"auto_play_next_episode": "Auto-play Next Episode",
|
"auto_play_next_episode": "Automatisch nächste Episode abspielen",
|
||||||
"max_auto_play_episode_count": "Max. automatische Wiedergabe Episodenanzahl",
|
"max_auto_play_episode_count": "Maximale automatisch abzuspielende Episodenanzahl",
|
||||||
"disabled": "Deaktiviert"
|
"disabled": "Deaktiviert"
|
||||||
},
|
},
|
||||||
"downloads": {
|
"downloads": {
|
||||||
"downloads_title": "Downloads"
|
"downloads_title": "Downloads"
|
||||||
},
|
},
|
||||||
"music": {
|
"music": {
|
||||||
"title": "Music",
|
"title": "Musik",
|
||||||
"playback_title": "Playback",
|
"playback_title": "Wiedergabe",
|
||||||
"playback_description": "Configure how music is played.",
|
"playback_description": "Konfigurieren, wie Musik abgespielt wird.",
|
||||||
"prefer_downloaded": "Prefer Downloaded Songs",
|
"prefer_downloaded": "Bevorzuge heruntergeladene Titel",
|
||||||
"caching_title": "Caching",
|
"caching_title": "Caching",
|
||||||
"caching_description": "Automatically cache upcoming tracks for smoother playback.",
|
"caching_description": "Automatisches Caching anstehender Titel für bessere Wiedergabe.",
|
||||||
"lookahead_enabled": "Enable Look-Ahead Caching",
|
"lookahead_enabled": "Look-Ahead Caching aktivieren",
|
||||||
"lookahead_count": "Tracks to Pre-cache",
|
"lookahead_count": "Titel vorher in den Cache laden",
|
||||||
"max_cache_size": "Max Cache Size"
|
"max_cache_size": "Maximale Cache-Größe"
|
||||||
},
|
},
|
||||||
"plugins": {
|
"plugins": {
|
||||||
"plugins_title": "Erweiterungen",
|
"plugins_title": "Plugins",
|
||||||
"jellyseerr": {
|
"jellyseerr": {
|
||||||
"jellyseerr_warning": "Diese integration ist in einer frühen Entwicklungsphase. Erwarte Veränderungen.",
|
"jellyseerr_warning": "Diese Integration ist in einer frühen Entwicklungsphase und kann jederzeit geändert werden.",
|
||||||
"server_url": "Server Adresse",
|
"server_url": "Server URL",
|
||||||
"server_url_hint": "Beispiel: http(s)://your-host.url\n(Portnummer hinzufügen, falls erforderlich)",
|
"server_url_hint": "Beispiel: http(s)://your-host.url\n(Port hinzufügen, falls erforderlich)",
|
||||||
"server_url_placeholder": "Jellyseerr URL...",
|
"server_url_placeholder": "Seerr URL",
|
||||||
"password": "Passwort",
|
"password": "Passwort",
|
||||||
"password_placeholder": "Passwort für Jellyfin Benutzer {{username}} eingeben",
|
"password_placeholder": "Passwort für Jellyfin Benutzer {{username}} eingeben",
|
||||||
"login_button": "Anmelden",
|
"login_button": "Anmelden",
|
||||||
"total_media_requests": "Gesamtanfragen",
|
"total_media_requests": "Gesamtanfragen",
|
||||||
"movie_quota_limit": "Film-Anfragelimit",
|
"movie_quota_limit": "Film-Anfragelimit",
|
||||||
"movie_quota_days": "Film-Anfragetage",
|
"movie_quota_days": "Film-Anfragetagelimit",
|
||||||
"tv_quota_limit": "TV-Anfragelimit",
|
"tv_quota_limit": "Serien-Anfragelimit",
|
||||||
"tv_quota_days": "TV-Anfragetage",
|
"tv_quota_days": "Serien-Anfragetagelimit",
|
||||||
"reset_jellyseerr_config_button": "Setze Jellyseerr-Konfiguration zurück",
|
"reset_jellyseerr_config_button": "Seerr-Konfiguration zurücksetzen",
|
||||||
"unlimited": "Unlimitiert",
|
"unlimited": "Unlimitiert",
|
||||||
"plus_n_more": "+{{n}} more",
|
"plus_n_more": "+{{n}} weitere",
|
||||||
"order_by": {
|
"order_by": {
|
||||||
"DEFAULT": "Standard",
|
"DEFAULT": "Standard",
|
||||||
"VOTE_COUNT_AND_AVERAGE": "Stimmenanzahl und Durchschnitt",
|
"VOTE_COUNT_AND_AVERAGE": "Stimmenanzahl und Durchschnitt",
|
||||||
@@ -352,71 +352,71 @@
|
|||||||
"enable_marlin_search": "Aktiviere Marlin Search",
|
"enable_marlin_search": "Aktiviere Marlin Search",
|
||||||
"url": "URL",
|
"url": "URL",
|
||||||
"server_url_placeholder": "http(s)://domain.org:port",
|
"server_url_placeholder": "http(s)://domain.org:port",
|
||||||
"marlin_search_hint": "Gib die URL für den Marlin Server ein. Die URL sollte http oder https enthalten und optional den Port.",
|
"marlin_search_hint": "URL für den Marlin Server eingeben. Die URL sollte http oder https enthalten und optional den Port.",
|
||||||
"read_more_about_marlin": "Erfahre mehr über Marlin.",
|
"read_more_about_marlin": "Erfahre mehr über Marlin.",
|
||||||
"save_button": "Speichern",
|
"save_button": "Speichern",
|
||||||
"toasts": {
|
"toasts": {
|
||||||
"saved": "Gespeichert",
|
"saved": "Gespeichert",
|
||||||
"refreshed": "Settings refreshed from server"
|
"refreshed": "Einstellungen vom Server aktualisiert"
|
||||||
},
|
},
|
||||||
"refresh_from_server": "Refresh Settings from Server"
|
"refresh_from_server": "Einstellungen vom Server aktualisieren"
|
||||||
},
|
},
|
||||||
"streamystats": {
|
"streamystats": {
|
||||||
"enable_streamystats": "Enable Streamystats",
|
"enable_streamystats": "Streamystats aktivieren",
|
||||||
"disable_streamystats": "Disable Streamystats",
|
"disable_streamystats": "Streamystats deaktivieren",
|
||||||
"enable_search": "Use for Search",
|
"enable_search": "Zum Suchen verwenden",
|
||||||
"url": "URL",
|
"url": "URL",
|
||||||
"server_url_placeholder": "http(s)://streamystats.example.com",
|
"server_url_placeholder": "http(s)://streamystats.example.com",
|
||||||
"streamystats_search_hint": "Enter the URL for your Streamystats server. The URL should include http or https and optionally the port.",
|
"streamystats_search_hint": "URL für den Streamystats-Server eingeben.",
|
||||||
"read_more_about_streamystats": "Read More About Streamystats.",
|
"read_more_about_streamystats": "Mehr über Streamystats erfahren.",
|
||||||
"save_button": "Save",
|
"save_button": "Speichern",
|
||||||
"save": "Save",
|
"save": "Gespeichert",
|
||||||
"features_title": "Features",
|
"features_title": "Features",
|
||||||
"home_sections_title": "Home Sections",
|
"home_sections_title": "Startseitenbereiche",
|
||||||
"enable_movie_recommendations": "Movie Recommendations",
|
"enable_movie_recommendations": "Filmempfehlungen",
|
||||||
"enable_series_recommendations": "Series Recommendations",
|
"enable_series_recommendations": "Serienempfehlungen",
|
||||||
"enable_promoted_watchlists": "Promoted Watchlists",
|
"enable_promoted_watchlists": "Empfohlene Merklisten",
|
||||||
"hide_watchlists_tab": "Hide Watchlists Tab",
|
"hide_watchlists_tab": "Merklisten-Tab ausblenden",
|
||||||
"home_sections_hint": "Show personalized recommendations and promoted watchlists from Streamystats on the home page.",
|
"home_sections_hint": "Zeige personalisierte Empfehlungen und empfohlene Merklisten von Streamystats auf der Startseite.",
|
||||||
"recommended_movies": "Recommended Movies",
|
"recommended_movies": "Empfohlene Filme",
|
||||||
"recommended_series": "Recommended Series",
|
"recommended_series": "Empfohlene Serien",
|
||||||
"toasts": {
|
"toasts": {
|
||||||
"saved": "Saved",
|
"saved": "Gespeichert",
|
||||||
"refreshed": "Settings refreshed from server",
|
"refreshed": "Einstellungen vom Server aktualisiert",
|
||||||
"disabled": "Streamystats disabled"
|
"disabled": "Streamystats deaktiviert"
|
||||||
},
|
},
|
||||||
"refresh_from_server": "Refresh Settings from Server"
|
"refresh_from_server": "Einstellungen vom Server aktualisieren"
|
||||||
},
|
},
|
||||||
"kefinTweaks": {
|
"kefinTweaks": {
|
||||||
"watchlist_enabler": "Enable our Watchlist integration",
|
"watchlist_enabler": "Merklisten-Integration aktivieren",
|
||||||
"watchlist_button": "Toggle Watchlist integration"
|
"watchlist_button": "Merklisten-Integration umschalten"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"storage": {
|
"storage": {
|
||||||
"storage_title": "Speicher",
|
"storage_title": "Speicher",
|
||||||
"app_usage": "App {{usedSpace}}%",
|
"app_usage": "App {{usedSpace}}%",
|
||||||
"device_usage": "Gerät {{availableSpace}}%",
|
"device_usage": "Gerät {{availableSpace}}%",
|
||||||
"size_used": "{{used}} von {{total}} benutzt",
|
"size_used": "{{used}} von {{total}} genutzt",
|
||||||
"delete_all_downloaded_files": "Alle Downloads löschen",
|
"delete_all_downloaded_files": "Alle heruntergeladenen Dateien löschen",
|
||||||
"music_cache_title": "Music Cache",
|
"music_cache_title": "Musik-Cache",
|
||||||
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
"music_cache_description": "Beim Anhören Titel automatisch in den Cache laden um bessere Wiedergabe und Offline-Wiedergabe zu ermöglichen",
|
||||||
"enable_music_cache": "Enable Music Cache",
|
"enable_music_cache": "Musik-Cache aktivieren",
|
||||||
"clear_music_cache": "Clear Music Cache",
|
"clear_music_cache": "Musik-Cache leeren",
|
||||||
"music_cache_size": "{{size}} cached",
|
"music_cache_size": "{{size}} gechached",
|
||||||
"music_cache_cleared": "Music cache cleared",
|
"music_cache_cleared": "Musik-Cache geleert",
|
||||||
"delete_all_downloaded_songs": "Delete All Downloaded Songs",
|
"delete_all_downloaded_songs": "Alle heruntergeladenen Titel löschen",
|
||||||
"downloaded_songs_size": "{{size}} downloaded",
|
"downloaded_songs_size": "{{size}} heruntergeladen",
|
||||||
"downloaded_songs_deleted": "Downloaded songs deleted"
|
"downloaded_songs_deleted": "Heruntergeladene Titel gelöscht"
|
||||||
},
|
},
|
||||||
"intro": {
|
"intro": {
|
||||||
"title": "Intro ",
|
"title": "Einführung",
|
||||||
"show_intro": "Show intro",
|
"show_intro": "Einführung anzeigen",
|
||||||
"reset_intro": "Reset intro"
|
"reset_intro": "Einführung zurücksetzen"
|
||||||
},
|
},
|
||||||
"logs": {
|
"logs": {
|
||||||
"logs_title": "Logs",
|
"logs_title": "Logs",
|
||||||
"export_logs": "Export logs",
|
"export_logs": "Logs exportieren",
|
||||||
"click_for_more_info": "Click for more info",
|
"click_for_more_info": "Für mehr Informationen klicken",
|
||||||
"level": "Level",
|
"level": "Level",
|
||||||
"no_logs_available": "Keine Logs verfügbar",
|
"no_logs_available": "Keine Logs verfügbar",
|
||||||
"delete_all_logs": "Alle Logs löschen"
|
"delete_all_logs": "Alle Logs löschen"
|
||||||
@@ -438,21 +438,21 @@
|
|||||||
},
|
},
|
||||||
"downloads": {
|
"downloads": {
|
||||||
"downloads_title": "Downloads",
|
"downloads_title": "Downloads",
|
||||||
"tvseries": "TV-Serien",
|
"tvseries": "Serien",
|
||||||
"movies": "Filme",
|
"movies": "Filme",
|
||||||
"queue": "Warteschlange",
|
"queue": "Warteschlange",
|
||||||
"other_media": "Andere Medien",
|
"other_media": "Andere Medien",
|
||||||
"queue_hint": "Warteschlange und aktive Downloads gehen verloren bei App-Neustart",
|
"queue_hint": "Warteschlange und aktive Downloads gehen verloren wenn die App neu gestartet wird",
|
||||||
"no_items_in_queue": "Keine Elemente in der Warteschlange",
|
"no_items_in_queue": "Keine Elemente in der Warteschlange",
|
||||||
"no_downloaded_items": "Keine heruntergeladenen Elemente",
|
"no_downloaded_items": "Keine heruntergeladenen Elemente",
|
||||||
"delete_all_movies_button": "Alle Filme löschen",
|
"delete_all_movies_button": "Alle Filme löschen",
|
||||||
"delete_all_tvseries_button": "Alle TV-Serien löschen",
|
"delete_all_tvseries_button": "Alle Serien löschen",
|
||||||
"delete_all_button": "Alles löschen",
|
"delete_all_button": "Alles löschen",
|
||||||
"delete_all_other_media_button": "Andere Medien löschen",
|
"delete_all_other_media_button": "Alle anderen Medien löschen",
|
||||||
"active_download": "Aktiver Download",
|
"active_download": "Aktiver Download",
|
||||||
"no_active_downloads": "Keine aktiven Downloads",
|
"no_active_downloads": "Keine aktiven Downloads",
|
||||||
"active_downloads": "Aktive Downloads",
|
"active_downloads": "Aktive Downloads",
|
||||||
"new_app_version_requires_re_download": "Die neue App-Version erfordert das erneute Herunterladen.",
|
"new_app_version_requires_re_download": "Neue App-Version erfordert erneutes Herunterladen",
|
||||||
"new_app_version_requires_re_download_description": "Die neue App-Version erfordert das erneute Herunterladen von Filmen und Serien. Bitte lösche alle heruntergeladenen Elemente und starte den Download erneut.",
|
"new_app_version_requires_re_download_description": "Die neue App-Version erfordert das erneute Herunterladen von Filmen und Serien. Bitte lösche alle heruntergeladenen Elemente und starte den Download erneut.",
|
||||||
"back": "Zurück",
|
"back": "Zurück",
|
||||||
"delete": "Löschen",
|
"delete": "Löschen",
|
||||||
@@ -463,8 +463,8 @@
|
|||||||
"you_are_not_allowed_to_download_files": "Du hast keine Berechtigung, Dateien herunterzuladen",
|
"you_are_not_allowed_to_download_files": "Du hast keine Berechtigung, Dateien herunterzuladen",
|
||||||
"deleted_all_movies_successfully": "Alle Filme erfolgreich gelöscht!",
|
"deleted_all_movies_successfully": "Alle Filme erfolgreich gelöscht!",
|
||||||
"failed_to_delete_all_movies": "Fehler beim Löschen aller Filme",
|
"failed_to_delete_all_movies": "Fehler beim Löschen aller Filme",
|
||||||
"deleted_all_tvseries_successfully": "Alle TV-Serien erfolgreich gelöscht!",
|
"deleted_all_tvseries_successfully": "Alle Serien erfolgreich gelöscht!",
|
||||||
"failed_to_delete_all_tvseries": "Fehler beim Löschen aller TV-Serien",
|
"failed_to_delete_all_tvseries": "Fehler beim Löschen aller Serien",
|
||||||
"deleted_media_successfully": "Andere Medien erfolgreich gelöscht!",
|
"deleted_media_successfully": "Andere Medien erfolgreich gelöscht!",
|
||||||
"failed_to_delete_media": "Fehler beim Löschen anderer Medien",
|
"failed_to_delete_media": "Fehler beim Löschen anderer Medien",
|
||||||
"download_deleted": "Download gelöscht",
|
"download_deleted": "Download gelöscht",
|
||||||
@@ -486,7 +486,7 @@
|
|||||||
"all_files_folders_and_jobs_deleted_successfully": "Alle Dateien, Ordner und Jobs erfolgreich gelöscht",
|
"all_files_folders_and_jobs_deleted_successfully": "Alle Dateien, Ordner und Jobs erfolgreich gelöscht",
|
||||||
"failed_to_clean_cache_directory": "Fehler beim Bereinigen des Cache-Verzeichnisses",
|
"failed_to_clean_cache_directory": "Fehler beim Bereinigen des Cache-Verzeichnisses",
|
||||||
"could_not_get_download_url_for_item": "Download-URL für {{itemName}} konnte nicht geladen werden",
|
"could_not_get_download_url_for_item": "Download-URL für {{itemName}} konnte nicht geladen werden",
|
||||||
"go_to_downloads": "Gehe zu den Downloads",
|
"go_to_downloads": "Zu Downloads gehen",
|
||||||
"file_deleted": "{{item}} gelöscht"
|
"file_deleted": "{{item}} gelöscht"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -499,18 +499,18 @@
|
|||||||
"subtitle": "Untertitel",
|
"subtitle": "Untertitel",
|
||||||
"play": "Abspielen",
|
"play": "Abspielen",
|
||||||
"none": "Keine",
|
"none": "Keine",
|
||||||
"track": "Track",
|
"track": "Spur",
|
||||||
"cancel": "Cancel",
|
"cancel": "Abbrechen",
|
||||||
"delete": "Delete",
|
"delete": "Löschen",
|
||||||
"ok": "OK",
|
"ok": "OK",
|
||||||
"remove": "Remove",
|
"remove": "Entfernen",
|
||||||
"next": "Next",
|
"next": "Weiter",
|
||||||
"back": "Back",
|
"back": "Zurück",
|
||||||
"continue": "Continue",
|
"continue": "Fortsetzen",
|
||||||
"verifying": "Verifying..."
|
"verifying": "Verifiziere..."
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
"search": "Suche...",
|
"search": "Suchen...",
|
||||||
"x_items": "{{count}} Elemente",
|
"x_items": "{{count}} Elemente",
|
||||||
"library": "Bibliothek",
|
"library": "Bibliothek",
|
||||||
"discover": "Entdecken",
|
"discover": "Entdecken",
|
||||||
@@ -521,33 +521,33 @@
|
|||||||
"episodes": "Episoden",
|
"episodes": "Episoden",
|
||||||
"collections": "Sammlungen",
|
"collections": "Sammlungen",
|
||||||
"actors": "Schauspieler",
|
"actors": "Schauspieler",
|
||||||
"artists": "Artists",
|
"artists": "Künstler",
|
||||||
"albums": "Albums",
|
"albums": "Alben",
|
||||||
"songs": "Songs",
|
"songs": "Titel",
|
||||||
"playlists": "Playlists",
|
"playlists": "Playlists",
|
||||||
"request_movies": "Film anfragen",
|
"request_movies": "Film anfragen",
|
||||||
"request_series": "Serie anfragen",
|
"request_series": "Serie anfragen",
|
||||||
"recently_added": "Kürzlich hinzugefügt",
|
"recently_added": "Kürzlich hinzugefügt",
|
||||||
"recent_requests": "Kürzlich angefragt",
|
"recent_requests": "Kürzlich angefragt",
|
||||||
"plex_watchlist": "Plex Watchlist",
|
"plex_watchlist": "Plex Merkliste",
|
||||||
"trending": "In den Trends",
|
"trending": "Beliebt",
|
||||||
"popular_movies": "Beliebte Filme",
|
"popular_movies": "Beliebte Filme",
|
||||||
"movie_genres": "Film-Genres",
|
"movie_genres": "Film-Genres",
|
||||||
"upcoming_movies": "Kommende Filme",
|
"upcoming_movies": "Kommende Filme",
|
||||||
"studios": "Studios",
|
"studios": "Studios",
|
||||||
"popular_tv": "Beliebte TV-Serien",
|
"popular_tv": "Beliebte Serien",
|
||||||
"tv_genres": "TV-Serien-Genres",
|
"tv_genres": "Serien-Genres",
|
||||||
"upcoming_tv": "Kommende TV-Serien",
|
"upcoming_tv": "Kommende Serien",
|
||||||
"networks": "Netzwerke",
|
"networks": "Sender",
|
||||||
"tmdb_movie_keyword": "TMDB Film-Schlüsselwort",
|
"tmdb_movie_keyword": "TMDB Film-Schlüsselwort",
|
||||||
"tmdb_movie_genre": "TMDB Film-Genre",
|
"tmdb_movie_genre": "TMDB Film-Genre",
|
||||||
"tmdb_tv_keyword": "TMDB TV-Serien-Schlüsselwort",
|
"tmdb_tv_keyword": "TMDB Serien-Schlüsselwort",
|
||||||
"tmdb_tv_genre": "TMDB TV-Serien-Genre",
|
"tmdb_tv_genre": "TMDB Serien-Genre",
|
||||||
"tmdb_search": "TMDB Suche",
|
"tmdb_search": "TMDB Suche",
|
||||||
"tmdb_studio": "TMDB Studio",
|
"tmdb_studio": "TMDB Studio",
|
||||||
"tmdb_network": "TMDB Netzwerk",
|
"tmdb_network": "TMDB Netzwerk",
|
||||||
"tmdb_movie_streaming_services": "TMDB Film-Streaming-Dienste",
|
"tmdb_movie_streaming_services": "TMDB Film-Streaming-Dienste",
|
||||||
"tmdb_tv_streaming_services": "TMDB TV-Serien-Streaming-Dienste"
|
"tmdb_tv_streaming_services": "TMDB Serien-Streaming-Dienste"
|
||||||
},
|
},
|
||||||
"library": {
|
"library": {
|
||||||
"no_results": "Keine Ergebnisse",
|
"no_results": "Keine Ergebnisse",
|
||||||
@@ -572,7 +572,7 @@
|
|||||||
"genres": "Genres",
|
"genres": "Genres",
|
||||||
"years": "Jahre",
|
"years": "Jahre",
|
||||||
"sort_by": "Sortieren nach",
|
"sort_by": "Sortieren nach",
|
||||||
"filter_by": "Filter By",
|
"filter_by": "Filtern nach",
|
||||||
"sort_order": "Sortierreihenfolge",
|
"sort_order": "Sortierreihenfolge",
|
||||||
"tags": "Tags"
|
"tags": "Tags"
|
||||||
}
|
}
|
||||||
@@ -585,7 +585,7 @@
|
|||||||
"boxsets": "Boxsets",
|
"boxsets": "Boxsets",
|
||||||
"playlists": "Wiedergabelisten",
|
"playlists": "Wiedergabelisten",
|
||||||
"noDataTitle": "Noch keine Favoriten",
|
"noDataTitle": "Noch keine Favoriten",
|
||||||
"noData": "Markiere Elemente als Favoriten, damit sie hier für einen schnellen Zugriff angezeigt werden."
|
"noData": "Elemente als Favoriten markieren, um sie hier anzuzeigen."
|
||||||
},
|
},
|
||||||
"custom_links": {
|
"custom_links": {
|
||||||
"no_links": "Keine Links"
|
"no_links": "Keine Links"
|
||||||
@@ -593,7 +593,7 @@
|
|||||||
"player": {
|
"player": {
|
||||||
"error": "Fehler",
|
"error": "Fehler",
|
||||||
"failed_to_get_stream_url": "Fehler beim Abrufen der Stream-URL",
|
"failed_to_get_stream_url": "Fehler beim Abrufen der Stream-URL",
|
||||||
"an_error_occured_while_playing_the_video": "Ein Fehler ist beim Abspielen des Videos aufgetreten. Überprüf die Logs in den Einstellungen.",
|
"an_error_occured_while_playing_the_video": "Ein Fehler ist beim Abspielen des Videos aufgetreten. Logs in den Einstellungen überprüfen.",
|
||||||
"client_error": "Client-Fehler",
|
"client_error": "Client-Fehler",
|
||||||
"could_not_create_stream_for_chromecast": "Konnte keinen Stream für Chromecast erstellen",
|
"could_not_create_stream_for_chromecast": "Konnte keinen Stream für Chromecast erstellen",
|
||||||
"message_from_server": "Nachricht vom Server: {{message}}",
|
"message_from_server": "Nachricht vom Server: {{message}}",
|
||||||
@@ -602,17 +602,17 @@
|
|||||||
"audio_tracks": "Audiospuren:",
|
"audio_tracks": "Audiospuren:",
|
||||||
"playback_state": "Wiedergabestatus:",
|
"playback_state": "Wiedergabestatus:",
|
||||||
"index": "Index:",
|
"index": "Index:",
|
||||||
"continue_watching": "Weiterschauen",
|
"continue_watching": "Fortsetzen",
|
||||||
"go_back": "Zurück",
|
"go_back": "Zurück",
|
||||||
"downloaded_file_title": "Diese Datei wurde heruntergeladen",
|
"downloaded_file_title": "Diese Datei wurde bereits heruntergeladen",
|
||||||
"downloaded_file_message": "Möchten Sie die heruntergeladene Datei abspielen?",
|
"downloaded_file_message": "Heruntergeladene Datei abspielen?",
|
||||||
"downloaded_file_yes": "Ja",
|
"downloaded_file_yes": "Ja",
|
||||||
"downloaded_file_no": "Nein",
|
"downloaded_file_no": "Nein",
|
||||||
"downloaded_file_cancel": "Abbrechen"
|
"downloaded_file_cancel": "Abbrechen"
|
||||||
},
|
},
|
||||||
"item_card": {
|
"item_card": {
|
||||||
"next_up": "Als Nächstes",
|
"next_up": "Als Nächstes",
|
||||||
"no_items_to_display": "Keine Elemente zum Anzeigen",
|
"no_items_to_display": "Keine Elemente",
|
||||||
"cast_and_crew": "Besetzung und Crew",
|
"cast_and_crew": "Besetzung und Crew",
|
||||||
"series": "Serien",
|
"series": "Serien",
|
||||||
"seasons": "Staffeln",
|
"seasons": "Staffeln",
|
||||||
@@ -630,7 +630,7 @@
|
|||||||
"subtitles": "Untertitel",
|
"subtitles": "Untertitel",
|
||||||
"show_more": "Mehr anzeigen",
|
"show_more": "Mehr anzeigen",
|
||||||
"show_less": "Weniger anzeigen",
|
"show_less": "Weniger anzeigen",
|
||||||
"appeared_in": "Erschienen in",
|
"appeared_in": "Erschien in",
|
||||||
"could_not_load_item": "Konnte Element nicht laden",
|
"could_not_load_item": "Konnte Element nicht laden",
|
||||||
"none": "Keine",
|
"none": "Keine",
|
||||||
"download": {
|
"download": {
|
||||||
@@ -639,13 +639,13 @@
|
|||||||
"download_episode": "Episode herunterladen",
|
"download_episode": "Episode herunterladen",
|
||||||
"download_movie": "Film herunterladen",
|
"download_movie": "Film herunterladen",
|
||||||
"download_x_item": "{{item_count}} Elemente herunterladen",
|
"download_x_item": "{{item_count}} Elemente herunterladen",
|
||||||
"download_unwatched_only": "Nur unbeobachtete",
|
"download_unwatched_only": "Nur Ungesehene",
|
||||||
"download_button": "Herunterladen"
|
"download_button": "Herunterladen"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"live_tv": {
|
"live_tv": {
|
||||||
"next": "Nächster",
|
"next": "Nächste",
|
||||||
"previous": "Vorheriger",
|
"previous": "Vorherige",
|
||||||
"coming_soon": "Demnächst",
|
"coming_soon": "Demnächst",
|
||||||
"on_now": "Jetzt",
|
"on_now": "Jetzt",
|
||||||
"shows": "Serien",
|
"shows": "Serien",
|
||||||
@@ -658,10 +658,10 @@
|
|||||||
"confirm": "Bestätigen",
|
"confirm": "Bestätigen",
|
||||||
"cancel": "Abbrechen",
|
"cancel": "Abbrechen",
|
||||||
"yes": "Ja",
|
"yes": "Ja",
|
||||||
"whats_wrong": "Hast du Probleme?",
|
"whats_wrong": "Was stimmt nicht?",
|
||||||
"issue_type": "Fehlerart",
|
"issue_type": "Art des Problems",
|
||||||
"select_an_issue": "Wähle einen Fehlerart aus",
|
"select_an_issue": "Wähle die Art des Problems aus",
|
||||||
"types": "Arten",
|
"types": "Problem-Arten",
|
||||||
"describe_the_issue": "(optional) Beschreibe das Problem",
|
"describe_the_issue": "(optional) Beschreibe das Problem",
|
||||||
"submit_button": "Absenden",
|
"submit_button": "Absenden",
|
||||||
"report_issue_button": "Fehler melden",
|
"report_issue_button": "Fehler melden",
|
||||||
@@ -671,7 +671,7 @@
|
|||||||
"cast": "Besetzung",
|
"cast": "Besetzung",
|
||||||
"details": "Details",
|
"details": "Details",
|
||||||
"status": "Status",
|
"status": "Status",
|
||||||
"original_title": "Original Titel",
|
"original_title": "Originaltitel",
|
||||||
"series_type": "Serien Typ",
|
"series_type": "Serien Typ",
|
||||||
"release_dates": "Veröffentlichungsdaten",
|
"release_dates": "Veröffentlichungsdaten",
|
||||||
"first_air_date": "Erstausstrahlungsdatum",
|
"first_air_date": "Erstausstrahlungsdatum",
|
||||||
@@ -687,10 +687,10 @@
|
|||||||
"request_as": "Anfragen als",
|
"request_as": "Anfragen als",
|
||||||
"tags": "Tags",
|
"tags": "Tags",
|
||||||
"quality_profile": "Qualitätsprofil",
|
"quality_profile": "Qualitätsprofil",
|
||||||
"root_folder": "Root-Ordner",
|
"root_folder": "Stammverzeichnis",
|
||||||
"season_all": "Season (all)",
|
"season_all": "Staffeln (alle)",
|
||||||
"season_number": "Staffel {{season_number}}",
|
"season_number": "Staffel {{season_number}}",
|
||||||
"number_episodes": "{{episode_number}} Folgen",
|
"number_episodes": "{{episode_number}} Episoden",
|
||||||
"born": "Geboren",
|
"born": "Geboren",
|
||||||
"appearances": "Auftritte",
|
"appearances": "Auftritte",
|
||||||
"approve": "Genehmigen",
|
"approve": "Genehmigen",
|
||||||
@@ -698,9 +698,9 @@
|
|||||||
"requested_by": "Angefragt von {{user}}",
|
"requested_by": "Angefragt von {{user}}",
|
||||||
"unknown_user": "Unbekannter Nutzer",
|
"unknown_user": "Unbekannter Nutzer",
|
||||||
"toasts": {
|
"toasts": {
|
||||||
"jellyseer_does_not_meet_requirements": "Jellyseerr Server erfüllt nicht die Anforderungsversion. Bitte aktualisiere deinen Jellyseerr Server auf mindestens 2.0.0",
|
"jellyseer_does_not_meet_requirements": "Seerr-Server erfüllt nicht die minimalen Versionsanforderungen. Bitte den Seerr-Server auf mindestens 2.0.0 aktualisieren.",
|
||||||
"jellyseerr_test_failed": "Jellyseerr-Test fehlgeschlagen. Bitte versuche es erneut.",
|
"jellyseerr_test_failed": "Seerr-Test fehlgeschlagen. Bitte erneut versuchen.",
|
||||||
"failed_to_test_jellyseerr_server_url": "Fehler beim Testen der Jellyseerr-Server-URL",
|
"failed_to_test_jellyseerr_server_url": "Fehler beim Test der Seerr-Server-URL",
|
||||||
"issue_submitted": "Problem eingereicht!",
|
"issue_submitted": "Problem eingereicht!",
|
||||||
"requested_item": "{{item}} angefragt!",
|
"requested_item": "{{item}} angefragt!",
|
||||||
"you_dont_have_permission_to_request": "Du hast keine Berechtigung Anfragen zu stellen",
|
"you_dont_have_permission_to_request": "Du hast keine Berechtigung Anfragen zu stellen",
|
||||||
@@ -715,131 +715,131 @@
|
|||||||
"home": "Startseite",
|
"home": "Startseite",
|
||||||
"search": "Suche",
|
"search": "Suche",
|
||||||
"library": "Bibliothek",
|
"library": "Bibliothek",
|
||||||
"custom_links": "Benutzerdefinierte Links",
|
"custom_links": "Links",
|
||||||
"favorites": "Favoriten"
|
"favorites": "Favoriten"
|
||||||
},
|
},
|
||||||
"music": {
|
"music": {
|
||||||
"title": "Music",
|
"title": "Musik",
|
||||||
"tabs": {
|
"tabs": {
|
||||||
"suggestions": "Suggestions",
|
"suggestions": "Vorschläge",
|
||||||
"albums": "Albums",
|
"albums": "Alben",
|
||||||
"artists": "Artists",
|
"artists": "Künstler",
|
||||||
"playlists": "Playlists",
|
"playlists": "Playlists",
|
||||||
"tracks": "tracks"
|
"tracks": "Titel"
|
||||||
},
|
},
|
||||||
"filters": {
|
"filters": {
|
||||||
"all": "All"
|
"all": "Alle"
|
||||||
},
|
},
|
||||||
"recently_added": "Recently Added",
|
"recently_added": "Kürzlich hinzugefügt",
|
||||||
"recently_played": "Recently Played",
|
"recently_played": "Vor kurzem gehört",
|
||||||
"frequently_played": "Frequently Played",
|
"frequently_played": "Oft gehört",
|
||||||
"explore": "Explore",
|
"explore": "Entdecken",
|
||||||
"top_tracks": "Top Tracks",
|
"top_tracks": "Top-Titel",
|
||||||
"play": "Play",
|
"play": "Abspielen",
|
||||||
"shuffle": "Shuffle",
|
"shuffle": "Shuffle",
|
||||||
"play_top_tracks": "Play Top Tracks",
|
"play_top_tracks": "Top-Tracks abspielen",
|
||||||
"no_suggestions": "No suggestions available",
|
"no_suggestions": "Keine Vorschläge verfügbar",
|
||||||
"no_albums": "No albums found",
|
"no_albums": "Keine Alben gefunden",
|
||||||
"no_artists": "No artists found",
|
"no_artists": "Keine Künstler gefunden",
|
||||||
"no_playlists": "No playlists found",
|
"no_playlists": "Keine Playlists gefunden",
|
||||||
"album_not_found": "Album not found",
|
"album_not_found": "Album nicht gefunden",
|
||||||
"artist_not_found": "Artist not found",
|
"artist_not_found": "Künstler nicht gefunden",
|
||||||
"playlist_not_found": "Playlist not found",
|
"playlist_not_found": "Playlist nicht gefunden",
|
||||||
"track_options": {
|
"track_options": {
|
||||||
"play_next": "Play Next",
|
"play_next": "Als Nächstes wiedergeben",
|
||||||
"add_to_queue": "Add to Queue",
|
"add_to_queue": "Zur Warteschlange hinzufügen",
|
||||||
"add_to_playlist": "Add to Playlist",
|
"add_to_playlist": "Zur Playlist hinzufügen",
|
||||||
"download": "Download",
|
"download": "Herunterladen",
|
||||||
"downloaded": "Downloaded",
|
"downloaded": "Heruntergeladen",
|
||||||
"downloading": "Downloading...",
|
"downloading": "Wird heruntergeladen...",
|
||||||
"cached": "Cached",
|
"cached": "Gecached",
|
||||||
"delete_download": "Delete Download",
|
"delete_download": "Download löschen",
|
||||||
"delete_cache": "Remove from Cache",
|
"delete_cache": "Aus dem Cache löschen",
|
||||||
"go_to_artist": "Go to Artist",
|
"go_to_artist": "Zum Künstler gehen",
|
||||||
"go_to_album": "Go to Album",
|
"go_to_album": "Zum Album gehen",
|
||||||
"add_to_favorites": "Add to Favorites",
|
"add_to_favorites": "Zu Favoriten hinzufügen",
|
||||||
"remove_from_favorites": "Remove from Favorites",
|
"remove_from_favorites": "Aus Favoriten entfernen",
|
||||||
"remove_from_playlist": "Remove from Playlist"
|
"remove_from_playlist": "Aus Playlist entfernen"
|
||||||
},
|
},
|
||||||
"playlists": {
|
"playlists": {
|
||||||
"create_playlist": "Create Playlist",
|
"create_playlist": "Playlist erstellen",
|
||||||
"playlist_name": "Playlist Name",
|
"playlist_name": "Playlist Name",
|
||||||
"enter_name": "Enter playlist name",
|
"enter_name": "Playlist Name eingeben",
|
||||||
"create": "Create",
|
"create": "Erstellen",
|
||||||
"search_playlists": "Search playlists...",
|
"search_playlists": "Playlisten durchsuchen...",
|
||||||
"added_to": "Added to {{name}}",
|
"added_to": "Zu {{name}} hinzugefügt",
|
||||||
"added": "Added to playlist",
|
"added": "Zur Playlist hinzugefügt",
|
||||||
"removed_from": "Removed from {{name}}",
|
"removed_from": "Aus {{name}} entfernt",
|
||||||
"removed": "Removed from playlist",
|
"removed": "Aus Playlist entfernt",
|
||||||
"created": "Playlist created",
|
"created": "Playlist erstellt",
|
||||||
"create_new": "Create New Playlist",
|
"create_new": "Neue Playlist erstellen",
|
||||||
"failed_to_add": "Failed to add to playlist",
|
"failed_to_add": "Fehler beim Hinzufügen zur Playlist",
|
||||||
"failed_to_remove": "Failed to remove from playlist",
|
"failed_to_remove": "Fehler beim Entfernen aus der Playlist",
|
||||||
"failed_to_create": "Failed to create playlist",
|
"failed_to_create": "Fehler beim Erstellen der Playlist",
|
||||||
"delete_playlist": "Delete Playlist",
|
"delete_playlist": "Playlist löschen",
|
||||||
"delete_confirm": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.",
|
"delete_confirm": "Bist Du sicher, dass Du \"{{name}}\" löschen möchtest? Das kann nicht rückgängig gemacht werden.",
|
||||||
"deleted": "Playlist deleted",
|
"deleted": "Playlist gelöscht",
|
||||||
"failed_to_delete": "Failed to delete playlist"
|
"failed_to_delete": "Fehler beim Löschen der Playlist"
|
||||||
},
|
},
|
||||||
"sort": {
|
"sort": {
|
||||||
"title": "Sort By",
|
"title": "Sortieren nach",
|
||||||
"alphabetical": "Alphabetical",
|
"alphabetical": "Alphabetisch",
|
||||||
"date_created": "Date Created"
|
"date_created": "Erstellungsdatum"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"watchlists": {
|
"watchlists": {
|
||||||
"title": "Watchlists",
|
"title": "Merklisten",
|
||||||
"my_watchlists": "My Watchlists",
|
"my_watchlists": "Meine Merklisten",
|
||||||
"public_watchlists": "Public Watchlists",
|
"public_watchlists": "Öffentliche Merklisten",
|
||||||
"create_title": "Create Watchlist",
|
"create_title": "Merkliste erstellen",
|
||||||
"edit_title": "Edit Watchlist",
|
"edit_title": "Merkliste bearbeiten",
|
||||||
"create_button": "Create Watchlist",
|
"create_button": "Merkliste erstellen",
|
||||||
"save_button": "Save Changes",
|
"save_button": "Änderungen speichern",
|
||||||
"delete_button": "Delete",
|
"delete_button": "Löschen",
|
||||||
"remove_button": "Remove",
|
"remove_button": "Entfernen",
|
||||||
"cancel_button": "Cancel",
|
"cancel_button": "Abbrechen",
|
||||||
"name_label": "Name",
|
"name_label": "Name",
|
||||||
"name_placeholder": "Enter watchlist name",
|
"name_placeholder": "Merklistenname eingeben",
|
||||||
"description_label": "Description",
|
"description_label": "Beschreibung",
|
||||||
"description_placeholder": "Enter description (optional)",
|
"description_placeholder": "Beschreibung eingeben (optional)",
|
||||||
"is_public_label": "Public Watchlist",
|
"is_public_label": "Öffentliche Merkliste",
|
||||||
"is_public_description": "Allow others to view this watchlist",
|
"is_public_description": "Anderen erlauben diese Merkliste anzusehen",
|
||||||
"allowed_type_label": "Content Type",
|
"allowed_type_label": "Inhaltstyp",
|
||||||
"sort_order_label": "Default Sort Order",
|
"sort_order_label": "Standard-Sortierreihenfolge",
|
||||||
"empty_title": "No Watchlists",
|
"empty_title": "Keine Merklisten",
|
||||||
"empty_description": "Create your first watchlist to start organizing your media",
|
"empty_description": "Erstelle deine erste Merkliste um deine Medien zu organisieren",
|
||||||
"empty_watchlist": "This watchlist is empty",
|
"empty_watchlist": "Diese Merkliste ist leer",
|
||||||
"empty_watchlist_hint": "Add items from your library to this watchlist",
|
"empty_watchlist_hint": "Füge Elemente aus deiner Bibliothek zu dieser Merkliste hinzu",
|
||||||
"not_configured_title": "Streamystats Not Configured",
|
"not_configured_title": "Streamystats nicht konfiguriert",
|
||||||
"not_configured_description": "Configure Streamystats in settings to use watchlists",
|
"not_configured_description": "Streamystats in den Einstellungen konfigurieren, um Merklisten zu verwenden",
|
||||||
"go_to_settings": "Go to Settings",
|
"go_to_settings": "Gehe zu Einstellungen",
|
||||||
"add_to_watchlist": "Add to Watchlist",
|
"add_to_watchlist": "Zur Merkliste hinzufügen",
|
||||||
"remove_from_watchlist": "Remove from Watchlist",
|
"remove_from_watchlist": "Von Merkliste entfernen",
|
||||||
"select_watchlist": "Select Watchlist",
|
"select_watchlist": "Merkliste auswählen",
|
||||||
"create_new": "Create New Watchlist",
|
"create_new": "Neue Merkliste erstellen",
|
||||||
"item": "item",
|
"item": "Element",
|
||||||
"items": "items",
|
"items": "Elemente",
|
||||||
"public": "Public",
|
"public": "Öffentlich",
|
||||||
"private": "Private",
|
"private": "Privat",
|
||||||
"you": "You",
|
"you": "Du",
|
||||||
"by_owner": "By another user",
|
"by_owner": "Von einem anderen Benutzer",
|
||||||
"not_found": "Watchlist not found",
|
"not_found": "Merkliste nicht gefunden",
|
||||||
"delete_confirm_title": "Delete Watchlist",
|
"delete_confirm_title": "Merkliste löschen",
|
||||||
"delete_confirm_message": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.",
|
"delete_confirm_message": "Bist Du sicher, dass Du \"{{name}}\" löschen möchtest? Das kann nicht rückgängig gemacht werden.",
|
||||||
"remove_item_title": "Remove from Watchlist",
|
"remove_item_title": "Von Merkliste entfernen",
|
||||||
"remove_item_message": "Remove \"{{name}}\" from this watchlist?",
|
"remove_item_message": "\"{{name}}\" von dieser Merkliste entfernen?",
|
||||||
"loading": "Loading watchlists...",
|
"loading": "Lade Merklisten...",
|
||||||
"no_compatible_watchlists": "No compatible watchlists",
|
"no_compatible_watchlists": "Keine kompatiblen Merklisten",
|
||||||
"create_one_first": "Create a watchlist that accepts this content type"
|
"create_one_first": "Erstelle eine Merkliste, welche diesen Inhaltstyp akzeptiert"
|
||||||
},
|
},
|
||||||
"playback_speed": {
|
"playback_speed": {
|
||||||
"title": "Playback Speed",
|
"title": "Wiedergabegeschwindigkeit",
|
||||||
"apply_to": "Apply To",
|
"apply_to": "Anwenden auf",
|
||||||
"speed": "Speed",
|
"speed": "Geschwindigkeit",
|
||||||
"scope": {
|
"scope": {
|
||||||
"media": "This media only",
|
"media": "Nur hier",
|
||||||
"show": "This show",
|
"show": "Nur diese Serie",
|
||||||
"all": "All media (default)"
|
"all": "Alle (Standard)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
{
|
{
|
||||||
"login": {
|
"login": {
|
||||||
"username_required": "Username Is Required",
|
"username_required": "Username is required",
|
||||||
"error_title": "Error",
|
"error_title": "Error",
|
||||||
"login_title": "Log In",
|
"login_title": "Log in",
|
||||||
"login_to_title": "Log in to",
|
"login_to_title": "Log in to",
|
||||||
"username_placeholder": "Username",
|
"username_placeholder": "Username",
|
||||||
"password_placeholder": "Password",
|
"password_placeholder": "Password",
|
||||||
"login_button": "Log In",
|
"login_button": "Log in",
|
||||||
"quick_connect": "Quick Connect",
|
"quick_connect": "Quick Connect",
|
||||||
"enter_code_to_login": "Enter code {{code}} to login",
|
"enter_code_to_login": "Enter code {{code}} to log in",
|
||||||
"failed_to_initiate_quick_connect": "Failed to initiate Quick Connect",
|
"failed_to_initiate_quick_connect": "Failed to initiate Quick Connect",
|
||||||
"got_it": "Got It",
|
"got_it": "Got It",
|
||||||
"connection_failed": "Connection Failed",
|
"connection_failed": "Connection Failed",
|
||||||
"could_not_connect_to_server": "Could not connect to the server. Please check the URL and your network connection.",
|
"could_not_connect_to_server": "Could not connect to the server. Please check the URL and your network connection.",
|
||||||
"an_unexpected_error_occured": "An Unexpected Error Occurred",
|
"an_unexpected_error_occured": "An unexpected error occurred",
|
||||||
"change_server": "Change Server",
|
"change_server": "Change Server",
|
||||||
"invalid_username_or_password": "Invalid Username or Password",
|
"invalid_username_or_password": "Invalid Username or Password",
|
||||||
"user_does_not_have_permission_to_log_in": "User does not have permission to log in",
|
"user_does_not_have_permission_to_log_in": "User does not have permission to log in",
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
"there_is_a_server_error": "There is a server error",
|
"there_is_a_server_error": "There is a server error",
|
||||||
"an_unexpected_error_occured_did_you_enter_the_correct_url": "An unexpected error occurred. Did you enter the server URL correctly?",
|
"an_unexpected_error_occured_did_you_enter_the_correct_url": "An unexpected error occurred. Did you enter the server URL correctly?",
|
||||||
"too_old_server_text": "Unsupported Jellyfin Server Discovered",
|
"too_old_server_text": "Unsupported Jellyfin Server Discovered",
|
||||||
"too_old_server_description": "Please update Jellyfin to the latest version"
|
"too_old_server_description": "Please update Jellyfin to the latest version."
|
||||||
},
|
},
|
||||||
"server": {
|
"server": {
|
||||||
"enter_url_to_jellyfin_server": "Enter the URL to your Jellyfin server",
|
"enter_url_to_jellyfin_server": "Enter the URL to your Jellyfin server",
|
||||||
@@ -88,27 +88,27 @@
|
|||||||
"continue_watching": "Continue Watching",
|
"continue_watching": "Continue Watching",
|
||||||
"next_up": "Next Up",
|
"next_up": "Next Up",
|
||||||
"continue_and_next_up": "Continue & Next Up",
|
"continue_and_next_up": "Continue & Next Up",
|
||||||
"recently_added_in": "Recently Added in {{libraryName}}",
|
"recently_added_in": "Recently added in {{libraryName}}",
|
||||||
"suggested_movies": "Suggested Movies",
|
"suggested_movies": "Suggested Movies",
|
||||||
"suggested_episodes": "Suggested Episodes",
|
"suggested_episodes": "Suggested Episodes",
|
||||||
"intro": {
|
"intro": {
|
||||||
"welcome_to_streamyfin": "Welcome to Streamyfin",
|
"welcome_to_streamyfin": "Welcome to Streamyfin",
|
||||||
"a_free_and_open_source_client_for_jellyfin": "A Free and Open-Source Client for Jellyfin.",
|
"a_free_and_open_source_client_for_jellyfin": "A Free and Open-Source Client for Jellyfin",
|
||||||
"features_title": "Features",
|
"features_title": "Features",
|
||||||
"features_description": "Streamyfin has a bunch of features and integrates with a wide array of software which you can find in the settings menu, these include:",
|
"features_description": "Streamyfin offers many features and integrates with a wide array of software which you can find in the settings menu, including:",
|
||||||
"jellyseerr_feature_description": "Connect to your Seerr instance and request movies directly in the app.",
|
"seerr_feature_description": "Connect to your Seerr instance and request movies directly in the app.",
|
||||||
"downloads_feature_title": "Downloads",
|
"downloads_feature_title": "Downloads",
|
||||||
"downloads_feature_description": "Download movies and tv-shows to view offline. Use either the default method or install the optimize server to download files in the background.",
|
"downloads_feature_description": "Download movies and TV shows to view offline.",
|
||||||
"chromecast_feature_description": "Cast movies and tv-shows to your Chromecast devices.",
|
"chromecast_feature_description": "Cast movies and TV shows to your Chromecast devices.",
|
||||||
"centralised_settings_plugin_title": "Centralised Settings Plugin",
|
"centralized_settings_plugin_title": "Centralized Settings Plugin",
|
||||||
"centralised_settings_plugin_description": "Configure settings from a centralised location on your Jellyfin server. All client settings for all users will be synced automatically.",
|
"centralized_settings_plugin_description": "Configure settings from a centralized location on your Jellyfin server. All client settings for all users will be synced automatically.",
|
||||||
"done_button": "Done",
|
"done_button": "Done",
|
||||||
"go_to_settings_button": "Go to Settings",
|
"go_to_settings_button": "Go to Settings",
|
||||||
"read_more": "Read More"
|
"read_more": "Read More"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"settings_title": "Settings",
|
"settings_title": "Settings",
|
||||||
"log_out_button": "Log Out",
|
"log_out_button": "Log out",
|
||||||
"categories": {
|
"categories": {
|
||||||
"title": "Categories"
|
"title": "Categories"
|
||||||
},
|
},
|
||||||
@@ -179,9 +179,9 @@
|
|||||||
"horizontal_swipe_skip": "Horizontal Swipe to Skip",
|
"horizontal_swipe_skip": "Horizontal Swipe to Skip",
|
||||||
"horizontal_swipe_skip_description": "Swipe left/right when controls are hidden to skip",
|
"horizontal_swipe_skip_description": "Swipe left/right when controls are hidden to skip",
|
||||||
"left_side_brightness": "Left Side Brightness Control",
|
"left_side_brightness": "Left Side Brightness Control",
|
||||||
"left_side_brightness_description": "Swipe up/down on left side to adjust brightness",
|
"left_side_brightness_description": "Swipe up/down on the left side to adjust brightness",
|
||||||
"right_side_volume": "Right Side Volume Control",
|
"right_side_volume": "Right Side Volume Control",
|
||||||
"right_side_volume_description": "Swipe up/down on right side to adjust volume",
|
"right_side_volume_description": "Swipe up/down on the right side to adjust volume",
|
||||||
"hide_volume_slider": "Hide Volume Slider",
|
"hide_volume_slider": "Hide Volume Slider",
|
||||||
"hide_volume_slider_description": "Hide the volume slider in the video player",
|
"hide_volume_slider_description": "Hide the volume slider in the video player",
|
||||||
"hide_brightness_slider": "Hide Brightness Slider",
|
"hide_brightness_slider": "Hide Brightness Slider",
|
||||||
@@ -218,7 +218,7 @@
|
|||||||
"Smart": "Smart",
|
"Smart": "Smart",
|
||||||
"Always": "Always",
|
"Always": "Always",
|
||||||
"None": "None",
|
"None": "None",
|
||||||
"OnlyForced": "OnlyForced"
|
"OnlyForced": "Only Forced"
|
||||||
},
|
},
|
||||||
"text_color": "Text Color",
|
"text_color": "Text Color",
|
||||||
"background_color": "Background Color",
|
"background_color": "Background Color",
|
||||||
@@ -253,29 +253,7 @@
|
|||||||
},
|
},
|
||||||
"subtitle_color": "Subtitle Color",
|
"subtitle_color": "Subtitle Color",
|
||||||
"subtitle_background_color": "Background Color",
|
"subtitle_background_color": "Background Color",
|
||||||
"subtitle_font": "Subtitle Font",
|
"subtitle_font": "Subtitle Font"
|
||||||
"ksplayer_title": "KSPlayer Settings",
|
|
||||||
"hardware_decode": "Hardware Decoding",
|
|
||||||
"hardware_decode_description": "Use hardware acceleration for video decoding. Disable if you experience playback issues."
|
|
||||||
},
|
|
||||||
"vlc_subtitles": {
|
|
||||||
"title": "VLC Subtitle Settings",
|
|
||||||
"hint": "Customize subtitle appearance for VLC player. Changes take effect on next playback.",
|
|
||||||
"text_color": "Text Color",
|
|
||||||
"background_color": "Background Color",
|
|
||||||
"background_opacity": "Background Opacity",
|
|
||||||
"outline_color": "Outline Color",
|
|
||||||
"outline_opacity": "Outline Opacity",
|
|
||||||
"outline_thickness": "Outline Thickness",
|
|
||||||
"bold": "Bold Text",
|
|
||||||
"margin": "Bottom Margin"
|
|
||||||
},
|
|
||||||
"video_player": {
|
|
||||||
"title": "Video Player",
|
|
||||||
"video_player": "Video Player",
|
|
||||||
"video_player_description": "Choose which video player to use on iOS.",
|
|
||||||
"ksplayer": "KSPlayer",
|
|
||||||
"vlc": "VLC"
|
|
||||||
},
|
},
|
||||||
"other": {
|
"other": {
|
||||||
"other_title": "Other",
|
"other_title": "Other",
|
||||||
@@ -294,20 +272,15 @@
|
|||||||
"UNKNOWN": "Unknown"
|
"UNKNOWN": "Unknown"
|
||||||
},
|
},
|
||||||
"safe_area_in_controls": "Safe Area in Controls",
|
"safe_area_in_controls": "Safe Area in Controls",
|
||||||
"video_player": "Video Player",
|
|
||||||
"video_players": {
|
|
||||||
"VLC_3": "VLC 3",
|
|
||||||
"VLC_4": "VLC 4 (Experimental + PiP)"
|
|
||||||
},
|
|
||||||
"show_custom_menu_links": "Show Custom Menu Links",
|
"show_custom_menu_links": "Show Custom Menu Links",
|
||||||
"show_large_home_carousel": "Show Large Home Carousel (beta)",
|
"show_large_home_carousel": "Show Large Home Carousel (beta)",
|
||||||
"hide_libraries": "Hide Libraries",
|
"hide_libraries": "Hide Libraries",
|
||||||
"select_liraries_you_want_to_hide": "Select the libraries you want to hide from the Library tab and home page sections.",
|
"select_libraries_you_want_to_hide": "Select the libraries you want to hide from the Library tab and home page sections.",
|
||||||
"disable_haptic_feedback": "Disable Haptic Feedback",
|
"disable_haptic_feedback": "Disable Haptic Feedback",
|
||||||
"default_quality": "Default Quality",
|
"default_quality": "Default Quality",
|
||||||
"default_playback_speed": "Default Playback Speed",
|
"default_playback_speed": "Default Playback Speed",
|
||||||
"auto_play_next_episode": "Auto-play Next Episode",
|
"auto_play_next_episode": "Autoplay Next Episode",
|
||||||
"max_auto_play_episode_count": "Max Auto Play Episode Count",
|
"max_auto_play_episode_count": "Max Autoplay Episode Count",
|
||||||
"disabled": "Disabled"
|
"disabled": "Disabled"
|
||||||
},
|
},
|
||||||
"downloads": {
|
"downloads": {
|
||||||
@@ -326,8 +299,8 @@
|
|||||||
},
|
},
|
||||||
"plugins": {
|
"plugins": {
|
||||||
"plugins_title": "Plugins",
|
"plugins_title": "Plugins",
|
||||||
"jellyseerr": {
|
"seerr": {
|
||||||
"jellyseerr_warning": "This integration is in its early stages. Expect things to change.",
|
"seerr_warning": "This integration is in early development. Features may change.",
|
||||||
"server_url": "Server URL",
|
"server_url": "Server URL",
|
||||||
"server_url_hint": "Example: http(s)://your-host.url\n(add port if required)",
|
"server_url_hint": "Example: http(s)://your-host.url\n(add port if required)",
|
||||||
"server_url_placeholder": "Seerr URL",
|
"server_url_placeholder": "Seerr URL",
|
||||||
@@ -339,9 +312,9 @@
|
|||||||
"movie_quota_days": "Movie Quota Days",
|
"movie_quota_days": "Movie Quota Days",
|
||||||
"tv_quota_limit": "TV Quota Limit",
|
"tv_quota_limit": "TV Quota Limit",
|
||||||
"tv_quota_days": "TV Quota Days",
|
"tv_quota_days": "TV Quota Days",
|
||||||
"reset_jellyseerr_config_button": "Reset Seerr Config",
|
"reset_seerr_config_button": "Reset Seerr Config",
|
||||||
"unlimited": "Unlimited",
|
"unlimited": "Unlimited",
|
||||||
"plus_n_more": "+{{n}} More",
|
"plus_n_more": "+{{n}} more",
|
||||||
"order_by": {
|
"order_by": {
|
||||||
"DEFAULT": "Default",
|
"DEFAULT": "Default",
|
||||||
"VOTE_COUNT_AND_AVERAGE": "Vote count and average",
|
"VOTE_COUNT_AND_AVERAGE": "Vote count and average",
|
||||||
@@ -353,7 +326,7 @@
|
|||||||
"url": "URL",
|
"url": "URL",
|
||||||
"server_url_placeholder": "http(s)://domain.org:port",
|
"server_url_placeholder": "http(s)://domain.org:port",
|
||||||
"marlin_search_hint": "Enter the URL for the Marlin server. The URL should include http or https and optionally the port.",
|
"marlin_search_hint": "Enter the URL for the Marlin server. The URL should include http or https and optionally the port.",
|
||||||
"read_more_about_marlin": "Read More About Marlin.",
|
"read_more_about_marlin": "Read more about Marlin.",
|
||||||
"save_button": "Save",
|
"save_button": "Save",
|
||||||
"toasts": {
|
"toasts": {
|
||||||
"saved": "Saved",
|
"saved": "Saved",
|
||||||
@@ -368,7 +341,7 @@
|
|||||||
"url": "URL",
|
"url": "URL",
|
||||||
"server_url_placeholder": "http(s)://streamystats.example.com",
|
"server_url_placeholder": "http(s)://streamystats.example.com",
|
||||||
"streamystats_search_hint": "Enter the URL for your Streamystats server. The URL should include http or https and optionally the port.",
|
"streamystats_search_hint": "Enter the URL for your Streamystats server. The URL should include http or https and optionally the port.",
|
||||||
"read_more_about_streamystats": "Read More About Streamystats.",
|
"read_more_about_streamystats": "Read more about Streamystats.",
|
||||||
"save_button": "Save",
|
"save_button": "Save",
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
"features_title": "Features",
|
"features_title": "Features",
|
||||||
@@ -427,18 +400,18 @@
|
|||||||
"system": "System"
|
"system": "System"
|
||||||
},
|
},
|
||||||
"toasts": {
|
"toasts": {
|
||||||
"error_deleting_files": "Error Deleting Files",
|
"error_deleting_files": "Error deleting files",
|
||||||
"background_downloads_enabled": "Background downloads enabled",
|
"background_downloads_enabled": "Background downloads enabled",
|
||||||
"background_downloads_disabled": "Background downloads disabled"
|
"background_downloads_disabled": "Background downloads disabled"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sessions": {
|
"sessions": {
|
||||||
"title": "Sessions",
|
"title": "Sessions",
|
||||||
"no_active_sessions": "No Active Sessions"
|
"no_active_sessions": "No active sessions"
|
||||||
},
|
},
|
||||||
"downloads": {
|
"downloads": {
|
||||||
"downloads_title": "Downloads",
|
"downloads_title": "Downloads",
|
||||||
"tvseries": "TV-Series",
|
"tvseries": "TV Series",
|
||||||
"movies": "Movies",
|
"movies": "Movies",
|
||||||
"queue": "Queue",
|
"queue": "Queue",
|
||||||
"other_media": "Other media",
|
"other_media": "Other media",
|
||||||
@@ -446,7 +419,7 @@
|
|||||||
"no_items_in_queue": "No Items in Queue",
|
"no_items_in_queue": "No Items in Queue",
|
||||||
"no_downloaded_items": "No Downloaded Items",
|
"no_downloaded_items": "No Downloaded Items",
|
||||||
"delete_all_movies_button": "Delete All Movies",
|
"delete_all_movies_button": "Delete All Movies",
|
||||||
"delete_all_tvseries_button": "Delete All TV-Series",
|
"delete_all_tvseries_button": "Delete All TV Series",
|
||||||
"delete_all_button": "Delete All",
|
"delete_all_button": "Delete All",
|
||||||
"delete_all_other_media_button": "Delete other media",
|
"delete_all_other_media_button": "Delete other media",
|
||||||
"active_download": "Active Download",
|
"active_download": "Active Download",
|
||||||
@@ -461,27 +434,27 @@
|
|||||||
"eta": "ETA {{eta}}",
|
"eta": "ETA {{eta}}",
|
||||||
"toasts": {
|
"toasts": {
|
||||||
"you_are_not_allowed_to_download_files": "You are not allowed to download files.",
|
"you_are_not_allowed_to_download_files": "You are not allowed to download files.",
|
||||||
"deleted_all_movies_successfully": "Deleted All Movies Successfully!",
|
"deleted_all_movies_successfully": "Deleted all movies successfully!",
|
||||||
"failed_to_delete_all_movies": "Failed to Delete All Movies",
|
"failed_to_delete_all_movies": "Failed to delete all movies",
|
||||||
"deleted_all_tvseries_successfully": "Deleted All TV-Series Successfully!",
|
"deleted_all_tvseries_successfully": "Deleted all TV series successfully!",
|
||||||
"failed_to_delete_all_tvseries": "Failed to Delete All TV-Series",
|
"failed_to_delete_all_tvseries": "Failed to delete all TV series",
|
||||||
"deleted_media_successfully": "Deleted other media Successfully!",
|
"deleted_media_successfully": "Deleted other media successfully!",
|
||||||
"failed_to_delete_media": "Failed to Delete other media",
|
"failed_to_delete_media": "Failed to delete other media",
|
||||||
"download_deleted": "Download Deleted",
|
"download_deleted": "Download deleted",
|
||||||
"download_cancelled": "Download Cancelled",
|
"download_cancelled": "Download cancelled",
|
||||||
"could_not_delete_download": "Could Not Delete Download",
|
"could_not_delete_download": "Could not delete download",
|
||||||
"download_paused": "Download Paused",
|
"download_paused": "Download paused",
|
||||||
"could_not_pause_download": "Could Not Pause Download",
|
"could_not_pause_download": "Could not pause download",
|
||||||
"download_resumed": "Download Resumed",
|
"download_resumed": "Download resumed",
|
||||||
"could_not_resume_download": "Could Not Resume Download",
|
"could_not_resume_download": "Could not resume download",
|
||||||
"download_completed": "Download Completed",
|
"download_completed": "Download completed",
|
||||||
"download_failed": "Download Failed",
|
"download_failed": "Download failed",
|
||||||
"download_failed_for_item": "Download failed for {{item}} - {{error}}",
|
"download_failed_for_item": "Download failed for {{item}} - {{error}}",
|
||||||
"download_completed_for_item": "Download Completed for {{item}}",
|
"download_completed_for_item": "Download completed for {{item}}",
|
||||||
"download_started_for_item": "Download Started for {{item}}",
|
"download_started_for_item": "Download started for {{item}}",
|
||||||
"failed_to_start_download": "Failed to start download",
|
"failed_to_start_download": "Failed to start download",
|
||||||
"item_already_downloading": "{{item}} is already downloading",
|
"item_already_downloading": "{{item}} is already downloading",
|
||||||
"all_files_deleted": "All Downloads Deleted Successfully",
|
"all_files_deleted": "All downloads deleted successfully",
|
||||||
"files_deleted_by_type": "{{count}} {{type}} deleted",
|
"files_deleted_by_type": "{{count}} {{type}} deleted",
|
||||||
"all_files_folders_and_jobs_deleted_successfully": "All files, folders, and jobs deleted successfully",
|
"all_files_folders_and_jobs_deleted_successfully": "All files, folders, and jobs deleted successfully",
|
||||||
"failed_to_clean_cache_directory": "Failed to clean cache directory",
|
"failed_to_clean_cache_directory": "Failed to clean cache directory",
|
||||||
@@ -515,7 +488,7 @@
|
|||||||
"library": "Library",
|
"library": "Library",
|
||||||
"discover": "Discover",
|
"discover": "Discover",
|
||||||
"no_results": "No Results",
|
"no_results": "No Results",
|
||||||
"no_results_found_for": "No Results Found For",
|
"no_results_found_for": "No results found for",
|
||||||
"movies": "Movies",
|
"movies": "Movies",
|
||||||
"series": "Series",
|
"series": "Series",
|
||||||
"episodes": "Episodes",
|
"episodes": "Episodes",
|
||||||
@@ -559,6 +532,7 @@
|
|||||||
"items": "Items"
|
"items": "Items"
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
|
"options_title": "Options",
|
||||||
"display": "Display",
|
"display": "Display",
|
||||||
"row": "Row",
|
"row": "Row",
|
||||||
"list": "List",
|
"list": "List",
|
||||||
@@ -598,17 +572,26 @@
|
|||||||
"could_not_create_stream_for_chromecast": "Could not create a stream for Chromecast",
|
"could_not_create_stream_for_chromecast": "Could not create a stream for Chromecast",
|
||||||
"message_from_server": "Message from Server: {{message}}",
|
"message_from_server": "Message from Server: {{message}}",
|
||||||
"next_episode": "Next Episode",
|
"next_episode": "Next Episode",
|
||||||
"refresh_tracks": "Refresh Tracks",
|
|
||||||
"audio_tracks": "Audio Tracks:",
|
|
||||||
"playback_state": "Playback State:",
|
|
||||||
"index": "Index:",
|
|
||||||
"continue_watching": "Continue Watching",
|
"continue_watching": "Continue Watching",
|
||||||
"go_back": "Go Back",
|
"go_back": "Go Back",
|
||||||
"downloaded_file_title": "You have this file downloaded",
|
"downloaded_file_title": "You have this file downloaded",
|
||||||
"downloaded_file_message": "Do you want to play the downloaded file?",
|
"downloaded_file_message": "Do you want to play the downloaded file?",
|
||||||
"downloaded_file_yes": "Yes",
|
"downloaded_file_yes": "Yes",
|
||||||
"downloaded_file_no": "No",
|
"downloaded_file_no": "No",
|
||||||
"downloaded_file_cancel": "Cancel"
|
"downloaded_file_cancel": "Cancel",
|
||||||
|
"playback_options_title": "Playback Options",
|
||||||
|
"mpv_subtitle_settings_title": "MPV Subtitle Settings",
|
||||||
|
"mpv_subtitle_settings_description": "Advanced subtitle customization for MPV player",
|
||||||
|
"subtitle_scale": "Subtitle Scale",
|
||||||
|
"vertical_margin": "Vertical Margin",
|
||||||
|
"horizontal_alignment": "Horizontal Alignment",
|
||||||
|
"vertical_alignment": "Vertical Alignment",
|
||||||
|
"alignment_left": "Left",
|
||||||
|
"alignment_center": "Center",
|
||||||
|
"alignment_right": "Right",
|
||||||
|
"alignment_top": "Top",
|
||||||
|
"alignment_bottom": "Bottom",
|
||||||
|
"mpv_player_title": "MPV Player"
|
||||||
},
|
},
|
||||||
"item_card": {
|
"item_card": {
|
||||||
"next_up": "Next Up",
|
"next_up": "Next Up",
|
||||||
@@ -627,11 +610,11 @@
|
|||||||
"media_options": "Media Options",
|
"media_options": "Media Options",
|
||||||
"quality": "Quality",
|
"quality": "Quality",
|
||||||
"audio": "Audio",
|
"audio": "Audio",
|
||||||
"subtitles": "Subtitle",
|
"subtitles": "Subtitles",
|
||||||
"show_more": "Show More",
|
"show_more": "Show More",
|
||||||
"show_less": "Show Less",
|
"show_less": "Show Less",
|
||||||
"appeared_in": "Appeared In",
|
"appeared_in": "Appeared In",
|
||||||
"could_not_load_item": "Could Not Load Item",
|
"could_not_load_item": "Could not load item",
|
||||||
"none": "None",
|
"none": "None",
|
||||||
"download": {
|
"download": {
|
||||||
"download_season": "Download Season",
|
"download_season": "Download Season",
|
||||||
@@ -654,11 +637,11 @@
|
|||||||
"for_kids": "For Kids",
|
"for_kids": "For Kids",
|
||||||
"news": "News"
|
"news": "News"
|
||||||
},
|
},
|
||||||
"jellyseerr": {
|
"seerr": {
|
||||||
"confirm": "Confirm",
|
"confirm": "Confirm",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"yes": "Yes",
|
"yes": "Yes",
|
||||||
"whats_wrong": "What's Wrong?",
|
"whats_wrong": "What's wrong?",
|
||||||
"issue_type": "Issue Type",
|
"issue_type": "Issue Type",
|
||||||
"select_an_issue": "Select an Issue",
|
"select_an_issue": "Select an Issue",
|
||||||
"types": "Types",
|
"types": "Types",
|
||||||
@@ -667,7 +650,7 @@
|
|||||||
"report_issue_button": "Report Issue",
|
"report_issue_button": "Report Issue",
|
||||||
"request_button": "Request",
|
"request_button": "Request",
|
||||||
"are_you_sure_you_want_to_request_all_seasons": "Are you sure you want to request all seasons?",
|
"are_you_sure_you_want_to_request_all_seasons": "Are you sure you want to request all seasons?",
|
||||||
"failed_to_login": "Failed to Login",
|
"failed_to_login": "Failed to log in",
|
||||||
"cast": "Cast",
|
"cast": "Cast",
|
||||||
"details": "Details",
|
"details": "Details",
|
||||||
"status": "Status",
|
"status": "Status",
|
||||||
@@ -698,17 +681,17 @@
|
|||||||
"requested_by": "Requested by {{user}}",
|
"requested_by": "Requested by {{user}}",
|
||||||
"unknown_user": "Unknown User",
|
"unknown_user": "Unknown User",
|
||||||
"toasts": {
|
"toasts": {
|
||||||
"jellyseer_does_not_meet_requirements": "Seerr server does not meet minimum version requirements! Please update to at least 2.0.0",
|
"seerr_does_not_meet_requirements": "Seerr server does not meet minimum version requirements! Please update to at least 2.0.0",
|
||||||
"jellyseerr_test_failed": "Seerr test failed. Please try again.",
|
"seerr_test_failed": "Seerr test failed. Please try again.",
|
||||||
"failed_to_test_jellyseerr_server_url": "Failed to test Seerr server url",
|
"failed_to_test_seerr_server_url": "Failed to test Seerr server URL",
|
||||||
"issue_submitted": "Issue Submitted!",
|
"issue_submitted": "Issue Submitted!",
|
||||||
"requested_item": "Requested {{item}}!",
|
"requested_item": "Requested {{item}}!",
|
||||||
"you_dont_have_permission_to_request": "You don't have permission to request!",
|
"you_dont_have_permission_to_request": "You don't have permission to request!",
|
||||||
"something_went_wrong_requesting_media": "Something went wrong requesting media!",
|
"something_went_wrong_requesting_media": "Something went wrong requesting media!",
|
||||||
"request_approved": "Request Approved!",
|
"request_approved": "Request Approved!",
|
||||||
"request_declined": "Request Declined!",
|
"request_declined": "Request Declined!",
|
||||||
"failed_to_approve_request": "Failed to Approve Request",
|
"failed_to_approve_request": "Failed to approve request",
|
||||||
"failed_to_decline_request": "Failed to Decline Request"
|
"failed_to_decline_request": "Failed to decline request"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tabs": {
|
"tabs": {
|
||||||
@@ -725,7 +708,7 @@
|
|||||||
"albums": "Albums",
|
"albums": "Albums",
|
||||||
"artists": "Artists",
|
"artists": "Artists",
|
||||||
"playlists": "Playlists",
|
"playlists": "Playlists",
|
||||||
"tracks": "tracks"
|
"tracks": "Tracks"
|
||||||
},
|
},
|
||||||
"filters": {
|
"filters": {
|
||||||
"all": "All"
|
"all": "All"
|
||||||
|
|||||||
@@ -39,39 +39,39 @@
|
|||||||
"please_login_again": "Su sesión guardada ha caducado. Por favor, inicie sesión de nuevo.",
|
"please_login_again": "Su sesión guardada ha caducado. Por favor, inicie sesión de nuevo.",
|
||||||
"remove_saved_login": "Eliminar inicio de sesión guardado",
|
"remove_saved_login": "Eliminar inicio de sesión guardado",
|
||||||
"remove_saved_login_description": "Esto eliminará tus credenciales guardadas para este servidor. Tendrás que volver a introducir tu nombre de usuario y contraseña la próxima vez.",
|
"remove_saved_login_description": "Esto eliminará tus credenciales guardadas para este servidor. Tendrás que volver a introducir tu nombre de usuario y contraseña la próxima vez.",
|
||||||
"accounts_count": "{{count}} accounts",
|
"accounts_count": "{{count}} cuentas",
|
||||||
"select_account": "Select Account",
|
"select_account": "Seleccione una cuenta",
|
||||||
"add_account": "Add Account",
|
"add_account": "Añadir cuenta",
|
||||||
"remove_account_description": "This will remove the saved credentials for {{username}}."
|
"remove_account_description": "Esto eliminará las credenciales guardadas para {{username}}."
|
||||||
},
|
},
|
||||||
"save_account": {
|
"save_account": {
|
||||||
"title": "Save Account",
|
"title": "Guardar Cuenta",
|
||||||
"save_for_later": "Save this account",
|
"save_for_later": "Guardar esta cuenta",
|
||||||
"security_option": "Security Option",
|
"security_option": "Opciones de seguridad",
|
||||||
"no_protection": "No protection",
|
"no_protection": "Sin Protección",
|
||||||
"no_protection_desc": "Quick login without authentication",
|
"no_protection_desc": "Inicio de sesión rápido sin autenticación",
|
||||||
"pin_code": "PIN code",
|
"pin_code": "Código PIN",
|
||||||
"pin_code_desc": "4-digit PIN required when switching",
|
"pin_code_desc": "PIN de 4 dígitos requerido al cambiar",
|
||||||
"password": "Re-enter password",
|
"password": "Vuelva a introducir la contraseña",
|
||||||
"password_desc": "Password required when switching",
|
"password_desc": "Contraseña requerida al cambiar",
|
||||||
"save_button": "Save",
|
"save_button": "Guardar",
|
||||||
"cancel_button": "Cancel"
|
"cancel_button": "Cancelar"
|
||||||
},
|
},
|
||||||
"pin": {
|
"pin": {
|
||||||
"enter_pin": "Enter PIN",
|
"enter_pin": "Introduce el PIN",
|
||||||
"enter_pin_for": "Enter PIN for {{username}}",
|
"enter_pin_for": "Introduzca el PIN para {{username}}",
|
||||||
"enter_4_digits": "Enter 4 digits",
|
"enter_4_digits": "Introduce 4 dígitos",
|
||||||
"invalid_pin": "Invalid PIN",
|
"invalid_pin": "PIN inválido",
|
||||||
"setup_pin": "Set Up PIN",
|
"setup_pin": "Configurar PIN",
|
||||||
"confirm_pin": "Confirm PIN",
|
"confirm_pin": "Confirmar PIN",
|
||||||
"pins_dont_match": "PINs don't match",
|
"pins_dont_match": "Los códigos PIN no coinciden",
|
||||||
"forgot_pin": "Forgot PIN?",
|
"forgot_pin": "¿Olvidó el PIN?",
|
||||||
"forgot_pin_desc": "Your saved credentials will be removed"
|
"forgot_pin_desc": "Sus credenciales guardadas serán eliminadas"
|
||||||
},
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"enter_password": "Enter Password",
|
"enter_password": "Introduzca la contraseña",
|
||||||
"enter_password_for": "Enter password for {{username}}",
|
"enter_password_for": "Introduzca la contraseña para {{username}}",
|
||||||
"invalid_password": "Invalid password"
|
"invalid_password": "Contraseña inválida"
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"checking_server_connection": "Comprobando conexión con el servidor...",
|
"checking_server_connection": "Comprobando conexión con el servidor...",
|
||||||
@@ -124,32 +124,32 @@
|
|||||||
"hide_remote_session_button": "Ocultar botón de sesión remota"
|
"hide_remote_session_button": "Ocultar botón de sesión remota"
|
||||||
},
|
},
|
||||||
"network": {
|
"network": {
|
||||||
"title": "Network",
|
"title": "Cadena",
|
||||||
"local_network": "Local Network",
|
"local_network": "Red local",
|
||||||
"auto_switch_enabled": "Auto-switch when at home",
|
"auto_switch_enabled": "Cambiar automáticamente en casa",
|
||||||
"auto_switch_description": "Automatically switch to local URL when connected to home WiFi",
|
"auto_switch_description": "Cambiar automáticamente a la URL local cuando se conecta a la WiFi de casa",
|
||||||
"local_url": "Local URL",
|
"local_url": "URL local",
|
||||||
"local_url_hint": "Enter your local server address (e.g., http://192.168.1.100:8096)",
|
"local_url_hint": "Introduzca la dirección de su servidor local (por ejemplo, http://192.168.1.100:8096)",
|
||||||
"local_url_placeholder": "http://192.168.1.100:8096",
|
"local_url_placeholder": "http://192.168.1.100:8096",
|
||||||
"home_wifi_networks": "Home WiFi Networks",
|
"home_wifi_networks": "Redes WiFi domésticas",
|
||||||
"add_current_network": "Add \"{{ssid}}\"",
|
"add_current_network": "Añadir \"{{ssid}}\"",
|
||||||
"not_connected_to_wifi": "Not connected to WiFi",
|
"not_connected_to_wifi": "No está conectado a WiFi",
|
||||||
"no_networks_configured": "No networks configured",
|
"no_networks_configured": "No hay redes configuradas",
|
||||||
"add_network_hint": "Add your home WiFi network to enable auto-switching",
|
"add_network_hint": "Añade tu red WiFi doméstica para activar el cambio automático",
|
||||||
"current_wifi": "Current WiFi",
|
"current_wifi": "WiFi actual",
|
||||||
"using_url": "Using",
|
"using_url": "Utilizando",
|
||||||
"local": "Local URL",
|
"local": "URL local",
|
||||||
"remote": "Remote URL",
|
"remote": "URL Remota",
|
||||||
"not_connected": "Not connected",
|
"not_connected": "Sin conexión",
|
||||||
"current_server": "Current Server",
|
"current_server": "Servidor actual",
|
||||||
"remote_url": "Remote URL",
|
"remote_url": "URL Remota",
|
||||||
"active_url": "Active URL",
|
"active_url": "URL Activa",
|
||||||
"not_configured": "Not configured",
|
"not_configured": "Sin configurar",
|
||||||
"network_added": "Network added",
|
"network_added": "Red añadida",
|
||||||
"network_already_added": "Network already added",
|
"network_already_added": "Red ya añadida",
|
||||||
"no_wifi_connected": "Not connected to WiFi",
|
"no_wifi_connected": "Sin conexión a WiFi",
|
||||||
"permission_denied": "Location permission denied",
|
"permission_denied": "Permiso de ubicación denegado",
|
||||||
"permission_denied_explanation": "Location permission is required to detect WiFi network for auto-switching. Please enable it in Settings."
|
"permission_denied_explanation": "Se necesita el permiso de ubicación para detectar la red WiFi para cambiar automáticamente. Por favor, actívala en Configuración."
|
||||||
},
|
},
|
||||||
"user_info": {
|
"user_info": {
|
||||||
"user_info_title": "Información de usuario",
|
"user_info_title": "Información de usuario",
|
||||||
@@ -195,12 +195,12 @@
|
|||||||
"none": "Ninguno",
|
"none": "Ninguno",
|
||||||
"language": "Idioma",
|
"language": "Idioma",
|
||||||
"transcode_mode": {
|
"transcode_mode": {
|
||||||
"title": "Audio Transcoding",
|
"title": "Transcodificación de audio",
|
||||||
"description": "Controls how surround audio (7.1, TrueHD, DTS-HD) is handled",
|
"description": "Controla cómo el audio envolvente (7.1, TrueHD, DTS-HD) es manejado",
|
||||||
"auto": "Auto",
|
"auto": "Auto",
|
||||||
"stereo": "Force Stereo",
|
"stereo": "Forzar salida estéreo",
|
||||||
"5_1": "Allow 5.1",
|
"5_1": "Permitir 5.1",
|
||||||
"passthrough": "Passthrough"
|
"passthrough": "Directo"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"subtitles": {
|
"subtitles": {
|
||||||
@@ -259,16 +259,16 @@
|
|||||||
"hardware_decode_description": "Utilizar la aceleración de hardware para la decodificación de vídeo. Deshabilite si experimenta problemas de reproducción."
|
"hardware_decode_description": "Utilizar la aceleración de hardware para la decodificación de vídeo. Deshabilite si experimenta problemas de reproducción."
|
||||||
},
|
},
|
||||||
"vlc_subtitles": {
|
"vlc_subtitles": {
|
||||||
"title": "VLC Subtitle Settings",
|
"title": "Configuración de subtítulos VLC",
|
||||||
"hint": "Customize subtitle appearance for VLC player. Changes take effect on next playback.",
|
"hint": "Personalizar la apariencia de los subtítulos para el reproductor VLC. Los cambios tendrán efecto en la próxima reproducción.",
|
||||||
"text_color": "Text Color",
|
"text_color": "Color del texto",
|
||||||
"background_color": "Background Color",
|
"background_color": "Color del fondo",
|
||||||
"background_opacity": "Background Opacity",
|
"background_opacity": "Opacidad del fondo",
|
||||||
"outline_color": "Outline Color",
|
"outline_color": "Color del contorno",
|
||||||
"outline_opacity": "Outline Opacity",
|
"outline_opacity": "Opacidad del contorno",
|
||||||
"outline_thickness": "Outline Thickness",
|
"outline_thickness": "Grosor del contorno",
|
||||||
"bold": "Bold Text",
|
"bold": "Texto en negrita",
|
||||||
"margin": "Bottom Margin"
|
"margin": "Margen inferior"
|
||||||
},
|
},
|
||||||
"video_player": {
|
"video_player": {
|
||||||
"title": "Reproductor de vídeo",
|
"title": "Reproductor de vídeo",
|
||||||
@@ -300,13 +300,13 @@
|
|||||||
"VLC_4": "VLC 4 (Experimental + PiP)"
|
"VLC_4": "VLC 4 (Experimental + PiP)"
|
||||||
},
|
},
|
||||||
"show_custom_menu_links": "Mostrar enlaces de menú personalizados",
|
"show_custom_menu_links": "Mostrar enlaces de menú personalizados",
|
||||||
"show_large_home_carousel": "Show Large Home Carousel (beta)",
|
"show_large_home_carousel": "Mostrar carrusel del menú principal grande (beta)",
|
||||||
"hide_libraries": "Ocultar bibliotecas",
|
"hide_libraries": "Ocultar bibliotecas",
|
||||||
"select_liraries_you_want_to_hide": "Selecciona las bibliotecas que quieres ocultar de la pestaña Bibliotecas y de Inicio.",
|
"select_liraries_you_want_to_hide": "Selecciona las bibliotecas que quieres ocultar de la pestaña Bibliotecas y de Inicio.",
|
||||||
"disable_haptic_feedback": "Desactivar feedback háptico",
|
"disable_haptic_feedback": "Desactivar feedback háptico",
|
||||||
"default_quality": "Calidad por defecto",
|
"default_quality": "Calidad por defecto",
|
||||||
"default_playback_speed": "Velocidad de reproducción predeterminada",
|
"default_playback_speed": "Velocidad de reproducción predeterminada",
|
||||||
"auto_play_next_episode": "Auto-play Next Episode",
|
"auto_play_next_episode": "Reproducir automáticamente el siguiente episodio",
|
||||||
"max_auto_play_episode_count": "Máximo número de episodios de Auto Play",
|
"max_auto_play_episode_count": "Máximo número de episodios de Auto Play",
|
||||||
"disabled": "Deshabilitado"
|
"disabled": "Deshabilitado"
|
||||||
},
|
},
|
||||||
@@ -317,10 +317,10 @@
|
|||||||
"title": "Música",
|
"title": "Música",
|
||||||
"playback_title": "Reproducir",
|
"playback_title": "Reproducir",
|
||||||
"playback_description": "Configurar cómo se reproduce la música.",
|
"playback_description": "Configurar cómo se reproduce la música.",
|
||||||
"prefer_downloaded": "Prefer Downloaded Songs",
|
"prefer_downloaded": "Preferir las canciones descargadas",
|
||||||
"caching_title": "Almacenando en caché",
|
"caching_title": "Almacenando en caché",
|
||||||
"caching_description": "Automatically cache upcoming tracks for smoother playback.",
|
"caching_description": "Cachear automáticamente las próximas canciones para una reproducción más suave.",
|
||||||
"lookahead_enabled": "Enable Look-Ahead Caching",
|
"lookahead_enabled": "Activar el look-Ahead Cache",
|
||||||
"lookahead_count": "",
|
"lookahead_count": "",
|
||||||
"max_cache_size": "Tamaño máximo del caché"
|
"max_cache_size": "Tamaño máximo del caché"
|
||||||
},
|
},
|
||||||
@@ -399,7 +399,7 @@
|
|||||||
"size_used": "{{used}} de {{total}} usado",
|
"size_used": "{{used}} de {{total}} usado",
|
||||||
"delete_all_downloaded_files": "Eliminar todos los archivos descargados",
|
"delete_all_downloaded_files": "Eliminar todos los archivos descargados",
|
||||||
"music_cache_title": "Caché de música",
|
"music_cache_title": "Caché de música",
|
||||||
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
"music_cache_description": "Cachear automáticamente las canciones mientras escuchas una reproducción más suave y soporte sin conexión",
|
||||||
"enable_music_cache": "Activar Caché de Música",
|
"enable_music_cache": "Activar Caché de Música",
|
||||||
"clear_music_cache": "Borrar Caché de Música",
|
"clear_music_cache": "Borrar Caché de Música",
|
||||||
"music_cache_size": "Caché {{Tamaño}}",
|
"music_cache_size": "Caché {{Tamaño}}",
|
||||||
@@ -504,10 +504,10 @@
|
|||||||
"delete": "Borrar",
|
"delete": "Borrar",
|
||||||
"ok": "Aceptar",
|
"ok": "Aceptar",
|
||||||
"remove": "Eliminar",
|
"remove": "Eliminar",
|
||||||
"next": "Next",
|
"next": "Siguiente",
|
||||||
"back": "Back",
|
"back": "Atrás",
|
||||||
"continue": "Continue",
|
"continue": "Continuar",
|
||||||
"verifying": "Verifying..."
|
"verifying": "Verificando..."
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
"search": "Buscar...",
|
"search": "Buscar...",
|
||||||
@@ -753,8 +753,8 @@
|
|||||||
"downloaded": "Descargado",
|
"downloaded": "Descargado",
|
||||||
"downloading": "Descargando...",
|
"downloading": "Descargando...",
|
||||||
"cached": "En caché",
|
"cached": "En caché",
|
||||||
"delete_download": "Delete Download",
|
"delete_download": "Eliminar descarga",
|
||||||
"delete_cache": "Remove from Cache",
|
"delete_cache": "Borrar del caché",
|
||||||
"go_to_artist": "Ir al artista",
|
"go_to_artist": "Ir al artista",
|
||||||
"go_to_album": "Ir al álbum",
|
"go_to_album": "Ir al álbum",
|
||||||
"add_to_favorites": "Añadir a Favoritos",
|
"add_to_favorites": "Añadir a Favoritos",
|
||||||
|
|||||||
@@ -305,8 +305,8 @@
|
|||||||
"select_liraries_you_want_to_hide": "Sélectionnez les bibliothèques que vous souhaitez masquer dans l'onglet Bibliothèque et les sections de la page d'accueil.",
|
"select_liraries_you_want_to_hide": "Sélectionnez les bibliothèques que vous souhaitez masquer dans l'onglet Bibliothèque et les sections de la page d'accueil.",
|
||||||
"disable_haptic_feedback": "Désactiver le retour haptique",
|
"disable_haptic_feedback": "Désactiver le retour haptique",
|
||||||
"default_quality": "Qualité par défaut",
|
"default_quality": "Qualité par défaut",
|
||||||
"default_playback_speed": "Default Playback Speed",
|
"default_playback_speed": "Vitesse de lecture par défaut",
|
||||||
"auto_play_next_episode": "Auto-play Next Episode",
|
"auto_play_next_episode": "Lecture automatique de l'épisode suivant",
|
||||||
"max_auto_play_episode_count": "Nombre d'épisodes en lecture automatique max",
|
"max_auto_play_episode_count": "Nombre d'épisodes en lecture automatique max",
|
||||||
"disabled": "Désactivé"
|
"disabled": "Désactivé"
|
||||||
},
|
},
|
||||||
@@ -314,15 +314,15 @@
|
|||||||
"downloads_title": "Téléchargements"
|
"downloads_title": "Téléchargements"
|
||||||
},
|
},
|
||||||
"music": {
|
"music": {
|
||||||
"title": "Music",
|
"title": "Musique",
|
||||||
"playback_title": "Playback",
|
"playback_title": "Lecture",
|
||||||
"playback_description": "Configure how music is played.",
|
"playback_description": "Configurer le mode de lecture de la musique.",
|
||||||
"prefer_downloaded": "Prefer Downloaded Songs",
|
"prefer_downloaded": "Supprimer toutes les musiques téléchargées",
|
||||||
"caching_title": "Caching",
|
"caching_title": "Mise en cache",
|
||||||
"caching_description": "Automatically cache upcoming tracks for smoother playback.",
|
"caching_description": "Mettre automatiquement en cache les pistes à venir pour une lecture plus fluide.",
|
||||||
"lookahead_enabled": "Enable Look-Ahead Caching",
|
"lookahead_enabled": "Activer la mise en cache guidée",
|
||||||
"lookahead_count": "Tracks to Pre-cache",
|
"lookahead_count": "Pistes à pré-mettre en cache",
|
||||||
"max_cache_size": "Max Cache Size"
|
"max_cache_size": "Taille max de cache"
|
||||||
},
|
},
|
||||||
"plugins": {
|
"plugins": {
|
||||||
"plugins_title": "Plugins",
|
"plugins_title": "Plugins",
|
||||||
@@ -357,19 +357,19 @@
|
|||||||
"save_button": "Enregistrer",
|
"save_button": "Enregistrer",
|
||||||
"toasts": {
|
"toasts": {
|
||||||
"saved": "Enregistré",
|
"saved": "Enregistré",
|
||||||
"refreshed": "Settings refreshed from server"
|
"refreshed": "Paramètres actualisés depuis le serveur"
|
||||||
},
|
},
|
||||||
"refresh_from_server": "Refresh Settings from Server"
|
"refresh_from_server": "Rafraîchir les paramètres depuis le serveur"
|
||||||
},
|
},
|
||||||
"streamystats": {
|
"streamystats": {
|
||||||
"enable_streamystats": "Enable Streamystats",
|
"enable_streamystats": "Activer Streamystats",
|
||||||
"disable_streamystats": "Disable Streamystats",
|
"disable_streamystats": "Désactiver Streamystats",
|
||||||
"enable_search": "Use for Search",
|
"enable_search": "Utiliser pour la recherche",
|
||||||
"url": "URL",
|
"url": "URL",
|
||||||
"server_url_placeholder": "http(s)://streamystats.example.com",
|
"server_url_placeholder": "http(s)://streamystats.example.com",
|
||||||
"streamystats_search_hint": "Enter the URL for your Streamystats server. The URL should include http or https and optionally the port.",
|
"streamystats_search_hint": "Entrez l'URL de votre serveur Streamystats. L'URL doit inclure http ou https et éventuellement le port.",
|
||||||
"read_more_about_streamystats": "Read More About Streamystats.",
|
"read_more_about_streamystats": "En savoir plus sur Streamystats.",
|
||||||
"save_button": "Save",
|
"save_button": "Enregistrer",
|
||||||
"save": "Enregistrer",
|
"save": "Enregistrer",
|
||||||
"features_title": "Fonctionnalités",
|
"features_title": "Fonctionnalités",
|
||||||
"home_sections_title": "Sections de la page d´accueil",
|
"home_sections_title": "Sections de la page d´accueil",
|
||||||
@@ -572,7 +572,7 @@
|
|||||||
"genres": "Genres",
|
"genres": "Genres",
|
||||||
"years": "Années",
|
"years": "Années",
|
||||||
"sort_by": "Trier par",
|
"sort_by": "Trier par",
|
||||||
"filter_by": "Filter By",
|
"filter_by": "Filtrer par",
|
||||||
"sort_order": "Ordre de tri",
|
"sort_order": "Ordre de tri",
|
||||||
"tags": "Tags"
|
"tags": "Tags"
|
||||||
}
|
}
|
||||||
@@ -719,127 +719,127 @@
|
|||||||
"favorites": "Favoris"
|
"favorites": "Favoris"
|
||||||
},
|
},
|
||||||
"music": {
|
"music": {
|
||||||
"title": "Music",
|
"title": "Musique",
|
||||||
"tabs": {
|
"tabs": {
|
||||||
"suggestions": "Suggestions",
|
"suggestions": "Suggestions",
|
||||||
"albums": "Albums",
|
"albums": "Albums",
|
||||||
"artists": "Artists",
|
"artists": "Artistes",
|
||||||
"playlists": "Playlists",
|
"playlists": "Playlists",
|
||||||
"tracks": "tracks"
|
"tracks": "morceaux"
|
||||||
},
|
},
|
||||||
"filters": {
|
"filters": {
|
||||||
"all": "All"
|
"all": "Toutes"
|
||||||
},
|
},
|
||||||
"recently_added": "Recently Added",
|
"recently_added": "Ajoutés récemment",
|
||||||
"recently_played": "Recently Played",
|
"recently_played": "Récemment joué",
|
||||||
"frequently_played": "Frequently Played",
|
"frequently_played": "Fréquemment joué",
|
||||||
"explore": "Explore",
|
"explore": "Explorez",
|
||||||
"top_tracks": "Top Tracks",
|
"top_tracks": "Top chansons",
|
||||||
"play": "Play",
|
"play": "Lecture",
|
||||||
"shuffle": "Shuffle",
|
"shuffle": "Aléatoire",
|
||||||
"play_top_tracks": "Play Top Tracks",
|
"play_top_tracks": "Jouer les pistes les plus populaires",
|
||||||
"no_suggestions": "No suggestions available",
|
"no_suggestions": "Pas de suggestion disponible",
|
||||||
"no_albums": "No albums found",
|
"no_albums": "Pas d'albums trouvés",
|
||||||
"no_artists": "No artists found",
|
"no_artists": "Pas d'artistes trouvé",
|
||||||
"no_playlists": "No playlists found",
|
"no_playlists": "Pas de playlists trouvées",
|
||||||
"album_not_found": "Album not found",
|
"album_not_found": "Album introuvable",
|
||||||
"artist_not_found": "Artist not found",
|
"artist_not_found": "Artiste introuvable",
|
||||||
"playlist_not_found": "Playlist not found",
|
"playlist_not_found": "Playlist introuvable",
|
||||||
"track_options": {
|
"track_options": {
|
||||||
"play_next": "Play Next",
|
"play_next": "Lecture suivante",
|
||||||
"add_to_queue": "Add to Queue",
|
"add_to_queue": "Ajouter à la file d'attente",
|
||||||
"add_to_playlist": "Add to Playlist",
|
"add_to_playlist": "Ajouter à la playlist",
|
||||||
"download": "Download",
|
"download": "Télécharger",
|
||||||
"downloaded": "Downloaded",
|
"downloaded": "Téléchargé",
|
||||||
"downloading": "Downloading...",
|
"downloading": "Téléchargement en cours...",
|
||||||
"cached": "Cached",
|
"cached": "En cache",
|
||||||
"delete_download": "Delete Download",
|
"delete_download": "Supprimer un téléchargement",
|
||||||
"delete_cache": "Remove from Cache",
|
"delete_cache": "Supprimer du cache",
|
||||||
"go_to_artist": "Go to Artist",
|
"go_to_artist": "Voir l'artiste",
|
||||||
"go_to_album": "Go to Album",
|
"go_to_album": "Aller à l’album",
|
||||||
"add_to_favorites": "Add to Favorites",
|
"add_to_favorites": "Ajouter aux favoris",
|
||||||
"remove_from_favorites": "Remove from Favorites",
|
"remove_from_favorites": "Retirer des favoris",
|
||||||
"remove_from_playlist": "Remove from Playlist"
|
"remove_from_playlist": "Retirer de la playlist"
|
||||||
},
|
},
|
||||||
"playlists": {
|
"playlists": {
|
||||||
"create_playlist": "Create Playlist",
|
"create_playlist": "Créer une Playlist",
|
||||||
"playlist_name": "Playlist Name",
|
"playlist_name": "Nom de la Playlist",
|
||||||
"enter_name": "Enter playlist name",
|
"enter_name": "Entrer le nom de la playlist",
|
||||||
"create": "Create",
|
"create": "Créer",
|
||||||
"search_playlists": "Search playlists...",
|
"search_playlists": "Rechercher des playlists...",
|
||||||
"added_to": "Added to {{name}}",
|
"added_to": "Ajouté à {{name}}",
|
||||||
"added": "Added to playlist",
|
"added": "Ajouté à la playlist",
|
||||||
"removed_from": "Removed from {{name}}",
|
"removed_from": "Retiré de {{name}}",
|
||||||
"removed": "Removed from playlist",
|
"removed": "Retiré de la playlist",
|
||||||
"created": "Playlist created",
|
"created": "Playlist créée",
|
||||||
"create_new": "Create New Playlist",
|
"create_new": "Créer une nouvelle playlist",
|
||||||
"failed_to_add": "Failed to add to playlist",
|
"failed_to_add": "Échec de l'ajout à la playlist",
|
||||||
"failed_to_remove": "Failed to remove from playlist",
|
"failed_to_remove": "Échec de la suppression de la playlist",
|
||||||
"failed_to_create": "Failed to create playlist",
|
"failed_to_create": "Échec de la suppression de la playlist",
|
||||||
"delete_playlist": "Delete Playlist",
|
"delete_playlist": "Supprimer la playlist",
|
||||||
"delete_confirm": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.",
|
"delete_confirm": "Êtes-vous sûr de vouloir supprimer « {{ name }} » ? Cette action est irréversible.",
|
||||||
"deleted": "Playlist deleted",
|
"deleted": "Playlist supprimée",
|
||||||
"failed_to_delete": "Failed to delete playlist"
|
"failed_to_delete": "Échec de la suppression de la playlist"
|
||||||
},
|
},
|
||||||
"sort": {
|
"sort": {
|
||||||
"title": "Sort By",
|
"title": "Trier par",
|
||||||
"alphabetical": "Alphabetical",
|
"alphabetical": "Ordre alphabétique",
|
||||||
"date_created": "Date Created"
|
"date_created": "Date de création"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"watchlists": {
|
"watchlists": {
|
||||||
"title": "Watchlists",
|
"title": "Watchlists",
|
||||||
"my_watchlists": "My Watchlists",
|
"my_watchlists": "My Watchlists",
|
||||||
"public_watchlists": "Public Watchlists",
|
"public_watchlists": "Watchlist publique",
|
||||||
"create_title": "Create Watchlist",
|
"create_title": "Créer une Watchlist",
|
||||||
"edit_title": "Edit Watchlist",
|
"edit_title": "Modifier la Watchlist",
|
||||||
"create_button": "Create Watchlist",
|
"create_button": "Créer une Watchlist",
|
||||||
"save_button": "Save Changes",
|
"save_button": "Enregistrer les modifications",
|
||||||
"delete_button": "Delete",
|
"delete_button": "Supprimer",
|
||||||
"remove_button": "Remove",
|
"remove_button": "Retirer",
|
||||||
"cancel_button": "Cancel",
|
"cancel_button": "Annuler",
|
||||||
"name_label": "Name",
|
"name_label": "Nom",
|
||||||
"name_placeholder": "Enter watchlist name",
|
"name_placeholder": "Entrer le nom de la playlist",
|
||||||
"description_label": "Description",
|
"description_label": "Description",
|
||||||
"description_placeholder": "Enter description (optional)",
|
"description_placeholder": "Entrez la description (facultatif)",
|
||||||
"is_public_label": "Public Watchlist",
|
"is_public_label": "Public Watchlist",
|
||||||
"is_public_description": "Allow others to view this watchlist",
|
"is_public_description": "Autoriser d'autres personnes à voir cette liste de suivi",
|
||||||
"allowed_type_label": "Content Type",
|
"allowed_type_label": "Type de contenu",
|
||||||
"sort_order_label": "Default Sort Order",
|
"sort_order_label": "Ordre de tri par défaut",
|
||||||
"empty_title": "No Watchlists",
|
"empty_title": "Pas de Watchlists",
|
||||||
"empty_description": "Create your first watchlist to start organizing your media",
|
"empty_description": "Créez votre première liste de suivi pour commencer à organiser vos médias",
|
||||||
"empty_watchlist": "This watchlist is empty",
|
"empty_watchlist": "Cette liste de suivi est vide",
|
||||||
"empty_watchlist_hint": "Add items from your library to this watchlist",
|
"empty_watchlist_hint": "Ajouter des éléments de votre bibliothèque à cette liste de suivi",
|
||||||
"not_configured_title": "Streamystats Not Configured",
|
"not_configured_title": "Streamystats non configuré",
|
||||||
"not_configured_description": "Configure Streamystats in settings to use watchlists",
|
"not_configured_description": "Configurer Streamystats dans les paramètres pour utiliser les listes de suivi",
|
||||||
"go_to_settings": "Go to Settings",
|
"go_to_settings": "Accédez aux Paramètres",
|
||||||
"add_to_watchlist": "Add to Watchlist",
|
"add_to_watchlist": "Ajouter à la Watchlist",
|
||||||
"remove_from_watchlist": "Remove from Watchlist",
|
"remove_from_watchlist": "Retirer de la Watchlist",
|
||||||
"select_watchlist": "Select Watchlist",
|
"select_watchlist": "Sélectionner la liste de suivi",
|
||||||
"create_new": "Create New Watchlist",
|
"create_new": "Créer une Watchlist",
|
||||||
"item": "item",
|
"item": "médias",
|
||||||
"items": "items",
|
"items": "élément",
|
||||||
"public": "Public",
|
"public": "Publique",
|
||||||
"private": "Private",
|
"private": "Privée",
|
||||||
"you": "You",
|
"you": "Vous-même",
|
||||||
"by_owner": "By another user",
|
"by_owner": "Par un autre utilisateur",
|
||||||
"not_found": "Watchlist not found",
|
"not_found": "Playlist introuvable",
|
||||||
"delete_confirm_title": "Delete Watchlist",
|
"delete_confirm_title": "Supprimer la Watchlist",
|
||||||
"delete_confirm_message": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.",
|
"delete_confirm_message": "Tous les médias (par défaut)",
|
||||||
"remove_item_title": "Remove from Watchlist",
|
"remove_item_title": "Retirer de la Watchlist",
|
||||||
"remove_item_message": "Remove \"{{name}}\" from this watchlist?",
|
"remove_item_message": "Retirer «{{name}}» de cette liste de suivi?",
|
||||||
"loading": "Loading watchlists...",
|
"loading": "Chargement des listes de suivi...",
|
||||||
"no_compatible_watchlists": "No compatible watchlists",
|
"no_compatible_watchlists": "Aucune liste de suivi compatible",
|
||||||
"create_one_first": "Create a watchlist that accepts this content type"
|
"create_one_first": "Créer une liste de suivi qui accepte ce type de contenu"
|
||||||
},
|
},
|
||||||
"playback_speed": {
|
"playback_speed": {
|
||||||
"title": "Playback Speed",
|
"title": "Vitesse de lecture",
|
||||||
"apply_to": "Apply To",
|
"apply_to": "Appliquer à",
|
||||||
"speed": "Speed",
|
"speed": "Vitesse",
|
||||||
"scope": {
|
"scope": {
|
||||||
"media": "This media only",
|
"media": "Ce média uniquement",
|
||||||
"show": "This show",
|
"show": "Cette série",
|
||||||
"all": "All media (default)"
|
"all": "Tous les médias (par défaut)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,9 +34,9 @@
|
|||||||
"search_for_local_servers": "Ricerca dei server locali",
|
"search_for_local_servers": "Ricerca dei server locali",
|
||||||
"searching": "Cercando...",
|
"searching": "Cercando...",
|
||||||
"servers": "Server",
|
"servers": "Server",
|
||||||
"saved": "Saved",
|
"saved": "Salvato",
|
||||||
"session_expired": "Session Expired",
|
"session_expired": "Session Expired",
|
||||||
"please_login_again": "Your saved session has expired. Please log in again.",
|
"please_login_again": "La tua sessione è scaduta. Si prega di eseguire nuovamente l'accesso.",
|
||||||
"remove_saved_login": "Remove Saved Login",
|
"remove_saved_login": "Remove Saved Login",
|
||||||
"remove_saved_login_description": "This will remove your saved credentials for this server. You'll need to enter your username and password again next time.",
|
"remove_saved_login_description": "This will remove your saved credentials for this server. You'll need to enter your username and password again next time.",
|
||||||
"accounts_count": "{{count}} accounts",
|
"accounts_count": "{{count}} accounts",
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
},
|
},
|
||||||
"network": {
|
"network": {
|
||||||
"title": "Network",
|
"title": "Network",
|
||||||
"local_network": "Local Network",
|
"local_network": "",
|
||||||
"auto_switch_enabled": "Auto-switch when at home",
|
"auto_switch_enabled": "Auto-switch when at home",
|
||||||
"auto_switch_description": "Automatically switch to local URL when connected to home WiFi",
|
"auto_switch_description": "Automatically switch to local URL when connected to home WiFi",
|
||||||
"local_url": "Local URL",
|
"local_url": "Local URL",
|
||||||
@@ -137,7 +137,7 @@
|
|||||||
"no_networks_configured": "No networks configured",
|
"no_networks_configured": "No networks configured",
|
||||||
"add_network_hint": "Add your home WiFi network to enable auto-switching",
|
"add_network_hint": "Add your home WiFi network to enable auto-switching",
|
||||||
"current_wifi": "Current WiFi",
|
"current_wifi": "Current WiFi",
|
||||||
"using_url": "Using",
|
"using_url": "Sta utilizzando",
|
||||||
"local": "Local URL",
|
"local": "Local URL",
|
||||||
"remote": "Remote URL",
|
"remote": "Remote URL",
|
||||||
"not_connected": "Not connected",
|
"not_connected": "Not connected",
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
"connect_button": "Verbinden",
|
"connect_button": "Verbinden",
|
||||||
"previous_servers": "vorige servers",
|
"previous_servers": "vorige servers",
|
||||||
"clear_button": "Wissen",
|
"clear_button": "Wissen",
|
||||||
"swipe_to_remove": "Swipe to remove",
|
"swipe_to_remove": "Swipe om te verwijderen.",
|
||||||
"search_for_local_servers": "Zoek naar lokale servers",
|
"search_for_local_servers": "Zoek naar lokale servers",
|
||||||
"searching": "Zoeken...",
|
"searching": "Zoeken...",
|
||||||
"servers": "Servers",
|
"servers": "Servers",
|
||||||
@@ -40,38 +40,38 @@
|
|||||||
"remove_saved_login": "Opgeslagen login verwijderen",
|
"remove_saved_login": "Opgeslagen login verwijderen",
|
||||||
"remove_saved_login_description": "Hiermee worden uw opgeslagen gegevens voor deze server verwijderd. U moet uw gebruikersnaam en wachtwoord de volgende keer opnieuw invoeren.",
|
"remove_saved_login_description": "Hiermee worden uw opgeslagen gegevens voor deze server verwijderd. U moet uw gebruikersnaam en wachtwoord de volgende keer opnieuw invoeren.",
|
||||||
"accounts_count": "{{count}} accounts",
|
"accounts_count": "{{count}} accounts",
|
||||||
"select_account": "Select Account",
|
"select_account": "Account selecteren",
|
||||||
"add_account": "Add Account",
|
"add_account": "Account toevoegen",
|
||||||
"remove_account_description": "This will remove the saved credentials for {{username}}."
|
"remove_account_description": "Hiermee worden de opgeslagen inloggegevens voor {{username}} verwijderd."
|
||||||
},
|
},
|
||||||
"save_account": {
|
"save_account": {
|
||||||
"title": "Save Account",
|
"title": "Account opslaan",
|
||||||
"save_for_later": "Save this account",
|
"save_for_later": "Dit account opslaan",
|
||||||
"security_option": "Security Option",
|
"security_option": "Beveiligingsopties",
|
||||||
"no_protection": "No protection",
|
"no_protection": "Geen beveiliging",
|
||||||
"no_protection_desc": "Quick login without authentication",
|
"no_protection_desc": "Snelle login zonder authenticatie",
|
||||||
"pin_code": "PIN code",
|
"pin_code": "Pincode",
|
||||||
"pin_code_desc": "4-digit PIN required when switching",
|
"pin_code_desc": "4-cijferige pincode vereist bij wisselen",
|
||||||
"password": "Re-enter password",
|
"password": "Wachtwoord opnieuw invoeren",
|
||||||
"password_desc": "Password required when switching",
|
"password_desc": "Wachtwoord vereist bij wisselen",
|
||||||
"save_button": "Save",
|
"save_button": "Opslaan",
|
||||||
"cancel_button": "Cancel"
|
"cancel_button": "Annuleren"
|
||||||
},
|
},
|
||||||
"pin": {
|
"pin": {
|
||||||
"enter_pin": "Enter PIN",
|
"enter_pin": "Pincode invoeren",
|
||||||
"enter_pin_for": "Enter PIN for {{username}}",
|
"enter_pin_for": "Pincode voor {{username}} invoeren",
|
||||||
"enter_4_digits": "Enter 4 digits",
|
"enter_4_digits": "Voer 6 cijfers in",
|
||||||
"invalid_pin": "Invalid PIN",
|
"invalid_pin": "Ongeldige pincode",
|
||||||
"setup_pin": "Set Up PIN",
|
"setup_pin": "Pincode instellen",
|
||||||
"confirm_pin": "Confirm PIN",
|
"confirm_pin": "Pincode bevestigen",
|
||||||
"pins_dont_match": "PINs don't match",
|
"pins_dont_match": "Pincodes komen niet overeen",
|
||||||
"forgot_pin": "Forgot PIN?",
|
"forgot_pin": "Pincode vergeten?",
|
||||||
"forgot_pin_desc": "Your saved credentials will be removed"
|
"forgot_pin_desc": "Je opgeslagen inloggegevens worden verwijderd"
|
||||||
},
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"enter_password": "Enter Password",
|
"enter_password": "Voer wachtwoord in",
|
||||||
"enter_password_for": "Enter password for {{username}}",
|
"enter_password_for": "Voer wachtwoord voor {{username}} in",
|
||||||
"invalid_password": "Invalid password"
|
"invalid_password": "Ongeldig wachtwoord"
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"checking_server_connection": "Serververbinding controleren...",
|
"checking_server_connection": "Serververbinding controleren...",
|
||||||
@@ -84,7 +84,7 @@
|
|||||||
"server_unreachable": "Server onbereikbaar",
|
"server_unreachable": "Server onbereikbaar",
|
||||||
"server_unreachable_message": "Kon de server niet bereiken.\nControleer uw netwerkverbinding.",
|
"server_unreachable_message": "Kon de server niet bereiken.\nControleer uw netwerkverbinding.",
|
||||||
"oops": "Oeps!",
|
"oops": "Oeps!",
|
||||||
"error_message": "Er ging iets fout\nGelieve af en aan te melden.",
|
"error_message": "Er ging iets fout\nProbeer opnieuw in te loggen.",
|
||||||
"continue_watching": "Verder Kijken",
|
"continue_watching": "Verder Kijken",
|
||||||
"next_up": "Volgende",
|
"next_up": "Volgende",
|
||||||
"continue_and_next_up": "Doorgaan & Volgende",
|
"continue_and_next_up": "Doorgaan & Volgende",
|
||||||
@@ -124,32 +124,32 @@
|
|||||||
"hide_remote_session_button": "Verberg Knop voor Externe Sessie"
|
"hide_remote_session_button": "Verberg Knop voor Externe Sessie"
|
||||||
},
|
},
|
||||||
"network": {
|
"network": {
|
||||||
"title": "Network",
|
"title": "Netwerk",
|
||||||
"local_network": "Local Network",
|
"local_network": "Lokaal netwerk",
|
||||||
"auto_switch_enabled": "Auto-switch when at home",
|
"auto_switch_enabled": "Automatisch wisselen wanneer thuis",
|
||||||
"auto_switch_description": "Automatically switch to local URL when connected to home WiFi",
|
"auto_switch_description": "Automatisch wisselen naar lokale URL wanneer verbonden met thuisnetwerk",
|
||||||
"local_url": "Local URL",
|
"local_url": "Lokale URL",
|
||||||
"local_url_hint": "Enter your local server address (e.g., http://192.168.1.100:8096)",
|
"local_url_hint": "Voer uw lokale serveradres in (bijv. http://192.168.1.100:8096)",
|
||||||
"local_url_placeholder": "http://192.168.1.100:8096",
|
"local_url_placeholder": "http://192.168.1.100:8096",
|
||||||
"home_wifi_networks": "Home WiFi Networks",
|
"home_wifi_networks": "Wi-Fi netwerken",
|
||||||
"add_current_network": "Add \"{{ssid}}\"",
|
"add_current_network": "Voeg \"{{ssid}} \" toe",
|
||||||
"not_connected_to_wifi": "Not connected to WiFi",
|
"not_connected_to_wifi": "Niet verbonden met Wi-Fi",
|
||||||
"no_networks_configured": "No networks configured",
|
"no_networks_configured": "Geen netwerken geconfigureerd",
|
||||||
"add_network_hint": "Add your home WiFi network to enable auto-switching",
|
"add_network_hint": "Voeg je thuisnetwerk toe om automatisch wisselen in te schakelen",
|
||||||
"current_wifi": "Current WiFi",
|
"current_wifi": "Huidige Wi-Fi",
|
||||||
"using_url": "Using",
|
"using_url": "Gebruik makend van",
|
||||||
"local": "Local URL",
|
"local": "Lokale URL",
|
||||||
"remote": "Remote URL",
|
"remote": "Externe URL",
|
||||||
"not_connected": "Not connected",
|
"not_connected": "Niet verbonden",
|
||||||
"current_server": "Current Server",
|
"current_server": "Huidige Server",
|
||||||
"remote_url": "Remote URL",
|
"remote_url": "Externe URL",
|
||||||
"active_url": "Active URL",
|
"active_url": "Actieve URL",
|
||||||
"not_configured": "Not configured",
|
"not_configured": "Niet geconfigureerd",
|
||||||
"network_added": "Network added",
|
"network_added": "Netwerk toegevoegd",
|
||||||
"network_already_added": "Network already added",
|
"network_already_added": "Netwerk reeds toegevoegd",
|
||||||
"no_wifi_connected": "Not connected to WiFi",
|
"no_wifi_connected": "Niet verbonden met Wi-Fi",
|
||||||
"permission_denied": "Location permission denied",
|
"permission_denied": "Locatie toestemming geweigerd",
|
||||||
"permission_denied_explanation": "Location permission is required to detect WiFi network for auto-switching. Please enable it in Settings."
|
"permission_denied_explanation": "Locatie permissie is vereist om Wifi-netwerk te kunnen detecteren voor automatisch wisselen. Schakel het in via Instellingen."
|
||||||
},
|
},
|
||||||
"user_info": {
|
"user_info": {
|
||||||
"user_info_title": "Gebruiker Info",
|
"user_info_title": "Gebruiker Info",
|
||||||
@@ -195,11 +195,11 @@
|
|||||||
"none": "Geen",
|
"none": "Geen",
|
||||||
"language": "Taal",
|
"language": "Taal",
|
||||||
"transcode_mode": {
|
"transcode_mode": {
|
||||||
"title": "Audio Transcoding",
|
"title": "Audio-transcoding",
|
||||||
"description": "Controls how surround audio (7.1, TrueHD, DTS-HD) is handled",
|
"description": "Bepaalt hoe surround audio (7.1, TrueHD, DTS-HD) wordt behandeld",
|
||||||
"auto": "Auto",
|
"auto": "Automatisch",
|
||||||
"stereo": "Force Stereo",
|
"stereo": "Stereo forceren",
|
||||||
"5_1": "Allow 5.1",
|
"5_1": "5.1 toestaan",
|
||||||
"passthrough": "Passthrough"
|
"passthrough": "Passthrough"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -231,7 +231,7 @@
|
|||||||
"Black": "Zwart",
|
"Black": "Zwart",
|
||||||
"Gray": "Grijs",
|
"Gray": "Grijs",
|
||||||
"Silver": "Zilver",
|
"Silver": "Zilver",
|
||||||
"White": "wit",
|
"White": "Wit",
|
||||||
"Maroon": "Kastanjebruin",
|
"Maroon": "Kastanjebruin",
|
||||||
"Red": "Rood",
|
"Red": "Rood",
|
||||||
"Fuchsia": "Fuchsia",
|
"Fuchsia": "Fuchsia",
|
||||||
@@ -259,14 +259,14 @@
|
|||||||
"hardware_decode_description": "Gebruik hardware acceleratie voor video-decodering. Uitschakelen als u problemen met afspelen ondervindt."
|
"hardware_decode_description": "Gebruik hardware acceleratie voor video-decodering. Uitschakelen als u problemen met afspelen ondervindt."
|
||||||
},
|
},
|
||||||
"vlc_subtitles": {
|
"vlc_subtitles": {
|
||||||
"title": "VLC Subtitle Settings",
|
"title": "VLC ondertitel instellingen",
|
||||||
"hint": "Customize subtitle appearance for VLC player. Changes take effect on next playback.",
|
"hint": "Aanpassen van ondertiteling voor VLC-speler. Wijzigingen worden toegepast bij het afspelen.",
|
||||||
"text_color": "Text Color",
|
"text_color": "Tekstkleur",
|
||||||
"background_color": "Background Color",
|
"background_color": "Achtergrondkleur",
|
||||||
"background_opacity": "Background Opacity",
|
"background_opacity": "Doorzichtigheid achtergrond",
|
||||||
"outline_color": "Outline Color",
|
"outline_color": "Kleur omlijning",
|
||||||
"outline_opacity": "Outline Opacity",
|
"outline_opacity": "Omtrek opaciteit",
|
||||||
"outline_thickness": "Outline Thickness",
|
"outline_thickness": "Omtrek dikte",
|
||||||
"bold": "Bold Text",
|
"bold": "Bold Text",
|
||||||
"margin": "Bottom Margin"
|
"margin": "Bottom Margin"
|
||||||
},
|
},
|
||||||
@@ -306,7 +306,7 @@
|
|||||||
"disable_haptic_feedback": "Haptische feedback uitschakelen",
|
"disable_haptic_feedback": "Haptische feedback uitschakelen",
|
||||||
"default_quality": "Standaard kwaliteit",
|
"default_quality": "Standaard kwaliteit",
|
||||||
"default_playback_speed": "Standaard Afspeelsnelheid",
|
"default_playback_speed": "Standaard Afspeelsnelheid",
|
||||||
"auto_play_next_episode": "Auto-play Next Episode",
|
"auto_play_next_episode": "Volgende aflevering automatisch afspelen",
|
||||||
"max_auto_play_episode_count": "Max Automatisch Aflevering Aantal",
|
"max_auto_play_episode_count": "Max Automatisch Aflevering Aantal",
|
||||||
"disabled": "Uitgeschakeld"
|
"disabled": "Uitgeschakeld"
|
||||||
},
|
},
|
||||||
@@ -378,12 +378,12 @@
|
|||||||
"enable_promoted_watchlists": "Gepromote Kijklijst",
|
"enable_promoted_watchlists": "Gepromote Kijklijst",
|
||||||
"hide_watchlists_tab": "Hide Watchlists Tab",
|
"hide_watchlists_tab": "Hide Watchlists Tab",
|
||||||
"home_sections_hint": "Show personalized recommendations and promoted watchlists from Streamystats on the home page.",
|
"home_sections_hint": "Show personalized recommendations and promoted watchlists from Streamystats on the home page.",
|
||||||
"recommended_movies": "Recommended Movies",
|
"recommended_movies": "Aanbevolen films",
|
||||||
"recommended_series": "Recommended Series",
|
"recommended_series": "Aanbevolen serie",
|
||||||
"toasts": {
|
"toasts": {
|
||||||
"saved": "Saved",
|
"saved": "Opgeslagen",
|
||||||
"refreshed": "Settings refreshed from server",
|
"refreshed": "Settings refreshed from server",
|
||||||
"disabled": "Streamystats disabled"
|
"disabled": "Streamystats uitgeschakeld"
|
||||||
},
|
},
|
||||||
"refresh_from_server": "Refresh Settings from Server"
|
"refresh_from_server": "Refresh Settings from Server"
|
||||||
},
|
},
|
||||||
@@ -402,24 +402,24 @@
|
|||||||
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
||||||
"enable_music_cache": "Enable Music Cache",
|
"enable_music_cache": "Enable Music Cache",
|
||||||
"clear_music_cache": "Clear Music Cache",
|
"clear_music_cache": "Clear Music Cache",
|
||||||
"music_cache_size": "{{size}} cached",
|
"music_cache_size": "{{size}} gecached",
|
||||||
"music_cache_cleared": "Music cache cleared",
|
"music_cache_cleared": "Muziek cache gewist",
|
||||||
"delete_all_downloaded_songs": "Delete All Downloaded Songs",
|
"delete_all_downloaded_songs": "Verwijder alle gedownloade liedjes",
|
||||||
"downloaded_songs_size": "{{size}} downloaded",
|
"downloaded_songs_size": "{{size}} gedownload",
|
||||||
"downloaded_songs_deleted": "Downloaded songs deleted"
|
"downloaded_songs_deleted": "Downloaded songs deleted"
|
||||||
},
|
},
|
||||||
"intro": {
|
"intro": {
|
||||||
"title": "Intro",
|
"title": "Intro",
|
||||||
"show_intro": "Toon intro",
|
"show_intro": "Toon intro",
|
||||||
"reset_intro": "intro opnieuw instellen"
|
"reset_intro": "Reset Intro"
|
||||||
},
|
},
|
||||||
"logs": {
|
"logs": {
|
||||||
"logs_title": "Logboek",
|
"logs_title": "Logboek",
|
||||||
"export_logs": "Export logs",
|
"export_logs": "Export logs",
|
||||||
"click_for_more_info": "Click for more info",
|
"click_for_more_info": "Klik voor meer info",
|
||||||
"level": "Niveau",
|
"level": "Niveau",
|
||||||
"no_logs_available": "Geen logs beschikbaar",
|
"no_logs_available": "Geen logs beschikbaar",
|
||||||
"delete_all_logs": "Verwijder alle logs"
|
"delete_all_logs": "Alle logs verwijderen"
|
||||||
},
|
},
|
||||||
"languages": {
|
"languages": {
|
||||||
"title": "Talen",
|
"title": "Talen",
|
||||||
@@ -500,14 +500,14 @@
|
|||||||
"play": "Afspelen",
|
"play": "Afspelen",
|
||||||
"none": "Geen",
|
"none": "Geen",
|
||||||
"track": "Spoor",
|
"track": "Spoor",
|
||||||
"cancel": "Cancel",
|
"cancel": "Annuleren",
|
||||||
"delete": "Delete",
|
"delete": "Verwijderen",
|
||||||
"ok": "OK",
|
"ok": "Oké",
|
||||||
"remove": "Remove",
|
"remove": "Verwijderen",
|
||||||
"next": "Next",
|
"next": "Volgende",
|
||||||
"back": "Back",
|
"back": "Terug",
|
||||||
"continue": "Continue",
|
"continue": "Doorgaan",
|
||||||
"verifying": "Verifying..."
|
"verifying": "Verifiëren..."
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
"search": "Zoek...",
|
"search": "Zoek...",
|
||||||
@@ -521,10 +521,10 @@
|
|||||||
"episodes": "Afleveringen",
|
"episodes": "Afleveringen",
|
||||||
"collections": "Collecties",
|
"collections": "Collecties",
|
||||||
"actors": "Acteurs",
|
"actors": "Acteurs",
|
||||||
"artists": "Artists",
|
"artists": "Artiesten",
|
||||||
"albums": "Albums",
|
"albums": "Albums",
|
||||||
"songs": "Songs",
|
"songs": "Nummers",
|
||||||
"playlists": "Playlists",
|
"playlists": "Afspeellijsten",
|
||||||
"request_movies": "Vraag films aan",
|
"request_movies": "Vraag films aan",
|
||||||
"request_series": "Vraag series aan",
|
"request_series": "Vraag series aan",
|
||||||
"recently_added": "Recent Toegevoegd",
|
"recently_added": "Recent Toegevoegd",
|
||||||
@@ -572,7 +572,7 @@
|
|||||||
"genres": "Genres",
|
"genres": "Genres",
|
||||||
"years": "Jaren",
|
"years": "Jaren",
|
||||||
"sort_by": "Sorteren op",
|
"sort_by": "Sorteren op",
|
||||||
"filter_by": "Filter By",
|
"filter_by": "Filteren op",
|
||||||
"sort_order": "Sorteer volgorde",
|
"sort_order": "Sorteer volgorde",
|
||||||
"tags": "Labels"
|
"tags": "Labels"
|
||||||
}
|
}
|
||||||
@@ -719,127 +719,127 @@
|
|||||||
"favorites": "Favorieten"
|
"favorites": "Favorieten"
|
||||||
},
|
},
|
||||||
"music": {
|
"music": {
|
||||||
"title": "Music",
|
"title": "Muziek",
|
||||||
"tabs": {
|
"tabs": {
|
||||||
"suggestions": "Suggestions",
|
"suggestions": "Suggesties",
|
||||||
"albums": "Albums",
|
"albums": "Albums",
|
||||||
"artists": "Artists",
|
"artists": "Artiesten",
|
||||||
"playlists": "Playlists",
|
"playlists": "Afspeellijsten",
|
||||||
"tracks": "tracks"
|
"tracks": "Nummers"
|
||||||
},
|
},
|
||||||
"filters": {
|
"filters": {
|
||||||
"all": "All"
|
"all": "Alle"
|
||||||
},
|
},
|
||||||
"recently_added": "Recently Added",
|
"recently_added": "Recent toegevoegd",
|
||||||
"recently_played": "Recently Played",
|
"recently_played": "Onlangs afgespeeld",
|
||||||
"frequently_played": "Frequently Played",
|
"frequently_played": "Vaak afgespeeld",
|
||||||
"explore": "Explore",
|
"explore": "Ontdek",
|
||||||
"top_tracks": "Top Tracks",
|
"top_tracks": "Top Tracks",
|
||||||
"play": "Play",
|
"play": "Afspelen",
|
||||||
"shuffle": "Shuffle",
|
"shuffle": "Shuffle",
|
||||||
"play_top_tracks": "Play Top Tracks",
|
"play_top_tracks": "Play Top Tracks",
|
||||||
"no_suggestions": "No suggestions available",
|
"no_suggestions": "Geen suggesties beschikbaar",
|
||||||
"no_albums": "No albums found",
|
"no_albums": "Geen albums gevonden",
|
||||||
"no_artists": "No artists found",
|
"no_artists": "Geen artiesten gevonden",
|
||||||
"no_playlists": "No playlists found",
|
"no_playlists": "Geen afspeellijsten gevonden",
|
||||||
"album_not_found": "Album not found",
|
"album_not_found": "Album niet gevonden",
|
||||||
"artist_not_found": "Artist not found",
|
"artist_not_found": "Artiest niet gevonden",
|
||||||
"playlist_not_found": "Playlist not found",
|
"playlist_not_found": "Afspeellijst niet gevonden",
|
||||||
"track_options": {
|
"track_options": {
|
||||||
"play_next": "Play Next",
|
"play_next": "Speel volgende af",
|
||||||
"add_to_queue": "Add to Queue",
|
"add_to_queue": "Toevoegen aan wachtrij",
|
||||||
"add_to_playlist": "Add to Playlist",
|
"add_to_playlist": "Voeg toe aan afspeellijst",
|
||||||
"download": "Download",
|
"download": "Downloaden",
|
||||||
"downloaded": "Downloaded",
|
"downloaded": "Gedownload",
|
||||||
"downloading": "Downloading...",
|
"downloading": "Downloaden...",
|
||||||
"cached": "Cached",
|
"cached": "Gecached",
|
||||||
"delete_download": "Delete Download",
|
"delete_download": "Download verwijderen",
|
||||||
"delete_cache": "Remove from Cache",
|
"delete_cache": "Verwijderen uit cache",
|
||||||
"go_to_artist": "Go to Artist",
|
"go_to_artist": "Ga naar artiest",
|
||||||
"go_to_album": "Go to Album",
|
"go_to_album": "Ga naar album",
|
||||||
"add_to_favorites": "Add to Favorites",
|
"add_to_favorites": "Toevoegen aan favorieten",
|
||||||
"remove_from_favorites": "Remove from Favorites",
|
"remove_from_favorites": "Verwijderen uit favorieten",
|
||||||
"remove_from_playlist": "Remove from Playlist"
|
"remove_from_playlist": "Verwijder uit afspeellijst"
|
||||||
},
|
},
|
||||||
"playlists": {
|
"playlists": {
|
||||||
"create_playlist": "Create Playlist",
|
"create_playlist": "Afspeellijst aanmaken",
|
||||||
"playlist_name": "Playlist Name",
|
"playlist_name": "Afspeellijst naam",
|
||||||
"enter_name": "Enter playlist name",
|
"enter_name": "Enter playlist name",
|
||||||
"create": "Create",
|
"create": "Aanmaken",
|
||||||
"search_playlists": "Search playlists...",
|
"search_playlists": "Playlist zoeken...",
|
||||||
"added_to": "Added to {{name}}",
|
"added_to": "Toegevoegd aan {{name}}",
|
||||||
"added": "Added to playlist",
|
"added": "Toegevoegd aan playlist",
|
||||||
"removed_from": "Removed from {{name}}",
|
"removed_from": "Verwijderd uit {{name}}",
|
||||||
"removed": "Removed from playlist",
|
"removed": "Verwijderd uit playlist",
|
||||||
"created": "Playlist created",
|
"created": "Playlist created",
|
||||||
"create_new": "Create New Playlist",
|
"create_new": "Create New Playlist",
|
||||||
"failed_to_add": "Failed to add to playlist",
|
"failed_to_add": "Failed to add to playlist",
|
||||||
"failed_to_remove": "Failed to remove from playlist",
|
"failed_to_remove": "Verwijderen uit afspeellijst is mislukt",
|
||||||
"failed_to_create": "Failed to create playlist",
|
"failed_to_create": "Het maken van de afspeellijst is mislukt",
|
||||||
"delete_playlist": "Delete Playlist",
|
"delete_playlist": "Afspeellijst verwijderen",
|
||||||
"delete_confirm": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.",
|
"delete_confirm": "Weet u zeker dat u \"{{name}}\"wilt verwijderen? Deze actie kan niet ongedaan worden gemaakt.",
|
||||||
"deleted": "Playlist deleted",
|
"deleted": "Afspeellijst verwijderd.",
|
||||||
"failed_to_delete": "Failed to delete playlist"
|
"failed_to_delete": "Verwijderen van afspeellijst mislukt"
|
||||||
},
|
},
|
||||||
"sort": {
|
"sort": {
|
||||||
"title": "Sort By",
|
"title": "Sorteren op",
|
||||||
"alphabetical": "Alphabetical",
|
"alphabetical": "Alfabetisch",
|
||||||
"date_created": "Date Created"
|
"date_created": "Aanmaakdatum"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"watchlists": {
|
"watchlists": {
|
||||||
"title": "Watchlists",
|
"title": "Watchlist",
|
||||||
"my_watchlists": "My Watchlists",
|
"my_watchlists": "Mijn watchlists",
|
||||||
"public_watchlists": "Public Watchlists",
|
"public_watchlists": "Public Watchlists",
|
||||||
"create_title": "Create Watchlist",
|
"create_title": "Create Watchlist",
|
||||||
"edit_title": "Edit Watchlist",
|
"edit_title": "Edit Watchlist",
|
||||||
"create_button": "Create Watchlist",
|
"create_button": "Create Watchlist",
|
||||||
"save_button": "Save Changes",
|
"save_button": "Wijzigingen opslaan",
|
||||||
"delete_button": "Delete",
|
"delete_button": "Verwijder",
|
||||||
"remove_button": "Remove",
|
"remove_button": "Verwijderen",
|
||||||
"cancel_button": "Cancel",
|
"cancel_button": "Annuleren",
|
||||||
"name_label": "Name",
|
"name_label": "Naam",
|
||||||
"name_placeholder": "Enter watchlist name",
|
"name_placeholder": "Voer naam van kijklijst in",
|
||||||
"description_label": "Description",
|
"description_label": "Beschrijving",
|
||||||
"description_placeholder": "Enter description (optional)",
|
"description_placeholder": "Voer beschrijving in (optioneel)",
|
||||||
"is_public_label": "Public Watchlist",
|
"is_public_label": "Openbare Kijklijst",
|
||||||
"is_public_description": "Allow others to view this watchlist",
|
"is_public_description": "Sta anderen toe om deze kijklijst te bekijken",
|
||||||
"allowed_type_label": "Content Type",
|
"allowed_type_label": "Inhoudstype",
|
||||||
"sort_order_label": "Default Sort Order",
|
"sort_order_label": "Standaard Sortering",
|
||||||
"empty_title": "No Watchlists",
|
"empty_title": "Geen Kijklijsten",
|
||||||
"empty_description": "Create your first watchlist to start organizing your media",
|
"empty_description": "Maak je eerste kijklijst om je media te organiseren",
|
||||||
"empty_watchlist": "This watchlist is empty",
|
"empty_watchlist": "Deze watchlist is leeg",
|
||||||
"empty_watchlist_hint": "Add items from your library to this watchlist",
|
"empty_watchlist_hint": "Voeg items uit je bibliotheek toe aan deze kijklijst",
|
||||||
"not_configured_title": "Streamystats Not Configured",
|
"not_configured_title": "Streamystats niet geconfigureerd",
|
||||||
"not_configured_description": "Configure Streamystats in settings to use watchlists",
|
"not_configured_description": "Configureer Streamystats in instellingen om kijklijsten te gebruiken",
|
||||||
"go_to_settings": "Go to Settings",
|
"go_to_settings": "Ga naar Instellingen",
|
||||||
"add_to_watchlist": "Add to Watchlist",
|
"add_to_watchlist": "Voeg toe aan kijklijst",
|
||||||
"remove_from_watchlist": "Remove from Watchlist",
|
"remove_from_watchlist": "Verwijder van kijklijst",
|
||||||
"select_watchlist": "Select Watchlist",
|
"select_watchlist": "Selecteer kijklijst",
|
||||||
"create_new": "Create New Watchlist",
|
"create_new": "Nieuwe kijklijst aanmaken",
|
||||||
"item": "item",
|
"item": "item",
|
||||||
"items": "items",
|
"items": "items",
|
||||||
"public": "Public",
|
"public": "Publiek",
|
||||||
"private": "Private",
|
"private": "Privé",
|
||||||
"you": "You",
|
"you": "Jij",
|
||||||
"by_owner": "By another user",
|
"by_owner": "Door een andere gebruiker",
|
||||||
"not_found": "Watchlist not found",
|
"not_found": "Kijklijst niet gevonden",
|
||||||
"delete_confirm_title": "Delete Watchlist",
|
"delete_confirm_title": "Verwijder kijklijst",
|
||||||
"delete_confirm_message": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.",
|
"delete_confirm_message": "Weet u zeker dat u \"{{name}}\"wilt verwijderen? Deze actie kan niet ongedaan worden gemaakt.",
|
||||||
"remove_item_title": "Remove from Watchlist",
|
"remove_item_title": "Verwijder van watchlist",
|
||||||
"remove_item_message": "Remove \"{{name}}\" from this watchlist?",
|
"remove_item_message": "Verwijder \"{{name}}\" uit deze watchlist?",
|
||||||
"loading": "Loading watchlists...",
|
"loading": "Laden van watchlists...",
|
||||||
"no_compatible_watchlists": "No compatible watchlists",
|
"no_compatible_watchlists": "Geen compatibele watchlist",
|
||||||
"create_one_first": "Create a watchlist that accepts this content type"
|
"create_one_first": "Maak een watchlist aan die dit inhoudstype accepteert"
|
||||||
},
|
},
|
||||||
"playback_speed": {
|
"playback_speed": {
|
||||||
"title": "Playback Speed",
|
"title": "Afspeelsnelheid",
|
||||||
"apply_to": "Apply To",
|
"apply_to": "Toepassen op",
|
||||||
"speed": "Speed",
|
"speed": "Snelheid",
|
||||||
"scope": {
|
"scope": {
|
||||||
"media": "This media only",
|
"media": "Alleen deze media",
|
||||||
"show": "This show",
|
"show": "Deze serie",
|
||||||
"all": "All media (default)"
|
"all": "Alle media (standaard)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,44 +34,44 @@
|
|||||||
"search_for_local_servers": "Поиск локальных серверов",
|
"search_for_local_servers": "Поиск локальных серверов",
|
||||||
"searching": "Поиск...",
|
"searching": "Поиск...",
|
||||||
"servers": "Сервера",
|
"servers": "Сервера",
|
||||||
"saved": "Saved",
|
"saved": "Сохранено",
|
||||||
"session_expired": "Session Expired",
|
"session_expired": "Сессия истекла",
|
||||||
"please_login_again": "Your saved session has expired. Please log in again.",
|
"please_login_again": "Ваша сессия истекла. Пожалуйста, войдите снова.",
|
||||||
"remove_saved_login": "Remove Saved Login",
|
"remove_saved_login": "Удалить сохраненный аккаунт",
|
||||||
"remove_saved_login_description": "This will remove your saved credentials for this server. You'll need to enter your username and password again next time.",
|
"remove_saved_login_description": "Ваши сохранённые данные для входа от этого сервера будут удалены. Вам придётся ввести ваши логин и пароль ещё раз.",
|
||||||
"accounts_count": "{{count}} accounts",
|
"accounts_count": "{{count}} аккаунтов",
|
||||||
"select_account": "Select Account",
|
"select_account": "Выбрать аккаунт",
|
||||||
"add_account": "Add Account",
|
"add_account": "Добавить аккаунт",
|
||||||
"remove_account_description": "This will remove the saved credentials for {{username}}."
|
"remove_account_description": "Данные для входа {{username}} будут удалены."
|
||||||
},
|
},
|
||||||
"save_account": {
|
"save_account": {
|
||||||
"title": "Save Account",
|
"title": "Сохранить аккаунт",
|
||||||
"save_for_later": "Save this account",
|
"save_for_later": "Сохранить этот аккаунт",
|
||||||
"security_option": "Security Option",
|
"security_option": "Опции безопасности",
|
||||||
"no_protection": "No protection",
|
"no_protection": "Без защиты",
|
||||||
"no_protection_desc": "Quick login without authentication",
|
"no_protection_desc": "Быстрый вход без ввода данных",
|
||||||
"pin_code": "PIN code",
|
"pin_code": "PIN-код",
|
||||||
"pin_code_desc": "4-digit PIN required when switching",
|
"pin_code_desc": "При переключении будет требоваться 4-значный PIN",
|
||||||
"password": "Re-enter password",
|
"password": "Пароль",
|
||||||
"password_desc": "Password required when switching",
|
"password_desc": "При переключении будет требоваться пароль",
|
||||||
"save_button": "Save",
|
"save_button": "Сохранить",
|
||||||
"cancel_button": "Cancel"
|
"cancel_button": "Отмена"
|
||||||
},
|
},
|
||||||
"pin": {
|
"pin": {
|
||||||
"enter_pin": "Enter PIN",
|
"enter_pin": "Введите PIN",
|
||||||
"enter_pin_for": "Enter PIN for {{username}}",
|
"enter_pin_for": "Введите PIN для {{username}}",
|
||||||
"enter_4_digits": "Enter 4 digits",
|
"enter_4_digits": "Введите 4 цифры",
|
||||||
"invalid_pin": "Invalid PIN",
|
"invalid_pin": "Некорректный PIN",
|
||||||
"setup_pin": "Set Up PIN",
|
"setup_pin": "Установить PIN",
|
||||||
"confirm_pin": "Confirm PIN",
|
"confirm_pin": "Подтвердите PIN",
|
||||||
"pins_dont_match": "PINs don't match",
|
"pins_dont_match": "PIN-коды не совпадают",
|
||||||
"forgot_pin": "Forgot PIN?",
|
"forgot_pin": "Забыли PIN?",
|
||||||
"forgot_pin_desc": "Your saved credentials will be removed"
|
"forgot_pin_desc": "Ваши данные для входа будут удалены"
|
||||||
},
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"enter_password": "Enter Password",
|
"enter_password": "Введите пароль",
|
||||||
"enter_password_for": "Enter password for {{username}}",
|
"enter_password_for": "Введите пароль для {{username}}",
|
||||||
"invalid_password": "Invalid password"
|
"invalid_password": "Неверный пароль"
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"checking_server_connection": "Проверка соединения с сервером...",
|
"checking_server_connection": "Проверка соединения с сервером...",
|
||||||
@@ -82,12 +82,12 @@
|
|||||||
"go_to_downloads": "В загрузки",
|
"go_to_downloads": "В загрузки",
|
||||||
"retry": "Повторить",
|
"retry": "Повторить",
|
||||||
"server_unreachable": "Сервер недоступен",
|
"server_unreachable": "Сервер недоступен",
|
||||||
"server_unreachable_message": "Could not reach the server.\nPlease check your network connection.",
|
"server_unreachable_message": "Не удалось соединиться с сервером.\nПожалуйста, проверьте настройки сети.",
|
||||||
"oops": "Упс!",
|
"oops": "Упс!",
|
||||||
"error_message": "Что-то пошло не так.\nПожалуйста выйдите и зайдите снова.",
|
"error_message": "Что-то пошло не так.\nПожалуйста выйдите и зайдите снова.",
|
||||||
"continue_watching": "Продолжить просмотр",
|
"continue_watching": "Продолжить",
|
||||||
"next_up": "Следующее",
|
"next_up": "Далее",
|
||||||
"continue_and_next_up": "Continue & Next Up",
|
"continue_and_next_up": "Продолжить и Далее",
|
||||||
"recently_added_in": "Недавно добавлено в {{libraryName}}",
|
"recently_added_in": "Недавно добавлено в {{libraryName}}",
|
||||||
"suggested_movies": "Предложенные фильмы",
|
"suggested_movies": "Предложенные фильмы",
|
||||||
"suggested_episodes": "Предложенные серии",
|
"suggested_episodes": "Предложенные серии",
|
||||||
@@ -110,46 +110,46 @@
|
|||||||
"settings_title": "Настройки",
|
"settings_title": "Настройки",
|
||||||
"log_out_button": "Выйти",
|
"log_out_button": "Выйти",
|
||||||
"categories": {
|
"categories": {
|
||||||
"title": "Categories"
|
"title": "Категории"
|
||||||
},
|
},
|
||||||
"playback_controls": {
|
"playback_controls": {
|
||||||
"title": "Playback & Controls"
|
"title": "Воспроизведение и управление"
|
||||||
},
|
},
|
||||||
"audio_subtitles": {
|
"audio_subtitles": {
|
||||||
"title": "Audio & Subtitles"
|
"title": "Аудио и субтитры"
|
||||||
},
|
},
|
||||||
"appearance": {
|
"appearance": {
|
||||||
"title": "Appearance",
|
"title": "Внешний вид",
|
||||||
"merge_next_up_continue_watching": "Merge Continue Watching & Next Up",
|
"merge_next_up_continue_watching": "Объединить «Продолжить» и «Далее»",
|
||||||
"hide_remote_session_button": "Hide Remote Session Button"
|
"hide_remote_session_button": "Скрыть кнопку «Удалённый сеанс»"
|
||||||
},
|
},
|
||||||
"network": {
|
"network": {
|
||||||
"title": "Network",
|
"title": "Сеть",
|
||||||
"local_network": "Local Network",
|
"local_network": "Локальная сеть",
|
||||||
"auto_switch_enabled": "Auto-switch when at home",
|
"auto_switch_enabled": "Переключаться дома автоматически",
|
||||||
"auto_switch_description": "Automatically switch to local URL when connected to home WiFi",
|
"auto_switch_description": "Автоматически переключаться на локальный URL при присоединении к домашней WiFi сети",
|
||||||
"local_url": "Local URL",
|
"local_url": "Локальный URL",
|
||||||
"local_url_hint": "Enter your local server address (e.g., http://192.168.1.100:8096)",
|
"local_url_hint": "Введите локальный URL вашего сервера (e.g., http://192.168.1.100:8096)",
|
||||||
"local_url_placeholder": "http://192.168.1.100:8096",
|
"local_url_placeholder": "http://192.168.1.100:8096",
|
||||||
"home_wifi_networks": "Home WiFi Networks",
|
"home_wifi_networks": "Домашние WiFi сети",
|
||||||
"add_current_network": "Add \"{{ssid}}\"",
|
"add_current_network": "Добавить \"{{ssid}}\"",
|
||||||
"not_connected_to_wifi": "Not connected to WiFi",
|
"not_connected_to_wifi": "Нет WiFi соединения",
|
||||||
"no_networks_configured": "No networks configured",
|
"no_networks_configured": "Нет настроенных сетей",
|
||||||
"add_network_hint": "Add your home WiFi network to enable auto-switching",
|
"add_network_hint": "Добавьте вашу домашнюю сеть WiFi для включения автоматического переключения",
|
||||||
"current_wifi": "Current WiFi",
|
"current_wifi": "Текущая WiFi сеть",
|
||||||
"using_url": "Using",
|
"using_url": "Используется",
|
||||||
"local": "Local URL",
|
"local": "Локальный",
|
||||||
"remote": "Remote URL",
|
"remote": "Внешний",
|
||||||
"not_connected": "Not connected",
|
"not_connected": "Нет соединения",
|
||||||
"current_server": "Current Server",
|
"current_server": "Текущий сервер",
|
||||||
"remote_url": "Remote URL",
|
"remote_url": "Внешний URL",
|
||||||
"active_url": "Active URL",
|
"active_url": "Активный URL",
|
||||||
"not_configured": "Not configured",
|
"not_configured": "Не настроено",
|
||||||
"network_added": "Network added",
|
"network_added": "Сеть добавлена",
|
||||||
"network_already_added": "Network already added",
|
"network_already_added": "Сеть уже добавлена",
|
||||||
"no_wifi_connected": "Not connected to WiFi",
|
"no_wifi_connected": "Нет WiFi соединения",
|
||||||
"permission_denied": "Location permission denied",
|
"permission_denied": "Нет доступа к местоположению",
|
||||||
"permission_denied_explanation": "Location permission is required to detect WiFi network for auto-switching. Please enable it in Settings."
|
"permission_denied_explanation": "Разрешение на доступ к местоположению обязательно для обнаружения WiFi сети при автоматическом переключении. Пожалуйста, включите его в настройках."
|
||||||
},
|
},
|
||||||
"user_info": {
|
"user_info": {
|
||||||
"user_info_title": "Информация о пользователе",
|
"user_info_title": "Информация о пользователе",
|
||||||
@@ -170,22 +170,22 @@
|
|||||||
},
|
},
|
||||||
"media_controls": {
|
"media_controls": {
|
||||||
"media_controls_title": "Медиа-контроль",
|
"media_controls_title": "Медиа-контроль",
|
||||||
"forward_skip_length": "Длина пропуска вперед",
|
"forward_skip_length": "Шаг перемотки вперёд",
|
||||||
"rewind_length": "Длина перемотки",
|
"rewind_length": "Шаг перемотки назад",
|
||||||
"seconds_unit": "c"
|
"seconds_unit": "c"
|
||||||
},
|
},
|
||||||
"gesture_controls": {
|
"gesture_controls": {
|
||||||
"gesture_controls_title": "Управление жестами",
|
"gesture_controls_title": "Управление жестами",
|
||||||
"horizontal_swipe_skip": "Горизонтальный свайп, чтобы пропустить",
|
"horizontal_swipe_skip": "Горизонтальный свайп для перемотки",
|
||||||
"horizontal_swipe_skip_description": "Проведите влево/вправо, когда элементы управления скрыты, чтобы пропустить",
|
"horizontal_swipe_skip_description": "Проведите влево/вправо, когда элементы управления скрыты, чтобы пропустить",
|
||||||
"left_side_brightness": "Управление яркостью левой стороны",
|
"left_side_brightness": "Управление яркостью левой стороны",
|
||||||
"left_side_brightness_description": "Смахните вверх/вниз на левой стороне для настройки яркости",
|
"left_side_brightness_description": "Смахните вверх/вниз на левой стороне для настройки яркости",
|
||||||
"right_side_volume": "Управление громкостью справа",
|
"right_side_volume": "Управление громкостью справа",
|
||||||
"right_side_volume_description": "Свайп вверх/вниз с правой стороны для настройки громкости",
|
"right_side_volume_description": "Свайп вверх/вниз с правой стороны для настройки громкости",
|
||||||
"hide_volume_slider": "Hide Volume Slider",
|
"hide_volume_slider": "Скрыть индикатор громкости",
|
||||||
"hide_volume_slider_description": "Hide the volume slider in the video player",
|
"hide_volume_slider_description": "Скрывает индикатор громкости в плеере",
|
||||||
"hide_brightness_slider": "Hide Brightness Slider",
|
"hide_brightness_slider": "Скрыть индикатор яркости",
|
||||||
"hide_brightness_slider_description": "Hide the brightness slider in the video player"
|
"hide_brightness_slider_description": "Скрывает индикатор яркости в плеере"
|
||||||
},
|
},
|
||||||
"audio": {
|
"audio": {
|
||||||
"audio_title": "Аудио",
|
"audio_title": "Аудио",
|
||||||
@@ -195,17 +195,17 @@
|
|||||||
"none": "Отсутствует",
|
"none": "Отсутствует",
|
||||||
"language": "Язык",
|
"language": "Язык",
|
||||||
"transcode_mode": {
|
"transcode_mode": {
|
||||||
"title": "Audio Transcoding",
|
"title": "Перекодировка аудио",
|
||||||
"description": "Controls how surround audio (7.1, TrueHD, DTS-HD) is handled",
|
"description": "Управляет обработкой пространственного звука (7.1, TrueHD, DTS-HD)",
|
||||||
"auto": "Auto",
|
"auto": "Авто",
|
||||||
"stereo": "Force Stereo",
|
"stereo": "Принудительно в стерео",
|
||||||
"5_1": "Allow 5.1",
|
"5_1": "Разрешить 5.1",
|
||||||
"passthrough": "Passthrough"
|
"passthrough": "Ничего не изменять"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"subtitles": {
|
"subtitles": {
|
||||||
"subtitle_title": "Субтитры",
|
"subtitle_title": "Субтитры",
|
||||||
"subtitle_hint": "Настроить субтитры.",
|
"subtitle_hint": "Настройки отображения субтитров",
|
||||||
"subtitle_language": "Язык субтитров",
|
"subtitle_language": "Язык субтитров",
|
||||||
"subtitle_mode": "Режим субтитров",
|
"subtitle_mode": "Режим субтитров",
|
||||||
"set_subtitle_track": "Устанавливать субтитры из предыдущего элемента",
|
"set_subtitle_track": "Устанавливать субтитры из предыдущего элемента",
|
||||||
@@ -226,24 +226,24 @@
|
|||||||
"outline_thickness": "Толщина контура",
|
"outline_thickness": "Толщина контура",
|
||||||
"background_opacity": "Прозрачность фона",
|
"background_opacity": "Прозрачность фона",
|
||||||
"outline_opacity": "Прозрачность контура",
|
"outline_opacity": "Прозрачность контура",
|
||||||
"bold_text": "Bold Text",
|
"bold_text": "Жирный",
|
||||||
"colors": {
|
"colors": {
|
||||||
"Black": "Черный",
|
"Black": "Черный",
|
||||||
"Gray": "Серый",
|
"Gray": "Серый",
|
||||||
"Silver": "Серебряный",
|
"Silver": "Серебристый",
|
||||||
"White": "Белый",
|
"White": "Белый",
|
||||||
"Maroon": "Марун",
|
"Maroon": "Бордовый",
|
||||||
"Red": "Красный",
|
"Red": "Красный",
|
||||||
"Fuchsia": "Fuchsia",
|
"Fuchsia": "Пурпурный",
|
||||||
"Yellow": "Жёлтый",
|
"Yellow": "Жёлтый",
|
||||||
"Olive": "Олив",
|
"Olive": "Оливковый",
|
||||||
"Green": "Зелёный",
|
"Green": "Зелёный",
|
||||||
"Teal": "Бирюзовый",
|
"Teal": "Бирюзовый",
|
||||||
"Lime": "Лаймовый",
|
"Lime": "Лаймовый",
|
||||||
"Purple": "Фиолетовый",
|
"Purple": "Фиолетовый",
|
||||||
"Navy": "Тёмно-синий",
|
"Navy": "Тёмно-синий",
|
||||||
"Blue": "Синий",
|
"Blue": "Синий",
|
||||||
"Aqua": "Акваа"
|
"Aqua": "Голубой"
|
||||||
},
|
},
|
||||||
"thickness": {
|
"thickness": {
|
||||||
"None": "Отсутствует",
|
"None": "Отсутствует",
|
||||||
@@ -251,29 +251,29 @@
|
|||||||
"Normal": "Обычный",
|
"Normal": "Обычный",
|
||||||
"Thick": "Толстый"
|
"Thick": "Толстый"
|
||||||
},
|
},
|
||||||
"subtitle_color": "Subtitle Color",
|
"subtitle_color": "Цвет субтитров",
|
||||||
"subtitle_background_color": "Background Color",
|
"subtitle_background_color": "Цвет фона",
|
||||||
"subtitle_font": "Subtitle Font",
|
"subtitle_font": "Шрифт субтитров",
|
||||||
"ksplayer_title": "KSPlayer Settings",
|
"ksplayer_title": "Настройки KSPlayer",
|
||||||
"hardware_decode": "Hardware Decoding",
|
"hardware_decode": "Аппаратное декодирование",
|
||||||
"hardware_decode_description": "Use hardware acceleration for video decoding. Disable if you experience playback issues."
|
"hardware_decode_description": "Использовать аппаратное ускорение для декодирования видео. Выключите, если наблюдаете проблемы с воспроизведением."
|
||||||
},
|
},
|
||||||
"vlc_subtitles": {
|
"vlc_subtitles": {
|
||||||
"title": "VLC Subtitle Settings",
|
"title": "Настройки субтитров в VLC",
|
||||||
"hint": "Customize subtitle appearance for VLC player. Changes take effect on next playback.",
|
"hint": "Настройте внешний вид субтитров в VLC плеере. Изменения применятся при следующем воспроизведении.",
|
||||||
"text_color": "Text Color",
|
"text_color": "Цвет текста",
|
||||||
"background_color": "Background Color",
|
"background_color": "Цвет фона",
|
||||||
"background_opacity": "Background Opacity",
|
"background_opacity": "Прозрачность фона",
|
||||||
"outline_color": "Outline Color",
|
"outline_color": "Цвет контура",
|
||||||
"outline_opacity": "Outline Opacity",
|
"outline_opacity": "Прозрачность контура",
|
||||||
"outline_thickness": "Outline Thickness",
|
"outline_thickness": "Толщина контура",
|
||||||
"bold": "Bold Text",
|
"bold": "Жирный",
|
||||||
"margin": "Bottom Margin"
|
"margin": "Отступ снизу"
|
||||||
},
|
},
|
||||||
"video_player": {
|
"video_player": {
|
||||||
"title": "Video Player",
|
"title": "Видеоплеер",
|
||||||
"video_player": "Video Player",
|
"video_player": "Видеоплеер",
|
||||||
"video_player_description": "Choose which video player to use on iOS.",
|
"video_player_description": "Выберите видеоплеер в iOS.",
|
||||||
"ksplayer": "KSPlayer",
|
"ksplayer": "KSPlayer",
|
||||||
"vlc": "VLC"
|
"vlc": "VLC"
|
||||||
},
|
},
|
||||||
@@ -294,19 +294,19 @@
|
|||||||
"UNKNOWN": "Неизвестное"
|
"UNKNOWN": "Неизвестное"
|
||||||
},
|
},
|
||||||
"safe_area_in_controls": "Безопасная зона в элементах управления",
|
"safe_area_in_controls": "Безопасная зона в элементах управления",
|
||||||
"video_player": "Видео прейер",
|
"video_player": "Видеоплеер",
|
||||||
"video_players": {
|
"video_players": {
|
||||||
"VLC_3": "VLC 3",
|
"VLC_3": "VLC 3",
|
||||||
"VLC_4": "VLC 4 (Экспериментальный + PiP)"
|
"VLC_4": "VLC 4 (Экспериментальный + PiP)"
|
||||||
},
|
},
|
||||||
"show_custom_menu_links": "Показать ссылки кастомного меню",
|
"show_custom_menu_links": "Показать ссылки кастомного меню",
|
||||||
"show_large_home_carousel": "Show Large Home Carousel (beta)",
|
"show_large_home_carousel": "Показывать большую карусель (beta)",
|
||||||
"hide_libraries": "Скрыть библиотеки",
|
"hide_libraries": "Скрыть библиотеки",
|
||||||
"select_liraries_you_want_to_hide": "Выберите Библиотеки, которое хотите спрятать из вкладки Библиотеки и домашней страницы.",
|
"select_liraries_you_want_to_hide": "Выберите Библиотеки, которое хотите спрятать из вкладки Библиотеки и домашней страницы.",
|
||||||
"disable_haptic_feedback": "Отключить тактильную обратную связь",
|
"disable_haptic_feedback": "Отключить тактильную обратную связь",
|
||||||
"default_quality": "Качество по умолчанию",
|
"default_quality": "Качество по умолчанию",
|
||||||
"default_playback_speed": "Default Playback Speed",
|
"default_playback_speed": "Скорость воспроизведения по умолчанию",
|
||||||
"auto_play_next_episode": "Auto-play Next Episode",
|
"auto_play_next_episode": "Автоматически воспроизводить следующий эпизод",
|
||||||
"max_auto_play_episode_count": "Максимальное количество автовоспроизведения эпизодов",
|
"max_auto_play_episode_count": "Максимальное количество автовоспроизведения эпизодов",
|
||||||
"disabled": "Отключено"
|
"disabled": "Отключено"
|
||||||
},
|
},
|
||||||
@@ -314,15 +314,15 @@
|
|||||||
"downloads_title": "Загрузки"
|
"downloads_title": "Загрузки"
|
||||||
},
|
},
|
||||||
"music": {
|
"music": {
|
||||||
"title": "Music",
|
"title": "Музыка",
|
||||||
"playback_title": "Playback",
|
"playback_title": "Воспроизведение",
|
||||||
"playback_description": "Configure how music is played.",
|
"playback_description": "Настройте воспроизведение музыки.",
|
||||||
"prefer_downloaded": "Prefer Downloaded Songs",
|
"prefer_downloaded": "Предпочитать скачанные песни",
|
||||||
"caching_title": "Caching",
|
"caching_title": "Кеширование",
|
||||||
"caching_description": "Automatically cache upcoming tracks for smoother playback.",
|
"caching_description": "Автоматически предкешировать следующие треки для стабильного воспроизведения.",
|
||||||
"lookahead_enabled": "Enable Look-Ahead Caching",
|
"lookahead_enabled": "Включить предкеширование",
|
||||||
"lookahead_count": "Tracks to Pre-cache",
|
"lookahead_count": "Сколько предкешировать",
|
||||||
"max_cache_size": "Max Cache Size"
|
"max_cache_size": "Максимальное число предкешированных треков"
|
||||||
},
|
},
|
||||||
"plugins": {
|
"plugins": {
|
||||||
"plugins_title": "Плагины",
|
"plugins_title": "Плагины",
|
||||||
@@ -357,39 +357,39 @@
|
|||||||
"save_button": "Сохранить",
|
"save_button": "Сохранить",
|
||||||
"toasts": {
|
"toasts": {
|
||||||
"saved": "Сохранено",
|
"saved": "Сохранено",
|
||||||
"refreshed": "Settings refreshed from server"
|
"refreshed": "Настройки обновлены с сервера"
|
||||||
},
|
},
|
||||||
"refresh_from_server": "Refresh Settings from Server"
|
"refresh_from_server": "Обновить настройки с сервера"
|
||||||
},
|
},
|
||||||
"streamystats": {
|
"streamystats": {
|
||||||
"enable_streamystats": "Enable Streamystats",
|
"enable_streamystats": "Включить Streamystats",
|
||||||
"disable_streamystats": "Disable Streamystats",
|
"disable_streamystats": "Выключить Streamystats",
|
||||||
"enable_search": "Use for Search",
|
"enable_search": "Использовать в поиске",
|
||||||
"url": "URL",
|
"url": "URL",
|
||||||
"server_url_placeholder": "http(s)://streamystats.example.com",
|
"server_url_placeholder": "http(s)://streamystats.example.com",
|
||||||
"streamystats_search_hint": "Enter the URL for your Streamystats server. The URL should include http or https and optionally the port.",
|
"streamystats_search_hint": "Введите URL вашего сервера Streamystats. URL должен включать http/https и порт при необходимости.",
|
||||||
"read_more_about_streamystats": "Read More About Streamystats.",
|
"read_more_about_streamystats": "Узнать больше про Streamystats.",
|
||||||
"save_button": "Save",
|
"save_button": "Сохранить",
|
||||||
"save": "Save",
|
"save": "Сохранить",
|
||||||
"features_title": "Features",
|
"features_title": "Функции",
|
||||||
"home_sections_title": "Home Sections",
|
"home_sections_title": "Показывать на главной",
|
||||||
"enable_movie_recommendations": "Movie Recommendations",
|
"enable_movie_recommendations": "Рекомендации фильмов",
|
||||||
"enable_series_recommendations": "Series Recommendations",
|
"enable_series_recommendations": "Рекомендации сериалов",
|
||||||
"enable_promoted_watchlists": "Promoted Watchlists",
|
"enable_promoted_watchlists": "Продвигаемые списки просмотра",
|
||||||
"hide_watchlists_tab": "Hide Watchlists Tab",
|
"hide_watchlists_tab": "Скрыть вкладку со списками",
|
||||||
"home_sections_hint": "Show personalized recommendations and promoted watchlists from Streamystats on the home page.",
|
"home_sections_hint": "Показывать персонализированные рекомендации и подходящие списки просмотров из Streamystats на главной странице.",
|
||||||
"recommended_movies": "Recommended Movies",
|
"recommended_movies": "Рекомендованные фильмы",
|
||||||
"recommended_series": "Recommended Series",
|
"recommended_series": "Рекомендованные сериалы",
|
||||||
"toasts": {
|
"toasts": {
|
||||||
"saved": "Saved",
|
"saved": "Сохранено",
|
||||||
"refreshed": "Settings refreshed from server",
|
"refreshed": "Настройки обновлены с сервера",
|
||||||
"disabled": "Streamystats disabled"
|
"disabled": "Streamystats отключен"
|
||||||
},
|
},
|
||||||
"refresh_from_server": "Refresh Settings from Server"
|
"refresh_from_server": "Обновить настройки с сервера"
|
||||||
},
|
},
|
||||||
"kefinTweaks": {
|
"kefinTweaks": {
|
||||||
"watchlist_enabler": "Enable our Watchlist integration",
|
"watchlist_enabler": "Включить интеграцию со списками просмотра",
|
||||||
"watchlist_button": "Toggle Watchlist integration"
|
"watchlist_button": "Изменить интеграцию со списками просмотра"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"storage": {
|
"storage": {
|
||||||
@@ -398,18 +398,18 @@
|
|||||||
"device_usage": "Устройство {{availableSpace}}%",
|
"device_usage": "Устройство {{availableSpace}}%",
|
||||||
"size_used": "{{used}} из {{total}} использовано",
|
"size_used": "{{used}} из {{total}} использовано",
|
||||||
"delete_all_downloaded_files": "Удалить все загруженные файлы",
|
"delete_all_downloaded_files": "Удалить все загруженные файлы",
|
||||||
"music_cache_title": "Music Cache",
|
"music_cache_title": "Кеш музыки",
|
||||||
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
"music_cache_description": "Автоматически прекешировать песни по мере прослушивания для плавного воспроизведения и поддержки отсутствия интернета",
|
||||||
"enable_music_cache": "Enable Music Cache",
|
"enable_music_cache": "Кешировать музыку",
|
||||||
"clear_music_cache": "Clear Music Cache",
|
"clear_music_cache": "Очистить кеш музыки",
|
||||||
"music_cache_size": "{{size}} cached",
|
"music_cache_size": "{{size}} кешировано",
|
||||||
"music_cache_cleared": "Music cache cleared",
|
"music_cache_cleared": "Кеш музыки очищен",
|
||||||
"delete_all_downloaded_songs": "Delete All Downloaded Songs",
|
"delete_all_downloaded_songs": "Удалить все скачанные песни",
|
||||||
"downloaded_songs_size": "{{size}} downloaded",
|
"downloaded_songs_size": "{{size}} скачано",
|
||||||
"downloaded_songs_deleted": "Downloaded songs deleted"
|
"downloaded_songs_deleted": "Скачанные песни удалены"
|
||||||
},
|
},
|
||||||
"intro": {
|
"intro": {
|
||||||
"title": "Intro",
|
"title": "Вступление",
|
||||||
"show_intro": "Показать вступление",
|
"show_intro": "Показать вступление",
|
||||||
"reset_intro": "Сбросить вступление"
|
"reset_intro": "Сбросить вступление"
|
||||||
},
|
},
|
||||||
@@ -441,24 +441,24 @@
|
|||||||
"tvseries": "Сериалы",
|
"tvseries": "Сериалы",
|
||||||
"movies": "Фильмы",
|
"movies": "Фильмы",
|
||||||
"queue": "Очередь",
|
"queue": "Очередь",
|
||||||
"other_media": "Другие медиа",
|
"other_media": "Прочие файлы",
|
||||||
"queue_hint": "Очередь и загрузки будут удалены при перезагрузке приложения",
|
"queue_hint": "Очередь очистится после перезапуска",
|
||||||
"no_items_in_queue": "Нет элементов в очереди",
|
"no_items_in_queue": "Нет элементов в очереди",
|
||||||
"no_downloaded_items": "Нет загруженых предметов",
|
"no_downloaded_items": "Нет загруженных файлов",
|
||||||
"delete_all_movies_button": "Удалить все фильмы",
|
"delete_all_movies_button": "Удалить все фильмы",
|
||||||
"delete_all_tvseries_button": "Удалить все сериалы",
|
"delete_all_tvseries_button": "Удалить все сериалы",
|
||||||
"delete_all_button": "Удалить все",
|
"delete_all_button": "Удалить все",
|
||||||
"delete_all_other_media_button": "Удалить другой материал",
|
"delete_all_other_media_button": "Удалить прочие файлы",
|
||||||
"active_download": "Активно загружается",
|
"active_download": "Загружается",
|
||||||
"no_active_downloads": "Нет активных загрузок",
|
"no_active_downloads": "Нет активных загрузок",
|
||||||
"active_downloads": "Активные загрузки",
|
"active_downloads": "Активные",
|
||||||
"new_app_version_requires_re_download": "Новая версия приложения требует повторной загрузки",
|
"new_app_version_requires_re_download": "Новая версия приложения требует повторной загрузки",
|
||||||
"new_app_version_requires_re_download_description": "Новая версия приложения требует повторной загрузки. Пожалуйста удалите всё и попробуйте заново.",
|
"new_app_version_requires_re_download_description": "Новая версия приложения требует повторной загрузки. Пожалуйста удалите всё и попробуйте заново.",
|
||||||
"back": "Назад",
|
"back": "Назад",
|
||||||
"delete": "Удалить",
|
"delete": "Удалить",
|
||||||
"something_went_wrong": "Что-то пошло не так",
|
"something_went_wrong": "Что-то пошло не так",
|
||||||
"could_not_get_stream_url_from_jellyfin": "Не удалось получить ссылку трансляции из Jellyfin",
|
"could_not_get_stream_url_from_jellyfin": "Не удалось получить ссылку трансляции из Jellyfin",
|
||||||
"eta": "ETA {{eta}}",
|
"eta": "Осталось {{eta}}",
|
||||||
"toasts": {
|
"toasts": {
|
||||||
"you_are_not_allowed_to_download_files": "Нет разрешения на скачивание файлов.",
|
"you_are_not_allowed_to_download_files": "Нет разрешения на скачивание файлов.",
|
||||||
"deleted_all_movies_successfully": "Все фильмы были успешно удалены!",
|
"deleted_all_movies_successfully": "Все фильмы были успешно удалены!",
|
||||||
@@ -467,64 +467,64 @@
|
|||||||
"failed_to_delete_all_tvseries": "Возникла ошибка при удалении всех сериалов",
|
"failed_to_delete_all_tvseries": "Возникла ошибка при удалении всех сериалов",
|
||||||
"deleted_media_successfully": "Другие носители успешно удалены!",
|
"deleted_media_successfully": "Другие носители успешно удалены!",
|
||||||
"failed_to_delete_media": "Не удалось удалить другой файл",
|
"failed_to_delete_media": "Не удалось удалить другой файл",
|
||||||
"download_deleted": "Загрузка удалена",
|
"download_deleted": "Удалено",
|
||||||
"download_cancelled": "Загрузка отменена",
|
"download_cancelled": "Загрузка отменена",
|
||||||
"could_not_delete_download": "Не удалось удалить загрузку",
|
"could_not_delete_download": "Не удалось удалить загрузку",
|
||||||
"download_paused": "Загрузка приостановлена",
|
"download_paused": "На паузе",
|
||||||
"could_not_pause_download": "Не удалось приостановить загрузку",
|
"could_not_pause_download": "Не удалось приостановить загрузку",
|
||||||
"download_resumed": "Загрузка возобновлена",
|
"download_resumed": "Продолжено",
|
||||||
"could_not_resume_download": "Не удалось продолжить загрузку",
|
"could_not_resume_download": "Не удалось продолжить загрузку",
|
||||||
"download_completed": "Загрузка завершена",
|
"download_completed": "Завершено",
|
||||||
"download_failed": "Download Failed",
|
"download_failed": "Не удалось загрузить",
|
||||||
"download_failed_for_item": "Загрузка {{item}} провалилась с ошибкой: {{error}}",
|
"download_failed_for_item": "Загрузка {{item}} провалилась с ошибкой: {{error}}",
|
||||||
"download_completed_for_item": "{{item}} успешно загружен",
|
"download_completed_for_item": "{{item}} успешно загружен",
|
||||||
"download_started_for_item": "Загрузка началась для {{item}}",
|
"download_started_for_item": "Загрузка началась для {{item}}",
|
||||||
"failed_to_start_download": "Не удалось начать загрузку",
|
"failed_to_start_download": "Не удалось начать загрузку",
|
||||||
"item_already_downloading": "{{item}} is already downloading",
|
"item_already_downloading": "{{item}} уже загружается",
|
||||||
"all_files_deleted": "All Downloads Deleted Successfully",
|
"all_files_deleted": "Все загрузки удалены",
|
||||||
"files_deleted_by_type": "{{count}} {{type}} deleted",
|
"files_deleted_by_type": "{{count}} {{type}} удалён(о)",
|
||||||
"all_files_folders_and_jobs_deleted_successfully": "Все файлы, папки, и задачи были успешно удалены",
|
"all_files_folders_and_jobs_deleted_successfully": "Все файлы, папки, и задачи были успешно удалены",
|
||||||
"failed_to_clean_cache_directory": "Не удалось очистить директорию кэша",
|
"failed_to_clean_cache_directory": "Не удалось очистить директорию кэша",
|
||||||
"could_not_get_download_url_for_item": "Не удалось получить URL загрузки для {{itemName}}",
|
"could_not_get_download_url_for_item": "Не удалось получить URL загрузки для {{itemName}}",
|
||||||
"go_to_downloads": "В загрузки",
|
"go_to_downloads": "В загрузки",
|
||||||
"file_deleted": "{{item}} deleted"
|
"file_deleted": "{{item}} удалён"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"common": {
|
"common": {
|
||||||
"select": "Выбрать",
|
"select": "Выбрать",
|
||||||
"no_trailer_available": "Прицеп недоступен",
|
"no_trailer_available": "Трейлер недоступен",
|
||||||
"video": "Видео",
|
"video": "Видео",
|
||||||
"audio": "Звук",
|
"audio": "Звук",
|
||||||
"subtitle": "Субтитры",
|
"subtitle": "Субтитры",
|
||||||
"play": "Играть",
|
"play": "Воспроизвести",
|
||||||
"none": "None",
|
"none": "Отсутствует",
|
||||||
"track": "Track",
|
"track": "Трек",
|
||||||
"cancel": "Cancel",
|
"cancel": "Отмена",
|
||||||
"delete": "Delete",
|
"delete": "Удалить",
|
||||||
"ok": "OK",
|
"ok": "ОК",
|
||||||
"remove": "Remove",
|
"remove": "Удалить",
|
||||||
"next": "Next",
|
"next": "Вперед",
|
||||||
"back": "Back",
|
"back": "Назад",
|
||||||
"continue": "Continue",
|
"continue": "Продолжить",
|
||||||
"verifying": "Verifying..."
|
"verifying": "Проверка..."
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
"search": "Поиск...",
|
"search": "Поиск...",
|
||||||
"x_items": "{{count}} предметов",
|
"x_items": "{{count}} элементов",
|
||||||
"library": "Библиотека",
|
"library": "Библиотека",
|
||||||
"discover": "Найти новое",
|
"discover": "Найти новое",
|
||||||
"no_results": "Нет результатов",
|
"no_results": "Ничего не найдено",
|
||||||
"no_results_found_for": "Не было результатов при поиске",
|
"no_results_found_for": "Ничего не найдено по запросу",
|
||||||
"movies": "Фильмы",
|
"movies": "Фильмы",
|
||||||
"series": "Сериалы",
|
"series": "Сериалы",
|
||||||
"episodes": "Серии",
|
"episodes": "Серии",
|
||||||
"collections": "Коллекции",
|
"collections": "Коллекции",
|
||||||
"actors": "Актеры",
|
"actors": "Актеры",
|
||||||
"artists": "Artists",
|
"artists": "Артисты",
|
||||||
"albums": "Albums",
|
"albums": "Альбомы",
|
||||||
"songs": "Songs",
|
"songs": "Песни",
|
||||||
"playlists": "Playlists",
|
"playlists": "Плейлисты",
|
||||||
"request_movies": "Запросить фильмы",
|
"request_movies": "Запросить фильмы",
|
||||||
"request_series": "Запросить сериалы",
|
"request_series": "Запросить сериалы",
|
||||||
"recently_added": "Недавно добавлено",
|
"recently_added": "Недавно добавлено",
|
||||||
@@ -553,7 +553,7 @@
|
|||||||
"no_results": "Нет результатов",
|
"no_results": "Нет результатов",
|
||||||
"no_libraries_found": "Библиотеки не найдены",
|
"no_libraries_found": "Библиотеки не найдены",
|
||||||
"item_types": {
|
"item_types": {
|
||||||
"movies": "фильмы",
|
"movies": "Фильмы",
|
||||||
"series": "Сериалы",
|
"series": "Сериалы",
|
||||||
"boxsets": "Коллекции",
|
"boxsets": "Коллекции",
|
||||||
"items": "элементы"
|
"items": "элементы"
|
||||||
@@ -571,9 +571,9 @@
|
|||||||
"filters": {
|
"filters": {
|
||||||
"genres": "Жанры",
|
"genres": "Жанры",
|
||||||
"years": "Года",
|
"years": "Года",
|
||||||
"sort_by": "Сортировать по",
|
"sort_by": "Сортировка",
|
||||||
"filter_by": "Filter By",
|
"filter_by": "Фильтр",
|
||||||
"sort_order": "Порядок сортировки",
|
"sort_order": "Порядок",
|
||||||
"tags": "Тэги"
|
"tags": "Тэги"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -604,14 +604,14 @@
|
|||||||
"index": "Индекс:",
|
"index": "Индекс:",
|
||||||
"continue_watching": "Продолжить просмотр",
|
"continue_watching": "Продолжить просмотр",
|
||||||
"go_back": "Назад",
|
"go_back": "Назад",
|
||||||
"downloaded_file_title": "You have this file downloaded",
|
"downloaded_file_title": "Этот файл уже скачан",
|
||||||
"downloaded_file_message": "Do you want to play the downloaded file?",
|
"downloaded_file_message": "Хотите воспроизвести скачанный файл?",
|
||||||
"downloaded_file_yes": "Yes",
|
"downloaded_file_yes": "Да",
|
||||||
"downloaded_file_no": "No",
|
"downloaded_file_no": "Нет",
|
||||||
"downloaded_file_cancel": "Cancel"
|
"downloaded_file_cancel": "Отмена"
|
||||||
},
|
},
|
||||||
"item_card": {
|
"item_card": {
|
||||||
"next_up": "Следующее",
|
"next_up": "Далее",
|
||||||
"no_items_to_display": "Нет элементов для отображения",
|
"no_items_to_display": "Нет элементов для отображения",
|
||||||
"cast_and_crew": "Актеры и съемочная группа",
|
"cast_and_crew": "Актеры и съемочная группа",
|
||||||
"series": "Серии",
|
"series": "Серии",
|
||||||
@@ -644,7 +644,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"live_tv": {
|
"live_tv": {
|
||||||
"next": "Следующая",
|
"next": "Далее",
|
||||||
"previous": "Предыдущая",
|
"previous": "Предыдущая",
|
||||||
"coming_soon": "Скоро",
|
"coming_soon": "Скоро",
|
||||||
"on_now": "Сейчас в эфире",
|
"on_now": "Сейчас в эфире",
|
||||||
@@ -675,7 +675,7 @@
|
|||||||
"series_type": "Тип сериала",
|
"series_type": "Тип сериала",
|
||||||
"release_dates": "Дата релиза",
|
"release_dates": "Дата релиза",
|
||||||
"first_air_date": "Первая дата выхода в эфир",
|
"first_air_date": "Первая дата выхода в эфир",
|
||||||
"next_air_date": "Следующая дата выхода в эфир",
|
"next_air_date": "Ближайшая дата выхода в эфир",
|
||||||
"revenue": "Прибыль",
|
"revenue": "Прибыль",
|
||||||
"budget": "Бюджет",
|
"budget": "Бюджет",
|
||||||
"original_language": "Оригинальный язык",
|
"original_language": "Оригинальный язык",
|
||||||
@@ -693,10 +693,10 @@
|
|||||||
"number_episodes": "{{episode_number}} серий",
|
"number_episodes": "{{episode_number}} серий",
|
||||||
"born": "Рожден",
|
"born": "Рожден",
|
||||||
"appearances": "Появления",
|
"appearances": "Появления",
|
||||||
"approve": "Approve",
|
"approve": "Одобрить",
|
||||||
"decline": "Decline",
|
"decline": "Отклонить",
|
||||||
"requested_by": "Requested by {{user}}",
|
"requested_by": "Запрошено {{user}}",
|
||||||
"unknown_user": "Unknown User",
|
"unknown_user": "Неизвестный пользователь",
|
||||||
"toasts": {
|
"toasts": {
|
||||||
"jellyseer_does_not_meet_requirements": "Сервер Jellyseerr не соответствует минимальным требованиям версии! Пожалуйста, обновите до версии не ниже 2.0.0",
|
"jellyseer_does_not_meet_requirements": "Сервер Jellyseerr не соответствует минимальным требованиям версии! Пожалуйста, обновите до версии не ниже 2.0.0",
|
||||||
"jellyseerr_test_failed": "Тест Jellyseerr не пройден. Попробуйте еще раз.",
|
"jellyseerr_test_failed": "Тест Jellyseerr не пройден. Попробуйте еще раз.",
|
||||||
@@ -705,141 +705,141 @@
|
|||||||
"requested_item": "Запрошено {{item}}!",
|
"requested_item": "Запрошено {{item}}!",
|
||||||
"you_dont_have_permission_to_request": "У вас нет разрешения на запрос!",
|
"you_dont_have_permission_to_request": "У вас нет разрешения на запрос!",
|
||||||
"something_went_wrong_requesting_media": "Что-то пошло не так при запросе медиафайлов!",
|
"something_went_wrong_requesting_media": "Что-то пошло не так при запросе медиафайлов!",
|
||||||
"request_approved": "Request Approved!",
|
"request_approved": "Запрос одобрен!",
|
||||||
"request_declined": "Request Declined!",
|
"request_declined": "Запрос отклонён!",
|
||||||
"failed_to_approve_request": "Failed to Approve Request",
|
"failed_to_approve_request": "Не удалось одобрить запрос",
|
||||||
"failed_to_decline_request": "Failed to Decline Request"
|
"failed_to_decline_request": "Не удалось отклонить запрос"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tabs": {
|
"tabs": {
|
||||||
"home": "Дом",
|
"home": "Главная",
|
||||||
"search": "Поиск",
|
"search": "Поиск",
|
||||||
"library": "Библиотека",
|
"library": "Библиотека",
|
||||||
"custom_links": "Кастомные ссылки",
|
"custom_links": "Ссылки",
|
||||||
"favorites": "Избранное"
|
"favorites": "Избранное"
|
||||||
},
|
},
|
||||||
"music": {
|
"music": {
|
||||||
"title": "Music",
|
"title": "Музыка",
|
||||||
"tabs": {
|
"tabs": {
|
||||||
"suggestions": "Suggestions",
|
"suggestions": "Рекомендации",
|
||||||
"albums": "Albums",
|
"albums": "Альбомы",
|
||||||
"artists": "Artists",
|
"artists": "Исполнители",
|
||||||
"playlists": "Playlists",
|
"playlists": "Плейлисты",
|
||||||
"tracks": "tracks"
|
"tracks": "треки"
|
||||||
},
|
},
|
||||||
"filters": {
|
"filters": {
|
||||||
"all": "All"
|
"all": "Все"
|
||||||
},
|
},
|
||||||
"recently_added": "Recently Added",
|
"recently_added": "Недавно добавлено",
|
||||||
"recently_played": "Recently Played",
|
"recently_played": "Недавно воспроизведено",
|
||||||
"frequently_played": "Frequently Played",
|
"frequently_played": "Часто играет",
|
||||||
"explore": "Explore",
|
"explore": "Найти новое",
|
||||||
"top_tracks": "Top Tracks",
|
"top_tracks": "Топ",
|
||||||
"play": "Play",
|
"play": "Воспроизвести",
|
||||||
"shuffle": "Shuffle",
|
"shuffle": "Перемешать",
|
||||||
"play_top_tracks": "Play Top Tracks",
|
"play_top_tracks": "Воспроизвести топ",
|
||||||
"no_suggestions": "No suggestions available",
|
"no_suggestions": "Нет рекомендаций",
|
||||||
"no_albums": "No albums found",
|
"no_albums": "Альбомы не найдены",
|
||||||
"no_artists": "No artists found",
|
"no_artists": "Исполнители не найдены",
|
||||||
"no_playlists": "No playlists found",
|
"no_playlists": "Плейлисты не найдены",
|
||||||
"album_not_found": "Album not found",
|
"album_not_found": "Альбом не найден",
|
||||||
"artist_not_found": "Artist not found",
|
"artist_not_found": "Исполнитель не найден",
|
||||||
"playlist_not_found": "Playlist not found",
|
"playlist_not_found": "Плейлист не найден",
|
||||||
"track_options": {
|
"track_options": {
|
||||||
"play_next": "Play Next",
|
"play_next": "Далее",
|
||||||
"add_to_queue": "Add to Queue",
|
"add_to_queue": "Добавить в очередь",
|
||||||
"add_to_playlist": "Add to Playlist",
|
"add_to_playlist": "Добавить в плейлист",
|
||||||
"download": "Download",
|
"download": "Скачать",
|
||||||
"downloaded": "Downloaded",
|
"downloaded": "Скачано",
|
||||||
"downloading": "Downloading...",
|
"downloading": "Скачивается...",
|
||||||
"cached": "Cached",
|
"cached": "Кешировано",
|
||||||
"delete_download": "Delete Download",
|
"delete_download": "Удалить загрузку",
|
||||||
"delete_cache": "Remove from Cache",
|
"delete_cache": "Удалить из кеша",
|
||||||
"go_to_artist": "Go to Artist",
|
"go_to_artist": "К исполнителю",
|
||||||
"go_to_album": "Go to Album",
|
"go_to_album": "К альбому",
|
||||||
"add_to_favorites": "Add to Favorites",
|
"add_to_favorites": "В избранное",
|
||||||
"remove_from_favorites": "Remove from Favorites",
|
"remove_from_favorites": "Удалить из избранного",
|
||||||
"remove_from_playlist": "Remove from Playlist"
|
"remove_from_playlist": "Удалить из плейлиста"
|
||||||
},
|
},
|
||||||
"playlists": {
|
"playlists": {
|
||||||
"create_playlist": "Create Playlist",
|
"create_playlist": "Создать плейлист",
|
||||||
"playlist_name": "Playlist Name",
|
"playlist_name": "Название плейлиста",
|
||||||
"enter_name": "Enter playlist name",
|
"enter_name": "Введите название плейлиста",
|
||||||
"create": "Create",
|
"create": "Создать",
|
||||||
"search_playlists": "Search playlists...",
|
"search_playlists": "Поиск плейлистов...",
|
||||||
"added_to": "Added to {{name}}",
|
"added_to": "Добавлено в {{name}}",
|
||||||
"added": "Added to playlist",
|
"added": "Добавлено в плейлист",
|
||||||
"removed_from": "Removed from {{name}}",
|
"removed_from": "Удалено из {{name}}",
|
||||||
"removed": "Removed from playlist",
|
"removed": "Удалено из плейлиста",
|
||||||
"created": "Playlist created",
|
"created": "Плейлист создан",
|
||||||
"create_new": "Create New Playlist",
|
"create_new": "Добавить новый плейлист",
|
||||||
"failed_to_add": "Failed to add to playlist",
|
"failed_to_add": "Не удалось добавить в плейлист",
|
||||||
"failed_to_remove": "Failed to remove from playlist",
|
"failed_to_remove": "Не удалось удалить из плейлиста",
|
||||||
"failed_to_create": "Failed to create playlist",
|
"failed_to_create": "Не удалось создать плейлист",
|
||||||
"delete_playlist": "Delete Playlist",
|
"delete_playlist": "Удалить плейлист",
|
||||||
"delete_confirm": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.",
|
"delete_confirm": "Вы уверены, что хотите удалить \"{{name}}\"? Это действие необратимо.",
|
||||||
"deleted": "Playlist deleted",
|
"deleted": "Плейлист удалён",
|
||||||
"failed_to_delete": "Failed to delete playlist"
|
"failed_to_delete": "Не удалось удалить плейлист"
|
||||||
},
|
},
|
||||||
"sort": {
|
"sort": {
|
||||||
"title": "Sort By",
|
"title": "Сортировка",
|
||||||
"alphabetical": "Alphabetical",
|
"alphabetical": "По алфавиту",
|
||||||
"date_created": "Date Created"
|
"date_created": "По дате создания"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"watchlists": {
|
"watchlists": {
|
||||||
"title": "Watchlists",
|
"title": "Списки просмотров",
|
||||||
"my_watchlists": "My Watchlists",
|
"my_watchlists": "Мои списки",
|
||||||
"public_watchlists": "Public Watchlists",
|
"public_watchlists": "Публичные списки",
|
||||||
"create_title": "Create Watchlist",
|
"create_title": "Создать список",
|
||||||
"edit_title": "Edit Watchlist",
|
"edit_title": "Редактировать список",
|
||||||
"create_button": "Create Watchlist",
|
"create_button": "Создать список",
|
||||||
"save_button": "Save Changes",
|
"save_button": "Сохранить изменения",
|
||||||
"delete_button": "Delete",
|
"delete_button": "Удалить",
|
||||||
"remove_button": "Remove",
|
"remove_button": "Удалить",
|
||||||
"cancel_button": "Cancel",
|
"cancel_button": "Отмена",
|
||||||
"name_label": "Name",
|
"name_label": "Название",
|
||||||
"name_placeholder": "Enter watchlist name",
|
"name_placeholder": "Введите название списка",
|
||||||
"description_label": "Description",
|
"description_label": "Описание",
|
||||||
"description_placeholder": "Enter description (optional)",
|
"description_placeholder": "Введите описание (не обязательно)",
|
||||||
"is_public_label": "Public Watchlist",
|
"is_public_label": "Публичный",
|
||||||
"is_public_description": "Allow others to view this watchlist",
|
"is_public_description": "Разрешить остальным пользователям видеть этот список",
|
||||||
"allowed_type_label": "Content Type",
|
"allowed_type_label": "Тип контента",
|
||||||
"sort_order_label": "Default Sort Order",
|
"sort_order_label": "Сортировка по умолчанию",
|
||||||
"empty_title": "No Watchlists",
|
"empty_title": "Нет списков",
|
||||||
"empty_description": "Create your first watchlist to start organizing your media",
|
"empty_description": "Создайте ваш первый список для управления вашими медиа",
|
||||||
"empty_watchlist": "This watchlist is empty",
|
"empty_watchlist": "Этот список пуст",
|
||||||
"empty_watchlist_hint": "Add items from your library to this watchlist",
|
"empty_watchlist_hint": "Добавляйте элементы из библиотеки в этот список",
|
||||||
"not_configured_title": "Streamystats Not Configured",
|
"not_configured_title": "Streamystats не настроен",
|
||||||
"not_configured_description": "Configure Streamystats in settings to use watchlists",
|
"not_configured_description": "Настройте Streamystats для использования функционала списков",
|
||||||
"go_to_settings": "Go to Settings",
|
"go_to_settings": "В настройки",
|
||||||
"add_to_watchlist": "Add to Watchlist",
|
"add_to_watchlist": "Добавить в список просмотра",
|
||||||
"remove_from_watchlist": "Remove from Watchlist",
|
"remove_from_watchlist": "Удалить из списка просмотра",
|
||||||
"select_watchlist": "Select Watchlist",
|
"select_watchlist": "Выбрать список",
|
||||||
"create_new": "Create New Watchlist",
|
"create_new": "Создать новый список",
|
||||||
"item": "item",
|
"item": "элемент",
|
||||||
"items": "items",
|
"items": "элементы",
|
||||||
"public": "Public",
|
"public": "Публичный",
|
||||||
"private": "Private",
|
"private": "Личный",
|
||||||
"you": "You",
|
"you": "Ваш",
|
||||||
"by_owner": "By another user",
|
"by_owner": "Другим пользователем",
|
||||||
"not_found": "Watchlist not found",
|
"not_found": "Список не найден",
|
||||||
"delete_confirm_title": "Delete Watchlist",
|
"delete_confirm_title": "Удалить список",
|
||||||
"delete_confirm_message": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.",
|
"delete_confirm_message": "Вы уверены, что хотите удалить список \"{{name}}\"? Это действие необратимо.",
|
||||||
"remove_item_title": "Remove from Watchlist",
|
"remove_item_title": "Удалить из списка",
|
||||||
"remove_item_message": "Remove \"{{name}}\" from this watchlist?",
|
"remove_item_message": "Удалить \"{{name}}\" из списка?",
|
||||||
"loading": "Loading watchlists...",
|
"loading": "Загрузка списков...",
|
||||||
"no_compatible_watchlists": "No compatible watchlists",
|
"no_compatible_watchlists": "Нет совместимых списков",
|
||||||
"create_one_first": "Create a watchlist that accepts this content type"
|
"create_one_first": "Создайте список просмотра с подходящим типом контента"
|
||||||
},
|
},
|
||||||
"playback_speed": {
|
"playback_speed": {
|
||||||
"title": "Playback Speed",
|
"title": "Скорость воспроизведения",
|
||||||
"apply_to": "Apply To",
|
"apply_to": "Применять к",
|
||||||
"speed": "Speed",
|
"speed": "Скорость",
|
||||||
"scope": {
|
"scope": {
|
||||||
"media": "This media only",
|
"media": "Только в этот раз",
|
||||||
"show": "This show",
|
"show": "Ко всему сериалу",
|
||||||
"all": "All media (default)"
|
"all": "Ко всем файлам (по умолчанию)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,88 +7,88 @@
|
|||||||
"username_placeholder": "Kullanıcı adı",
|
"username_placeholder": "Kullanıcı adı",
|
||||||
"password_placeholder": "Şifre",
|
"password_placeholder": "Şifre",
|
||||||
"login_button": "Giriş yap",
|
"login_button": "Giriş yap",
|
||||||
"quick_connect": "Hızlı Bağlantı",
|
"quick_connect": "Hızlı Bağlan",
|
||||||
"enter_code_to_login": "Giriş yapmak için {{code}} kodunu girin",
|
"enter_code_to_login": "Giriş yapmak için {{code}} kodunu girin",
|
||||||
"failed_to_initiate_quick_connect": "Quick Connect başlatılamadı",
|
"failed_to_initiate_quick_connect": "Hızlı Bağlan başlatılamadı",
|
||||||
"got_it": "Anlaşıldı",
|
"got_it": "Anlaşıldı",
|
||||||
"connection_failed": "Bağlantı başarısız",
|
"connection_failed": "Bağlantı başarısız",
|
||||||
"could_not_connect_to_server": "Sunucuya bağlanılamadı. Lütfen URL'yi ve ağ bağlantınızı kontrol edin",
|
"could_not_connect_to_server": "Sunucuya bağlanılamadı. Lütfen URL'yi ve ağ bağlantınızı kontrol edin.",
|
||||||
"an_unexpected_error_occured": "Beklenmedik bir hata oluştu",
|
"an_unexpected_error_occured": "Beklenmedik bir hata oluştu",
|
||||||
"change_server": "Sunucuyu değiştir",
|
"change_server": "Sunucu değiştir",
|
||||||
"invalid_username_or_password": "Geçersiz kullanıcı adı veya şifre",
|
"invalid_username_or_password": "Geçersiz kullanıcı adı veya şifre",
|
||||||
"user_does_not_have_permission_to_log_in": "Kullanıcının giriş yapma izni yok",
|
"user_does_not_have_permission_to_log_in": "Kullanıcının giriş yapma izni yok",
|
||||||
"server_is_taking_too_long_to_respond_try_again_later": "Sunucu yanıt vermekte çok uzun sürüyor, lütfen tekrar deneyin",
|
"server_is_taking_too_long_to_respond_try_again_later": "Sunucunun yanıt vermesi çok uzun sürüyor, lütfen daha sonra tekrar deneyin",
|
||||||
"server_received_too_many_requests_try_again_later": "Sunucu çok fazla istek aldı, lütfen tekrar deneyin.",
|
"server_received_too_many_requests_try_again_later": "Sunucu çok fazla istek aldı, lütfen daha sonra tekrar deneyin.",
|
||||||
"there_is_a_server_error": "Sunucu hatası var",
|
"there_is_a_server_error": "Sunucu hatası var",
|
||||||
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Beklenmedik bir hata oluştu. Sunucu URL'sini doğru girdiğinizden emin oldunuz mu?",
|
"an_unexpected_error_occured_did_you_enter_the_correct_url": "Beklenmedik bir hata oluştu. Sunucu URL'sini doğru girdiğinizden emin misiniz?",
|
||||||
"too_old_server_text": "Unsupported Jellyfin Server Discovered",
|
"too_old_server_text": "Desteklenmeyen Jellyfin Sunucu sürümü bulundu.",
|
||||||
"too_old_server_description": "Please update Jellyfin to the latest version"
|
"too_old_server_description": "Lütfen Jellyfin'i en son sürüme güncelleyin."
|
||||||
},
|
},
|
||||||
"server": {
|
"server": {
|
||||||
"enter_url_to_jellyfin_server": "Jellyfin sunucunusun URL'sini girin",
|
"enter_url_to_jellyfin_server": "Jellyfin sunucunusun URL adresini girin",
|
||||||
"server_url_placeholder": "http(s)://sunucunuz.com",
|
"server_url_placeholder": "http(s)://sunucunuz.com",
|
||||||
"connect_button": "Bağlan",
|
"connect_button": "Bağlan",
|
||||||
"previous_servers": "Önceki sunucular",
|
"previous_servers": "Önceki sunucular",
|
||||||
"clear_button": "Temizle",
|
"clear_button": "Temizle",
|
||||||
"swipe_to_remove": "Swipe to remove",
|
"swipe_to_remove": "Kaldırmak için kaydırın",
|
||||||
"search_for_local_servers": "Yerel sunucuları ara",
|
"search_for_local_servers": "Yerel sunucuları ara",
|
||||||
"searching": "Aranıyor...",
|
"searching": "Aranıyor...",
|
||||||
"servers": "Sunucular",
|
"servers": "Sunucular",
|
||||||
"saved": "Saved",
|
"saved": "Kaydedildi",
|
||||||
"session_expired": "Session Expired",
|
"session_expired": "Oturum süresi doldu",
|
||||||
"please_login_again": "Your saved session has expired. Please log in again.",
|
"please_login_again": "Kaydedilmiş oturumunuzun süresi doldu. Lütfen tekrar giriş yapın.",
|
||||||
"remove_saved_login": "Remove Saved Login",
|
"remove_saved_login": "Kayıtlı oturumu kaldır",
|
||||||
"remove_saved_login_description": "This will remove your saved credentials for this server. You'll need to enter your username and password again next time.",
|
"remove_saved_login_description": "Bu sunucu için kaydedilmiş kimlik bilgileriniz kaldırılacaktır. Bir sonraki sefere kullanıcı adı ve şifrenizi yeniden girmeniz gerekecek.",
|
||||||
"accounts_count": "{{count}} accounts",
|
"accounts_count": "{{count}} hesap",
|
||||||
"select_account": "Select Account",
|
"select_account": "Hesap Seç",
|
||||||
"add_account": "Add Account",
|
"add_account": "Hesap Ekle",
|
||||||
"remove_account_description": "This will remove the saved credentials for {{username}}."
|
"remove_account_description": "{{username}} için kayıtlı bilgiler kaldırılacaktır."
|
||||||
},
|
},
|
||||||
"save_account": {
|
"save_account": {
|
||||||
"title": "Save Account",
|
"title": "Hesabı Kaydet",
|
||||||
"save_for_later": "Save this account",
|
"save_for_later": "Bu hesabı kaydet",
|
||||||
"security_option": "Security Option",
|
"security_option": "Güvenlik Seçeneği",
|
||||||
"no_protection": "No protection",
|
"no_protection": "No protection",
|
||||||
"no_protection_desc": "Quick login without authentication",
|
"no_protection_desc": "Kimlik doğrulamasız hızlı giriş",
|
||||||
"pin_code": "PIN code",
|
"pin_code": "PIN kodu",
|
||||||
"pin_code_desc": "4-digit PIN required when switching",
|
"pin_code_desc": "Geçiş yaparken 4 haneli PIN kodu gereklidir",
|
||||||
"password": "Re-enter password",
|
"password": "Şifrenizi tekrar girin ",
|
||||||
"password_desc": "Password required when switching",
|
"password_desc": "Geçiş yaparken şifre gereklidir",
|
||||||
"save_button": "Save",
|
"save_button": "Kaydet",
|
||||||
"cancel_button": "Cancel"
|
"cancel_button": "Vazgeç"
|
||||||
},
|
},
|
||||||
"pin": {
|
"pin": {
|
||||||
"enter_pin": "Enter PIN",
|
"enter_pin": "PIN kodunu girin",
|
||||||
"enter_pin_for": "Enter PIN for {{username}}",
|
"enter_pin_for": "{{username}} için PIN kodunu girin",
|
||||||
"enter_4_digits": "Enter 4 digits",
|
"enter_4_digits": "4 hane girin",
|
||||||
"invalid_pin": "Invalid PIN",
|
"invalid_pin": "Geçersiz PIN kodu",
|
||||||
"setup_pin": "Set Up PIN",
|
"setup_pin": "PIN kodunu ayarla",
|
||||||
"confirm_pin": "Confirm PIN",
|
"confirm_pin": "PIN kodunu onayla",
|
||||||
"pins_dont_match": "PINs don't match",
|
"pins_dont_match": "PIN kodları eşleşmiyor",
|
||||||
"forgot_pin": "Forgot PIN?",
|
"forgot_pin": "PIN kodunu mu unuttunuz?",
|
||||||
"forgot_pin_desc": "Your saved credentials will be removed"
|
"forgot_pin_desc": "Kayıtlı bilgileriniz kaldırılacaktır"
|
||||||
},
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"enter_password": "Enter Password",
|
"enter_password": "Şifrenizi girin",
|
||||||
"enter_password_for": "Enter password for {{username}}",
|
"enter_password_for": "{{username}} için şifrenizi girin",
|
||||||
"invalid_password": "Invalid password"
|
"invalid_password": "Geçersiz şifre"
|
||||||
},
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"checking_server_connection": "Checking server connection...",
|
"checking_server_connection": "Sunucu bağlantısı kontrol ediliyor...",
|
||||||
"no_internet": "İnternet Yok",
|
"no_internet": "İnternet Yok",
|
||||||
"no_items": "Öge Yok",
|
"no_items": "Öge Yok",
|
||||||
"no_internet_message": "Endişelenmeyin, hala\ndownloaded içerik izleyebilirsiniz.",
|
"no_internet_message": "Endişelenmeyin, indirilmiş içerikleri izleyebilirsiniz.",
|
||||||
"checking_server_connection_message": "Checking connection to server",
|
"checking_server_connection_message": "Sunucuya bağlantı kontrol ediliyor",
|
||||||
"go_to_downloads": "İndirmelere Git",
|
"go_to_downloads": "İndirilenlere git",
|
||||||
"retry": "Retry",
|
"retry": "Tekrar dene",
|
||||||
"server_unreachable": "Server Unreachable",
|
"server_unreachable": "Sunucuya ulaşılamıyor",
|
||||||
"server_unreachable_message": "Could not reach the server.\nPlease check your network connection.",
|
"server_unreachable_message": "Sunucuya bağlanılamadı. Lütfen ağ bağlantınızı kontrol edin.",
|
||||||
"oops": "Hups!",
|
"oops": "Hups!",
|
||||||
"error_message": "Bir şeyler ters gitti.\nLütfen çıkış yapın ve tekrar giriş yapın.",
|
"error_message": "Bir şeyler ters gitti.\nLütfen çıkış yapıp tekrar giriş yapın.",
|
||||||
"continue_watching": "İzlemeye Devam Et",
|
"continue_watching": "İzlemeye Devam Et",
|
||||||
"next_up": "Sonraki",
|
"next_up": "Sonraki",
|
||||||
"continue_and_next_up": "Continue & Next Up",
|
"continue_and_next_up": "İzlemeye Devam Et & Sıradakiler",
|
||||||
"recently_added_in": "{{libraryName}}'de Yakınlarda Eklendi",
|
"recently_added_in": "{{libraryName}} Kütüphanesine Son Eklenenler",
|
||||||
"suggested_movies": "Önerilen Filmler",
|
"suggested_movies": "Önerilen Filmler",
|
||||||
"suggested_episodes": "Önerilen Bölümler",
|
"suggested_episodes": "Önerilen Bölümler",
|
||||||
"intro": {
|
"intro": {
|
||||||
@@ -110,52 +110,52 @@
|
|||||||
"settings_title": "Ayarlar",
|
"settings_title": "Ayarlar",
|
||||||
"log_out_button": "Çıkış Yap",
|
"log_out_button": "Çıkış Yap",
|
||||||
"categories": {
|
"categories": {
|
||||||
"title": "Categories"
|
"title": "Kategoriler"
|
||||||
},
|
},
|
||||||
"playback_controls": {
|
"playback_controls": {
|
||||||
"title": "Playback & Controls"
|
"title": "Oynatma & Kontroller"
|
||||||
},
|
},
|
||||||
"audio_subtitles": {
|
"audio_subtitles": {
|
||||||
"title": "Audio & Subtitles"
|
"title": "Ses & Altyazılar"
|
||||||
},
|
},
|
||||||
"appearance": {
|
"appearance": {
|
||||||
"title": "Appearance",
|
"title": "Görünüm",
|
||||||
"merge_next_up_continue_watching": "Merge Continue Watching & Next Up",
|
"merge_next_up_continue_watching": "İzlemeye Devam Et & Sıradakiler'i birleştir",
|
||||||
"hide_remote_session_button": "Hide Remote Session Button"
|
"hide_remote_session_button": "Uzak Oturum Butonunu Gizle"
|
||||||
},
|
},
|
||||||
"network": {
|
"network": {
|
||||||
"title": "Network",
|
"title": "Ağ",
|
||||||
"local_network": "Local Network",
|
"local_network": "Yerel Ağ",
|
||||||
"auto_switch_enabled": "Auto-switch when at home",
|
"auto_switch_enabled": "Evdeyken otomatik geçiş yap",
|
||||||
"auto_switch_description": "Automatically switch to local URL when connected to home WiFi",
|
"auto_switch_description": "Ev WiFi'sine bağlanınca otomatik olarak yerek URL adresine geçiş yap",
|
||||||
"local_url": "Local URL",
|
"local_url": "Yerel URL Adresi",
|
||||||
"local_url_hint": "Enter your local server address (e.g., http://192.168.1.100:8096)",
|
"local_url_hint": "Yerel sunucu adresinizi girin (http://192.168.1.100:8096, gibi)",
|
||||||
"local_url_placeholder": "http://192.168.1.100:8096",
|
"local_url_placeholder": "http://192.168.1.100:8096",
|
||||||
"home_wifi_networks": "Home WiFi Networks",
|
"home_wifi_networks": "Ev WiFi ağları",
|
||||||
"add_current_network": "Add \"{{ssid}}\"",
|
"add_current_network": "\"{{ssid}}\"'yi ekle",
|
||||||
"not_connected_to_wifi": "Not connected to WiFi",
|
"not_connected_to_wifi": "WiFi'a bağlı değil",
|
||||||
"no_networks_configured": "No networks configured",
|
"no_networks_configured": "Herhangi bir ağ ayarlanmadı",
|
||||||
"add_network_hint": "Add your home WiFi network to enable auto-switching",
|
"add_network_hint": "Otomatik geçişi etkinleştirmek için ev WiFi'nizi ekleyin",
|
||||||
"current_wifi": "Current WiFi",
|
"current_wifi": "Şu anki WiFi",
|
||||||
"using_url": "Using",
|
"using_url": "Kullanılıyor",
|
||||||
"local": "Local URL",
|
"local": "Yerel URL Adresi",
|
||||||
"remote": "Remote URL",
|
"remote": "Uzak URL Adresi",
|
||||||
"not_connected": "Not connected",
|
"not_connected": "Bağlı değil",
|
||||||
"current_server": "Current Server",
|
"current_server": "Geçerli Sunucu",
|
||||||
"remote_url": "Remote URL",
|
"remote_url": "Uzak URL Adresi",
|
||||||
"active_url": "Active URL",
|
"active_url": "Aktif URL Adresi",
|
||||||
"not_configured": "Not configured",
|
"not_configured": "Yapılandırılmamış",
|
||||||
"network_added": "Network added",
|
"network_added": "Ağ eklendi",
|
||||||
"network_already_added": "Network already added",
|
"network_already_added": "Ağ zaten eklendi",
|
||||||
"no_wifi_connected": "Not connected to WiFi",
|
"no_wifi_connected": "WiFi'a bağlı değil",
|
||||||
"permission_denied": "Location permission denied",
|
"permission_denied": "Konum izni reddedildi",
|
||||||
"permission_denied_explanation": "Location permission is required to detect WiFi network for auto-switching. Please enable it in Settings."
|
"permission_denied_explanation": "Otomatik geçiş yapabilmek için WiFi ağını algılayabilmek için konum izni gereklidir. Lütfen Ayarlarda etkinleştirin."
|
||||||
},
|
},
|
||||||
"user_info": {
|
"user_info": {
|
||||||
"user_info_title": "Kullanıcı Bilgisi",
|
"user_info_title": "Kullanıcı Bilgisi",
|
||||||
"user": "Kullanıcı",
|
"user": "Kullanıcı",
|
||||||
"server": "Sunucu",
|
"server": "Sunucu",
|
||||||
"token": "Token",
|
"token": "Erişim Anahtarı",
|
||||||
"app_version": "Uygulama Sürümü"
|
"app_version": "Uygulama Sürümü"
|
||||||
},
|
},
|
||||||
"quick_connect": {
|
"quick_connect": {
|
||||||
@@ -172,20 +172,20 @@
|
|||||||
"media_controls_title": "Medya Kontrolleri",
|
"media_controls_title": "Medya Kontrolleri",
|
||||||
"forward_skip_length": "İleri Sarma Uzunluğu",
|
"forward_skip_length": "İleri Sarma Uzunluğu",
|
||||||
"rewind_length": "Geri Sarma Uzunluğu",
|
"rewind_length": "Geri Sarma Uzunluğu",
|
||||||
"seconds_unit": "s"
|
"seconds_unit": "sn"
|
||||||
},
|
},
|
||||||
"gesture_controls": {
|
"gesture_controls": {
|
||||||
"gesture_controls_title": "Gesture Controls",
|
"gesture_controls_title": "Hareketle Kontrol",
|
||||||
"horizontal_swipe_skip": "Horizontal Swipe to Skip",
|
"horizontal_swipe_skip": "Atlamak için yatay kaydırma",
|
||||||
"horizontal_swipe_skip_description": "Swipe left/right when controls are hidden to skip",
|
"horizontal_swipe_skip_description": "Kontroller gizliyken sola/sağa kaydırarak atlama",
|
||||||
"left_side_brightness": "Left Side Brightness Control",
|
"left_side_brightness": "Sol Taraf Parlaklık Kontrolü",
|
||||||
"left_side_brightness_description": "Swipe up/down on left side to adjust brightness",
|
"left_side_brightness_description": "Sol tarafta aşağı/yukarı kaydırarak parlaklık ayarı",
|
||||||
"right_side_volume": "Right Side Volume Control",
|
"right_side_volume": "Sağ Taraf Ses Kontrolü",
|
||||||
"right_side_volume_description": "Swipe up/down on right side to adjust volume",
|
"right_side_volume_description": "Sağ tarafta aşağı/yukarı kaydırarak ses ayarı",
|
||||||
"hide_volume_slider": "Hide Volume Slider",
|
"hide_volume_slider": "Ses Ayarını Gizle",
|
||||||
"hide_volume_slider_description": "Hide the volume slider in the video player",
|
"hide_volume_slider_description": "Video oynatıcıda ses ayarını gizle",
|
||||||
"hide_brightness_slider": "Hide Brightness Slider",
|
"hide_brightness_slider": "Parlaklık Ayarını Gizle",
|
||||||
"hide_brightness_slider_description": "Hide the brightness slider in the video player"
|
"hide_brightness_slider_description": "Video oynatıcıda parlaklık ayarını gizle"
|
||||||
},
|
},
|
||||||
"audio": {
|
"audio": {
|
||||||
"audio_title": "Ses",
|
"audio_title": "Ses",
|
||||||
@@ -195,12 +195,12 @@
|
|||||||
"none": "Yok",
|
"none": "Yok",
|
||||||
"language": "Dil",
|
"language": "Dil",
|
||||||
"transcode_mode": {
|
"transcode_mode": {
|
||||||
"title": "Audio Transcoding",
|
"title": "Ses Kod Dönüştürmesi",
|
||||||
"description": "Controls how surround audio (7.1, TrueHD, DTS-HD) is handled",
|
"description": "Surround sesin (7.1, TrueHD, DTS-HD) nasıl işleneceğini kontrol eder.",
|
||||||
"auto": "Auto",
|
"auto": "Oto",
|
||||||
"stereo": "Force Stereo",
|
"stereo": "Stereo'ya zorla",
|
||||||
"5_1": "Allow 5.1",
|
"5_1": "5.1'e izin ver",
|
||||||
"passthrough": "Passthrough"
|
"passthrough": "Doğrudan geçiş"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"subtitles": {
|
"subtitles": {
|
||||||
@@ -220,60 +220,60 @@
|
|||||||
"None": "Yok",
|
"None": "Yok",
|
||||||
"OnlyForced": "Sadece Zorunlu"
|
"OnlyForced": "Sadece Zorunlu"
|
||||||
},
|
},
|
||||||
"text_color": "Text Color",
|
"text_color": "Metin Rengi",
|
||||||
"background_color": "Background Color",
|
"background_color": "Arkaplan Rengi",
|
||||||
"outline_color": "Outline Color",
|
"outline_color": "Kenarlık Rengi",
|
||||||
"outline_thickness": "Outline Thickness",
|
"outline_thickness": "Kenarlık kalınlığı",
|
||||||
"background_opacity": "Background Opacity",
|
"background_opacity": "Arkaplan Opaklığı",
|
||||||
"outline_opacity": "Outline Opacity",
|
"outline_opacity": "Kenarlık Opaklığı",
|
||||||
"bold_text": "Bold Text",
|
"bold_text": "Kalın Metin",
|
||||||
"colors": {
|
"colors": {
|
||||||
"Black": "Black",
|
"Black": "Siyah",
|
||||||
"Gray": "Gray",
|
"Gray": "Gri",
|
||||||
"Silver": "Silver",
|
"Silver": "Gümüş",
|
||||||
"White": "White",
|
"White": "Beyaz",
|
||||||
"Maroon": "Maroon",
|
"Maroon": "Kestane",
|
||||||
"Red": "Red",
|
"Red": "Kırmızı",
|
||||||
"Fuchsia": "Fuchsia",
|
"Fuchsia": "Fuşya",
|
||||||
"Yellow": "Yellow",
|
"Yellow": "Sarı",
|
||||||
"Olive": "Olive",
|
"Olive": "Zeytin yeşili",
|
||||||
"Green": "Green",
|
"Green": "Yeşil",
|
||||||
"Teal": "Teal",
|
"Teal": "Deniz mavisi",
|
||||||
"Lime": "Lime",
|
"Lime": "Limon",
|
||||||
"Purple": "Purple",
|
"Purple": "Mor",
|
||||||
"Navy": "Navy",
|
"Navy": "Lacivert",
|
||||||
"Blue": "Blue",
|
"Blue": "Mavi",
|
||||||
"Aqua": "Aqua"
|
"Aqua": "Açık Mavi"
|
||||||
},
|
},
|
||||||
"thickness": {
|
"thickness": {
|
||||||
"None": "Hiçbiri",
|
"None": "Hiçbiri",
|
||||||
"Thin": "Thin",
|
"Thin": "İnce",
|
||||||
"Normal": "Normal",
|
"Normal": "Normal",
|
||||||
"Thick": "Thick"
|
"Thick": "Kalın"
|
||||||
},
|
},
|
||||||
"subtitle_color": "Subtitle Color",
|
"subtitle_color": "Altyazı Rengi",
|
||||||
"subtitle_background_color": "Background Color",
|
"subtitle_background_color": "Arkaplan Rengi",
|
||||||
"subtitle_font": "Subtitle Font",
|
"subtitle_font": "Altyazı Yazı Tipi",
|
||||||
"ksplayer_title": "KSPlayer Settings",
|
"ksplayer_title": "KSPlayer Ayarları",
|
||||||
"hardware_decode": "Hardware Decoding",
|
"hardware_decode": "Donanımsal Kod Çözme",
|
||||||
"hardware_decode_description": "Use hardware acceleration for video decoding. Disable if you experience playback issues."
|
"hardware_decode_description": "Video kod çözme için donanımsal hızlandırma kullan. Oynatma sorunları yaşıyorsanız devre dışı bırakın."
|
||||||
},
|
},
|
||||||
"vlc_subtitles": {
|
"vlc_subtitles": {
|
||||||
"title": "VLC Subtitle Settings",
|
"title": "VLC Altyazı Ayarları",
|
||||||
"hint": "Customize subtitle appearance for VLC player. Changes take effect on next playback.",
|
"hint": "VLC oynatıcı için altyazı görünümünü değiştirin. Değişiklikler bir sonraki oynatmada etkili olacak.",
|
||||||
"text_color": "Text Color",
|
"text_color": "Metin Rengi",
|
||||||
"background_color": "Background Color",
|
"background_color": "Arkaplan Rengi",
|
||||||
"background_opacity": "Background Opacity",
|
"background_opacity": "Arkaplan Opaklığı",
|
||||||
"outline_color": "Outline Color",
|
"outline_color": "Kenarlık Rengi",
|
||||||
"outline_opacity": "Outline Opacity",
|
"outline_opacity": "Kenarlık Opaklığı",
|
||||||
"outline_thickness": "Outline Thickness",
|
"outline_thickness": "Kenarlık Kalınlığı",
|
||||||
"bold": "Bold Text",
|
"bold": "Kalın Metin",
|
||||||
"margin": "Bottom Margin"
|
"margin": "Alt Kenar Boşluğu"
|
||||||
},
|
},
|
||||||
"video_player": {
|
"video_player": {
|
||||||
"title": "Video Player",
|
"title": "Video oynatıcısı",
|
||||||
"video_player": "Video Player",
|
"video_player": "Video oynatıcısı",
|
||||||
"video_player_description": "Choose which video player to use on iOS.",
|
"video_player_description": "iOS'da hangi video oynatıcının kullanılacağını seçin.",
|
||||||
"ksplayer": "KSPlayer",
|
"ksplayer": "KSPlayer",
|
||||||
"vlc": "VLC"
|
"vlc": "VLC"
|
||||||
},
|
},
|
||||||
@@ -297,7 +297,7 @@
|
|||||||
"video_player": "Video player",
|
"video_player": "Video player",
|
||||||
"video_players": {
|
"video_players": {
|
||||||
"VLC_3": "VLC 3",
|
"VLC_3": "VLC 3",
|
||||||
"VLC_4": "VLC 4 (Experimental + PiP)"
|
"VLC_4": "VLC 4 (Deneysel + PiP)"
|
||||||
},
|
},
|
||||||
"show_custom_menu_links": "Özel Menü Bağlantılarını Göster",
|
"show_custom_menu_links": "Özel Menü Bağlantılarını Göster",
|
||||||
"show_large_home_carousel": "Show Large Home Carousel (beta)",
|
"show_large_home_carousel": "Show Large Home Carousel (beta)",
|
||||||
@@ -305,24 +305,24 @@
|
|||||||
"select_liraries_you_want_to_hide": "Kütüphane sekmesinden ve ana sayfa bölümlerinden gizlemek istediğiniz kütüphaneleri seçin.",
|
"select_liraries_you_want_to_hide": "Kütüphane sekmesinden ve ana sayfa bölümlerinden gizlemek istediğiniz kütüphaneleri seçin.",
|
||||||
"disable_haptic_feedback": "Dokunsal Geri Bildirimi Devre Dışı Bırak",
|
"disable_haptic_feedback": "Dokunsal Geri Bildirimi Devre Dışı Bırak",
|
||||||
"default_quality": "Varsayılan kalite",
|
"default_quality": "Varsayılan kalite",
|
||||||
"default_playback_speed": "Default Playback Speed",
|
"default_playback_speed": "Varsayılan Oynatma Hızı",
|
||||||
"auto_play_next_episode": "Auto-play Next Episode",
|
"auto_play_next_episode": "Otomatik Sonraki Bölümü Oynat",
|
||||||
"max_auto_play_episode_count": "Max Auto Play Episode Count",
|
"max_auto_play_episode_count": "En Fazla Otomatik Oynatılacak Bölüm Sayısı",
|
||||||
"disabled": "Devre dışı"
|
"disabled": "Devre dışı"
|
||||||
},
|
},
|
||||||
"downloads": {
|
"downloads": {
|
||||||
"downloads_title": "İndirmeler"
|
"downloads_title": "İndirmeler"
|
||||||
},
|
},
|
||||||
"music": {
|
"music": {
|
||||||
"title": "Music",
|
"title": "Müzik",
|
||||||
"playback_title": "Playback",
|
"playback_title": "Oynatma",
|
||||||
"playback_description": "Configure how music is played.",
|
"playback_description": "Müziğin nasıl çalınacağını ayarlayın.",
|
||||||
"prefer_downloaded": "Prefer Downloaded Songs",
|
"prefer_downloaded": "İndirilmiş Şarkıları Tercih Et",
|
||||||
"caching_title": "Caching",
|
"caching_title": "Önbellekleme",
|
||||||
"caching_description": "Automatically cache upcoming tracks for smoother playback.",
|
"caching_description": "Akıcı oynatım için gelecek şarkıları otomatik önbelleğe al.",
|
||||||
"lookahead_enabled": "Enable Look-Ahead Caching",
|
"lookahead_enabled": "Enable Look-Ahead Caching",
|
||||||
"lookahead_count": "Tracks to Pre-cache",
|
"lookahead_count": "Önden Önbelleklenecek Parça Sayısı",
|
||||||
"max_cache_size": "Max Cache Size"
|
"max_cache_size": "Maksimum Önbellek Boyutu"
|
||||||
},
|
},
|
||||||
"plugins": {
|
"plugins": {
|
||||||
"plugins_title": "Eklentiler",
|
"plugins_title": "Eklentiler",
|
||||||
@@ -345,7 +345,7 @@
|
|||||||
"order_by": {
|
"order_by": {
|
||||||
"DEFAULT": "Varsayılan",
|
"DEFAULT": "Varsayılan",
|
||||||
"VOTE_COUNT_AND_AVERAGE": "Vote count and average",
|
"VOTE_COUNT_AND_AVERAGE": "Vote count and average",
|
||||||
"POPULARITY": "Popularity"
|
"POPULARITY": "Popülerlik"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"marlin_search": {
|
"marlin_search": {
|
||||||
@@ -357,35 +357,35 @@
|
|||||||
"save_button": "Kaydet",
|
"save_button": "Kaydet",
|
||||||
"toasts": {
|
"toasts": {
|
||||||
"saved": "Kaydedildi",
|
"saved": "Kaydedildi",
|
||||||
"refreshed": "Settings refreshed from server"
|
"refreshed": "Ayarlar sunucudan yeniden alındı"
|
||||||
},
|
},
|
||||||
"refresh_from_server": "Refresh Settings from Server"
|
"refresh_from_server": "Ayarları Sunucudan Yeniden Al"
|
||||||
},
|
},
|
||||||
"streamystats": {
|
"streamystats": {
|
||||||
"enable_streamystats": "Enable Streamystats",
|
"enable_streamystats": "Streamystats'ı Etkinleştir",
|
||||||
"disable_streamystats": "Disable Streamystats",
|
"disable_streamystats": "Streamystats'ı Devre Dışı Bırak",
|
||||||
"enable_search": "Use for Search",
|
"enable_search": "Arama için kullan",
|
||||||
"url": "URL",
|
"url": "URL Adresi",
|
||||||
"server_url_placeholder": "http(s)://streamystats.example.com",
|
"server_url_placeholder": "http(s)://streamystats.example.com",
|
||||||
"streamystats_search_hint": "Enter the URL for your Streamystats server. The URL should include http or https and optionally the port.",
|
"streamystats_search_hint": "Streamystats sunucu URL'sini girin. URL, http veya https içermeli ve isteğe bağlı olarak portu içerebilir.",
|
||||||
"read_more_about_streamystats": "Read More About Streamystats.",
|
"read_more_about_streamystats": "Streamystats hakkında daha fazla bilgi.",
|
||||||
"save_button": "Save",
|
"save_button": "Kaydet",
|
||||||
"save": "Save",
|
"save": "Kaydet",
|
||||||
"features_title": "Features",
|
"features_title": "Özellikler",
|
||||||
"home_sections_title": "Home Sections",
|
"home_sections_title": "Home Sections",
|
||||||
"enable_movie_recommendations": "Movie Recommendations",
|
"enable_movie_recommendations": "Film Önerileri",
|
||||||
"enable_series_recommendations": "Series Recommendations",
|
"enable_series_recommendations": "Dizi Önerileri",
|
||||||
"enable_promoted_watchlists": "Promoted Watchlists",
|
"enable_promoted_watchlists": "Promoted Watchlists",
|
||||||
"hide_watchlists_tab": "Hide Watchlists Tab",
|
"hide_watchlists_tab": "Hide Watchlists Tab",
|
||||||
"home_sections_hint": "Show personalized recommendations and promoted watchlists from Streamystats on the home page.",
|
"home_sections_hint": "Show personalized recommendations and promoted watchlists from Streamystats on the home page.",
|
||||||
"recommended_movies": "Recommended Movies",
|
"recommended_movies": "Önerilen Filmler",
|
||||||
"recommended_series": "Recommended Series",
|
"recommended_series": "Önerilen Diziler",
|
||||||
"toasts": {
|
"toasts": {
|
||||||
"saved": "Saved",
|
"saved": "Kaydedildi",
|
||||||
"refreshed": "Settings refreshed from server",
|
"refreshed": "Ayarlar sunucudan yeniden alındı",
|
||||||
"disabled": "Streamystats disabled"
|
"disabled": "Streamystats devre dışı"
|
||||||
},
|
},
|
||||||
"refresh_from_server": "Refresh Settings from Server"
|
"refresh_from_server": "Ayarları Sunucudan Yeniden Al"
|
||||||
},
|
},
|
||||||
"kefinTweaks": {
|
"kefinTweaks": {
|
||||||
"watchlist_enabler": "Enable our Watchlist integration",
|
"watchlist_enabler": "Enable our Watchlist integration",
|
||||||
@@ -398,18 +398,18 @@
|
|||||||
"device_usage": "Cihaz {{availableSpace}}%",
|
"device_usage": "Cihaz {{availableSpace}}%",
|
||||||
"size_used": "{{used}} / {{total}} kullanıldı",
|
"size_used": "{{used}} / {{total}} kullanıldı",
|
||||||
"delete_all_downloaded_files": "Tüm indirilen dosyaları sil",
|
"delete_all_downloaded_files": "Tüm indirilen dosyaları sil",
|
||||||
"music_cache_title": "Music Cache",
|
"music_cache_title": "Müzik Ön Belleği",
|
||||||
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
"music_cache_description": "Automatically cache songs as you listen for smoother playback and offline support",
|
||||||
"enable_music_cache": "Enable Music Cache",
|
"enable_music_cache": "Müzik Ön Belleğini Etkinleştir",
|
||||||
"clear_music_cache": "Clear Music Cache",
|
"clear_music_cache": "Müzik Ön Belleğini Temizle",
|
||||||
"music_cache_size": "{{size}} cached",
|
"music_cache_size": "{{size}} ön belleklendi",
|
||||||
"music_cache_cleared": "Music cache cleared",
|
"music_cache_cleared": "Müzik ön belleği temizlendi",
|
||||||
"delete_all_downloaded_songs": "Delete All Downloaded Songs",
|
"delete_all_downloaded_songs": "Tüm İndirilen Müzikleri Sil",
|
||||||
"downloaded_songs_size": "{{size}} downloaded",
|
"downloaded_songs_size": "{{size}} indirildi",
|
||||||
"downloaded_songs_deleted": "Downloaded songs deleted"
|
"downloaded_songs_deleted": "İndirilen müzikler silindi"
|
||||||
},
|
},
|
||||||
"intro": {
|
"intro": {
|
||||||
"title": "Intro",
|
"title": "Giriş",
|
||||||
"show_intro": "Tanıtımı Göster",
|
"show_intro": "Tanıtımı Göster",
|
||||||
"reset_intro": "Tanıtımı Sıfırla"
|
"reset_intro": "Tanıtımı Sıfırla"
|
||||||
},
|
},
|
||||||
@@ -417,7 +417,7 @@
|
|||||||
"logs_title": "Günlükler",
|
"logs_title": "Günlükler",
|
||||||
"export_logs": "Export logs",
|
"export_logs": "Export logs",
|
||||||
"click_for_more_info": "Click for more info",
|
"click_for_more_info": "Click for more info",
|
||||||
"level": "Level",
|
"level": "Düzey",
|
||||||
"no_logs_available": "Günlükler mevcut değil",
|
"no_logs_available": "Günlükler mevcut değil",
|
||||||
"delete_all_logs": "Tüm günlükleri sil"
|
"delete_all_logs": "Tüm günlükleri sil"
|
||||||
},
|
},
|
||||||
@@ -433,22 +433,22 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sessions": {
|
"sessions": {
|
||||||
"title": "Sessions",
|
"title": "Oturumlar",
|
||||||
"no_active_sessions": "No Active Sessions"
|
"no_active_sessions": "Aktif Oturum Yok"
|
||||||
},
|
},
|
||||||
"downloads": {
|
"downloads": {
|
||||||
"downloads_title": "İndirilenler",
|
"downloads_title": "İndirilenler",
|
||||||
"tvseries": "Diziler",
|
"tvseries": "Diziler",
|
||||||
"movies": "Filmler",
|
"movies": "Filmler",
|
||||||
"queue": "Sıra",
|
"queue": "Sıra",
|
||||||
"other_media": "Other media",
|
"other_media": "Diğer medya",
|
||||||
"queue_hint": "Sıra ve indirmeler uygulama yeniden başlatıldığında kaybolacaktır",
|
"queue_hint": "Sıra ve indirmeler uygulama yeniden başlatıldığında kaybolacaktır",
|
||||||
"no_items_in_queue": "Sırada öğe yok",
|
"no_items_in_queue": "Sırada öğe yok",
|
||||||
"no_downloaded_items": "İndirilen öğe yok",
|
"no_downloaded_items": "İndirilen öğe yok",
|
||||||
"delete_all_movies_button": "Tüm Filmleri Sil",
|
"delete_all_movies_button": "Tüm Filmleri Sil",
|
||||||
"delete_all_tvseries_button": "Tüm Dizileri Sil",
|
"delete_all_tvseries_button": "Tüm Dizileri Sil",
|
||||||
"delete_all_button": "Tümünü Sil",
|
"delete_all_button": "Tümünü Sil",
|
||||||
"delete_all_other_media_button": "Delete other media",
|
"delete_all_other_media_button": "Diğer medyayı sil",
|
||||||
"active_download": "Aktif indirme",
|
"active_download": "Aktif indirme",
|
||||||
"no_active_downloads": "Aktif indirme yok",
|
"no_active_downloads": "Aktif indirme yok",
|
||||||
"active_downloads": "Aktif indirmeler",
|
"active_downloads": "Aktif indirmeler",
|
||||||
@@ -465,49 +465,49 @@
|
|||||||
"failed_to_delete_all_movies": "Filmler silinemedi",
|
"failed_to_delete_all_movies": "Filmler silinemedi",
|
||||||
"deleted_all_tvseries_successfully": "Tüm diziler başarıyla silindi!",
|
"deleted_all_tvseries_successfully": "Tüm diziler başarıyla silindi!",
|
||||||
"failed_to_delete_all_tvseries": "Diziler silinemedi",
|
"failed_to_delete_all_tvseries": "Diziler silinemedi",
|
||||||
"deleted_media_successfully": "Deleted other media Successfully!",
|
"deleted_media_successfully": "Diğer medya başarıyla silindi!",
|
||||||
"failed_to_delete_media": "Failed to Delete other media",
|
"failed_to_delete_media": "Failed to Delete other media",
|
||||||
"download_deleted": "Download Deleted",
|
"download_deleted": "İndirme silindi",
|
||||||
"download_cancelled": "İndirme iptal edildi",
|
"download_cancelled": "İndirme iptal edildi",
|
||||||
"could_not_delete_download": "Could Not Delete Download",
|
"could_not_delete_download": "İndirme Silinemedi",
|
||||||
"download_paused": "Download Paused",
|
"download_paused": "İndirme Duraklatıldı",
|
||||||
"could_not_pause_download": "Could Not Pause Download",
|
"could_not_pause_download": "İndirme Duraklatılamadı",
|
||||||
"download_resumed": "Download Resumed",
|
"download_resumed": "İndirme Devam Ediyor",
|
||||||
"could_not_resume_download": "Could Not Resume Download",
|
"could_not_resume_download": "İndirme Devam Ettirilemedi",
|
||||||
"download_completed": "İndirme tamamlandı",
|
"download_completed": "İndirme tamamlandı",
|
||||||
"download_failed": "Download Failed",
|
"download_failed": "İndirme başarısız oldu",
|
||||||
"download_failed_for_item": "{{item}} için indirme başarısız oldu - {{error}}",
|
"download_failed_for_item": "{{item}} için indirme başarısız oldu - {{error}}",
|
||||||
"download_completed_for_item": "{{item}} için indirme tamamlandı",
|
"download_completed_for_item": "{{item}} için indirme tamamlandı",
|
||||||
"download_started_for_item": "Download Started for {{item}}",
|
"download_started_for_item": "{{item}} için indirme başladı",
|
||||||
"failed_to_start_download": "Failed to start download",
|
"failed_to_start_download": "İndirme başlatılamadı",
|
||||||
"item_already_downloading": "{{item}} is already downloading",
|
"item_already_downloading": "{{item}} zaten indiriliyor",
|
||||||
"all_files_deleted": "All Downloads Deleted Successfully",
|
"all_files_deleted": "Bütün indirilenler başarıyla silindi",
|
||||||
"files_deleted_by_type": "{{count}} {{type}} deleted",
|
"files_deleted_by_type": "{{count}} {{type}} silindi",
|
||||||
"all_files_folders_and_jobs_deleted_successfully": "Tüm dosyalar, klasörler ve işler başarıyla silindi",
|
"all_files_folders_and_jobs_deleted_successfully": "Tüm dosyalar, klasörler ve işler başarıyla silindi",
|
||||||
"failed_to_clean_cache_directory": "Failed to clean cache directory",
|
"failed_to_clean_cache_directory": "Önbellek dizini temizlenemedi",
|
||||||
"could_not_get_download_url_for_item": "Could not get download URL for {{itemName}}",
|
"could_not_get_download_url_for_item": "{{itemName}} için indirme URL'si alınamadı",
|
||||||
"go_to_downloads": "İndirmelere git",
|
"go_to_downloads": "İndirmelere git",
|
||||||
"file_deleted": "{{item}} deleted"
|
"file_deleted": "{{item}} silindi"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"common": {
|
"common": {
|
||||||
"select": "Select",
|
"select": "Seç",
|
||||||
"no_trailer_available": "No trailer available",
|
"no_trailer_available": "Fragman mevcut değil",
|
||||||
"video": "Video",
|
"video": "Video",
|
||||||
"audio": "Ses",
|
"audio": "Ses",
|
||||||
"subtitle": "Altyazı",
|
"subtitle": "Altyazı",
|
||||||
"play": "Play",
|
"play": "Oynat",
|
||||||
"none": "None",
|
"none": "Hiçbiri",
|
||||||
"track": "Track",
|
"track": "Parça",
|
||||||
"cancel": "Cancel",
|
"cancel": "Vazgeç",
|
||||||
"delete": "Delete",
|
"delete": "Sil",
|
||||||
"ok": "OK",
|
"ok": "Tamam",
|
||||||
"remove": "Remove",
|
"remove": "Kaldır",
|
||||||
"next": "Next",
|
"next": "Sonraki",
|
||||||
"back": "Back",
|
"back": "Geri",
|
||||||
"continue": "Continue",
|
"continue": "Devam",
|
||||||
"verifying": "Verifying..."
|
"verifying": "Doğrulanıyor..."
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
"search": "Ara...",
|
"search": "Ara...",
|
||||||
@@ -521,10 +521,10 @@
|
|||||||
"episodes": "Bölümler",
|
"episodes": "Bölümler",
|
||||||
"collections": "Koleksiyonlar",
|
"collections": "Koleksiyonlar",
|
||||||
"actors": "Oyuncular",
|
"actors": "Oyuncular",
|
||||||
"artists": "Artists",
|
"artists": "Sanatçılar",
|
||||||
"albums": "Albums",
|
"albums": "Albümler",
|
||||||
"songs": "Songs",
|
"songs": "Şarkılar",
|
||||||
"playlists": "Playlists",
|
"playlists": "Çalma listeleri",
|
||||||
"request_movies": "Film Talep Et",
|
"request_movies": "Film Talep Et",
|
||||||
"request_series": "Dizi Talep Et",
|
"request_series": "Dizi Talep Et",
|
||||||
"recently_added": "Son Eklenenler",
|
"recently_added": "Son Eklenenler",
|
||||||
@@ -572,7 +572,7 @@
|
|||||||
"genres": "Türler",
|
"genres": "Türler",
|
||||||
"years": "Yıllar",
|
"years": "Yıllar",
|
||||||
"sort_by": "Sırala",
|
"sort_by": "Sırala",
|
||||||
"filter_by": "Filter By",
|
"filter_by": "Filtrele",
|
||||||
"sort_order": "Sıralama düzeni",
|
"sort_order": "Sıralama düzeni",
|
||||||
"tags": "Etiketler"
|
"tags": "Etiketler"
|
||||||
}
|
}
|
||||||
@@ -604,11 +604,11 @@
|
|||||||
"index": "İndeks:",
|
"index": "İndeks:",
|
||||||
"continue_watching": "İzlemeye devam et",
|
"continue_watching": "İzlemeye devam et",
|
||||||
"go_back": "Geri",
|
"go_back": "Geri",
|
||||||
"downloaded_file_title": "You have this file downloaded",
|
"downloaded_file_title": "Bu dosya indirilmiş",
|
||||||
"downloaded_file_message": "Do you want to play the downloaded file?",
|
"downloaded_file_message": "İndirilmiş dosyayı oynatmak ister misiniz?",
|
||||||
"downloaded_file_yes": "Yes",
|
"downloaded_file_yes": "Evet",
|
||||||
"downloaded_file_no": "No",
|
"downloaded_file_no": "Hayır",
|
||||||
"downloaded_file_cancel": "Cancel"
|
"downloaded_file_cancel": "Vazgeç"
|
||||||
},
|
},
|
||||||
"item_card": {
|
"item_card": {
|
||||||
"next_up": "Sıradaki",
|
"next_up": "Sıradaki",
|
||||||
@@ -624,7 +624,7 @@
|
|||||||
"no_similar_items_found": "Benzer öge bulunamadı",
|
"no_similar_items_found": "Benzer öge bulunamadı",
|
||||||
"video": "Video",
|
"video": "Video",
|
||||||
"more_details": "Daha fazla detay",
|
"more_details": "Daha fazla detay",
|
||||||
"media_options": "Media Options",
|
"media_options": "Medya Seçenekleri",
|
||||||
"quality": "Kalite",
|
"quality": "Kalite",
|
||||||
"audio": "Ses",
|
"audio": "Ses",
|
||||||
"subtitles": "Altyazı",
|
"subtitles": "Altyazı",
|
||||||
@@ -639,7 +639,7 @@
|
|||||||
"download_episode": "Bölümü indir",
|
"download_episode": "Bölümü indir",
|
||||||
"download_movie": "Filmi indir",
|
"download_movie": "Filmi indir",
|
||||||
"download_x_item": "{{item_count}} tane ögeyi indir",
|
"download_x_item": "{{item_count}} tane ögeyi indir",
|
||||||
"download_unwatched_only": "Unwatched Only",
|
"download_unwatched_only": "Yalnızca İzlenmemişler",
|
||||||
"download_button": "İndir"
|
"download_button": "İndir"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -693,10 +693,10 @@
|
|||||||
"number_episodes": "Bölüm {{episode_number}}",
|
"number_episodes": "Bölüm {{episode_number}}",
|
||||||
"born": "Doğum",
|
"born": "Doğum",
|
||||||
"appearances": "Görünmeler",
|
"appearances": "Görünmeler",
|
||||||
"approve": "Approve",
|
"approve": "Onayla",
|
||||||
"decline": "Decline",
|
"decline": "Reddet",
|
||||||
"requested_by": "Requested by {{user}}",
|
"requested_by": "{{user}} tarafından istendi",
|
||||||
"unknown_user": "Unknown User",
|
"unknown_user": "Bilinmeyen Kullanıcı",
|
||||||
"toasts": {
|
"toasts": {
|
||||||
"jellyseer_does_not_meet_requirements": "Jellyseerr sunucusu minimum sürüm gereksinimlerini karşılamıyor! Lütfen en az 2.0.0 sürümüne güncelleyin",
|
"jellyseer_does_not_meet_requirements": "Jellyseerr sunucusu minimum sürüm gereksinimlerini karşılamıyor! Lütfen en az 2.0.0 sürümüne güncelleyin",
|
||||||
"jellyseerr_test_failed": "Jellyseerr testi başarısız oldu. Lütfen tekrar deneyin.",
|
"jellyseerr_test_failed": "Jellyseerr testi başarısız oldu. Lütfen tekrar deneyin.",
|
||||||
@@ -705,10 +705,10 @@
|
|||||||
"requested_item": "{{item}} talep edildi!",
|
"requested_item": "{{item}} talep edildi!",
|
||||||
"you_dont_have_permission_to_request": "İstek göndermeye izniniz yok!",
|
"you_dont_have_permission_to_request": "İstek göndermeye izniniz yok!",
|
||||||
"something_went_wrong_requesting_media": "Medya talep edilirken bir şeyler ters gitti!",
|
"something_went_wrong_requesting_media": "Medya talep edilirken bir şeyler ters gitti!",
|
||||||
"request_approved": "Request Approved!",
|
"request_approved": "İstek Onaylandı!",
|
||||||
"request_declined": "Request Declined!",
|
"request_declined": "İstek Reddedildi!",
|
||||||
"failed_to_approve_request": "Failed to Approve Request",
|
"failed_to_approve_request": "İsteği Onaylama Başarısız Oldu",
|
||||||
"failed_to_decline_request": "Failed to Decline Request"
|
"failed_to_decline_request": "İsteği Reddetme Başarısız Oldu"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tabs": {
|
"tabs": {
|
||||||
@@ -719,127 +719,127 @@
|
|||||||
"favorites": "Favoriler"
|
"favorites": "Favoriler"
|
||||||
},
|
},
|
||||||
"music": {
|
"music": {
|
||||||
"title": "Music",
|
"title": "Müzik",
|
||||||
"tabs": {
|
"tabs": {
|
||||||
"suggestions": "Suggestions",
|
"suggestions": "Öneriler",
|
||||||
"albums": "Albums",
|
"albums": "Albümler",
|
||||||
"artists": "Artists",
|
"artists": "Sanatçılar",
|
||||||
"playlists": "Playlists",
|
"playlists": "Çalma listeleri",
|
||||||
"tracks": "tracks"
|
"tracks": "parçalar"
|
||||||
},
|
},
|
||||||
"filters": {
|
"filters": {
|
||||||
"all": "All"
|
"all": "Tümü"
|
||||||
},
|
},
|
||||||
"recently_added": "Recently Added",
|
"recently_added": "Son Eklenenler",
|
||||||
"recently_played": "Recently Played",
|
"recently_played": "Son Oynatılanlar",
|
||||||
"frequently_played": "Frequently Played",
|
"frequently_played": "Sık Oynatılanlar",
|
||||||
"explore": "Explore",
|
"explore": "Keşfet",
|
||||||
"top_tracks": "Top Tracks",
|
"top_tracks": "En Popülar Parçalar",
|
||||||
"play": "Play",
|
"play": "Oynat",
|
||||||
"shuffle": "Shuffle",
|
"shuffle": "Karıştır",
|
||||||
"play_top_tracks": "Play Top Tracks",
|
"play_top_tracks": "En Çok Oynatılan Parçaları Oynat",
|
||||||
"no_suggestions": "No suggestions available",
|
"no_suggestions": "Öneri mevcut değil",
|
||||||
"no_albums": "No albums found",
|
"no_albums": "Hiç albüm bulunamadı",
|
||||||
"no_artists": "No artists found",
|
"no_artists": "Hiç sanatçı bulunamadı",
|
||||||
"no_playlists": "No playlists found",
|
"no_playlists": "Hiç çalma listesi bulunamadı",
|
||||||
"album_not_found": "Album not found",
|
"album_not_found": "Albüm bulunamadı",
|
||||||
"artist_not_found": "Artist not found",
|
"artist_not_found": "Sanatçı bulunamadı",
|
||||||
"playlist_not_found": "Playlist not found",
|
"playlist_not_found": "Çalma listesi bulunamadı",
|
||||||
"track_options": {
|
"track_options": {
|
||||||
"play_next": "Play Next",
|
"play_next": "Sıradakini Çal",
|
||||||
"add_to_queue": "Add to Queue",
|
"add_to_queue": "Sıraya Ekle",
|
||||||
"add_to_playlist": "Add to Playlist",
|
"add_to_playlist": "Çalma listesine ekle",
|
||||||
"download": "Download",
|
"download": "İndir",
|
||||||
"downloaded": "Downloaded",
|
"downloaded": "İndirildi",
|
||||||
"downloading": "Downloading...",
|
"downloading": "İndiriliyor...",
|
||||||
"cached": "Cached",
|
"cached": "Önbellekte",
|
||||||
"delete_download": "Delete Download",
|
"delete_download": "İndirmeyi Sil",
|
||||||
"delete_cache": "Remove from Cache",
|
"delete_cache": "Ön bellekten kaldır",
|
||||||
"go_to_artist": "Go to Artist",
|
"go_to_artist": "Sanatçıya Git",
|
||||||
"go_to_album": "Go to Album",
|
"go_to_album": "Albüme Git",
|
||||||
"add_to_favorites": "Add to Favorites",
|
"add_to_favorites": "Favorilere Ekle",
|
||||||
"remove_from_favorites": "Remove from Favorites",
|
"remove_from_favorites": "Favorilerden Kaldır",
|
||||||
"remove_from_playlist": "Remove from Playlist"
|
"remove_from_playlist": "Çalma Listesinden Kaldır"
|
||||||
},
|
},
|
||||||
"playlists": {
|
"playlists": {
|
||||||
"create_playlist": "Create Playlist",
|
"create_playlist": "Çalma Listesi Oluştur",
|
||||||
"playlist_name": "Playlist Name",
|
"playlist_name": "Çalma Listesi Adı",
|
||||||
"enter_name": "Enter playlist name",
|
"enter_name": "Çalma listesi adı girin",
|
||||||
"create": "Create",
|
"create": "Oluştur",
|
||||||
"search_playlists": "Search playlists...",
|
"search_playlists": "Çalma listelerini ara...",
|
||||||
"added_to": "Added to {{name}}",
|
"added_to": "Şu çalma listesine eklendi: {{name}}",
|
||||||
"added": "Added to playlist",
|
"added": "Çalma listesine eklendi",
|
||||||
"removed_from": "Removed from {{name}}",
|
"removed_from": "Şu çalma listesinden kaldırıldı: {{name}}",
|
||||||
"removed": "Removed from playlist",
|
"removed": "Çalma listesinden kaldır",
|
||||||
"created": "Playlist created",
|
"created": "Çalma listesi oluşturuldu",
|
||||||
"create_new": "Create New Playlist",
|
"create_new": "Yeni Çalma Listesi Oluştur",
|
||||||
"failed_to_add": "Failed to add to playlist",
|
"failed_to_add": "Çalma listesine eklenemedi",
|
||||||
"failed_to_remove": "Failed to remove from playlist",
|
"failed_to_remove": "Çalma listesinden kaldırılamadı",
|
||||||
"failed_to_create": "Failed to create playlist",
|
"failed_to_create": "Çalma listesi oluşturulamadı",
|
||||||
"delete_playlist": "Delete Playlist",
|
"delete_playlist": "Çalma Listesini Sil",
|
||||||
"delete_confirm": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.",
|
"delete_confirm": "\"{{name}}\" adlı çalma listesini silmek istediğinize emin misiniz? Bu işlem geri alınamaz.",
|
||||||
"deleted": "Playlist deleted",
|
"deleted": "Çalma listesi silindi",
|
||||||
"failed_to_delete": "Failed to delete playlist"
|
"failed_to_delete": "Çalma listesi oluşturulamadı"
|
||||||
},
|
},
|
||||||
"sort": {
|
"sort": {
|
||||||
"title": "Sort By",
|
"title": "Sırala",
|
||||||
"alphabetical": "Alphabetical",
|
"alphabetical": "Alfabetik",
|
||||||
"date_created": "Date Created"
|
"date_created": "Oluşturulma Tarihi"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"watchlists": {
|
"watchlists": {
|
||||||
"title": "Watchlists",
|
"title": "İzleme listeleri",
|
||||||
"my_watchlists": "My Watchlists",
|
"my_watchlists": "İzleme listelerim",
|
||||||
"public_watchlists": "Public Watchlists",
|
"public_watchlists": "Herkese açık izleme listeleri",
|
||||||
"create_title": "Create Watchlist",
|
"create_title": "İzleme listesi oluştur",
|
||||||
"edit_title": "Edit Watchlist",
|
"edit_title": "İzleme listesini düzenle",
|
||||||
"create_button": "Create Watchlist",
|
"create_button": "İzleme listesi oluştur",
|
||||||
"save_button": "Save Changes",
|
"save_button": "Değişiklikleri Kaydet",
|
||||||
"delete_button": "Delete",
|
"delete_button": "Sil",
|
||||||
"remove_button": "Remove",
|
"remove_button": "Kaldır",
|
||||||
"cancel_button": "Cancel",
|
"cancel_button": "Vazgeç",
|
||||||
"name_label": "Name",
|
"name_label": "Name",
|
||||||
"name_placeholder": "Enter watchlist name",
|
"name_placeholder": "İzleme listesi adını girin",
|
||||||
"description_label": "Description",
|
"description_label": "Açıklama",
|
||||||
"description_placeholder": "Enter description (optional)",
|
"description_placeholder": "Açıklama girin (isteğe bağlı)",
|
||||||
"is_public_label": "Public Watchlist",
|
"is_public_label": "Herkese açık izleme listesi",
|
||||||
"is_public_description": "Allow others to view this watchlist",
|
"is_public_description": "Başkalarının da bu izleme listesini görmesine izin ver",
|
||||||
"allowed_type_label": "Content Type",
|
"allowed_type_label": "İçerik Türü",
|
||||||
"sort_order_label": "Default Sort Order",
|
"sort_order_label": "Varsayılan Sıralama",
|
||||||
"empty_title": "No Watchlists",
|
"empty_title": "İzleme listesi yok",
|
||||||
"empty_description": "Create your first watchlist to start organizing your media",
|
"empty_description": "Create your first watchlist to start organizing your media",
|
||||||
"empty_watchlist": "This watchlist is empty",
|
"empty_watchlist": "Bu izleme listesi boş",
|
||||||
"empty_watchlist_hint": "Add items from your library to this watchlist",
|
"empty_watchlist_hint": "Kütüphanenizdeki nesneleri bu izleme listesine ekleyin",
|
||||||
"not_configured_title": "Streamystats Not Configured",
|
"not_configured_title": "Streamystats ayarlanmamış",
|
||||||
"not_configured_description": "Configure Streamystats in settings to use watchlists",
|
"not_configured_description": "İzleme listelerini kullanmak için ayarlardan Streamystats'ı ayarlayın",
|
||||||
"go_to_settings": "Go to Settings",
|
"go_to_settings": "Ayarlara git",
|
||||||
"add_to_watchlist": "Add to Watchlist",
|
"add_to_watchlist": "İzleme Listesine Ekle",
|
||||||
"remove_from_watchlist": "Remove from Watchlist",
|
"remove_from_watchlist": "İzleme Listesinden Kaldır",
|
||||||
"select_watchlist": "Select Watchlist",
|
"select_watchlist": "İzleme Listesi Seç",
|
||||||
"create_new": "Create New Watchlist",
|
"create_new": "Yeni İzleme Listesi Oluştur",
|
||||||
"item": "item",
|
"item": "öğe",
|
||||||
"items": "items",
|
"items": "öğeler",
|
||||||
"public": "Public",
|
"public": "Herkese Açık",
|
||||||
"private": "Private",
|
"private": "Özel",
|
||||||
"you": "You",
|
"you": "Siz",
|
||||||
"by_owner": "By another user",
|
"by_owner": "Başka kullanıcı tarafından",
|
||||||
"not_found": "Watchlist not found",
|
"not_found": "İzleme listesi bulunamadı",
|
||||||
"delete_confirm_title": "Delete Watchlist",
|
"delete_confirm_title": "İzleme listesini sil",
|
||||||
"delete_confirm_message": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.",
|
"delete_confirm_message": "Are you sure you want to delete \"{{name}}\"? This action cannot be undone.",
|
||||||
"remove_item_title": "Remove from Watchlist",
|
"remove_item_title": "İzleme Listesinden Kaldır",
|
||||||
"remove_item_message": "Remove \"{{name}}\" from this watchlist?",
|
"remove_item_message": "{{name}} bu izleme listesinden kaldırılsın mı?",
|
||||||
"loading": "Loading watchlists...",
|
"loading": "İzleme listeleri yükleniyor...",
|
||||||
"no_compatible_watchlists": "No compatible watchlists",
|
"no_compatible_watchlists": "Uyumlu izleme listesi yok",
|
||||||
"create_one_first": "Create a watchlist that accepts this content type"
|
"create_one_first": "Bu içerik türünü kabul eden bir izleme listesi oluşturun"
|
||||||
},
|
},
|
||||||
"playback_speed": {
|
"playback_speed": {
|
||||||
"title": "Playback Speed",
|
"title": "Oynatma Hızı",
|
||||||
"apply_to": "Apply To",
|
"apply_to": "Şuna Uygula",
|
||||||
"speed": "Speed",
|
"speed": "Hız",
|
||||||
"scope": {
|
"scope": {
|
||||||
"media": "This media only",
|
"media": "Yalnızca bu medyada",
|
||||||
"show": "This show",
|
"show": "Bu dizide",
|
||||||
"all": "All media (default)"
|
"all": "Bütün medyalarda (varsayılan)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { useJellyseerr } from "@/hooks/useJellyseerr";
|
import { useSeerr } from "@/hooks/useSeerr";
|
||||||
import {
|
import {
|
||||||
MediaRequestStatus,
|
MediaRequestStatus,
|
||||||
MediaStatus,
|
MediaStatus,
|
||||||
@@ -18,13 +18,13 @@ import type MediaRequest from "../jellyseerr/server/entity/MediaRequest";
|
|||||||
import type { MovieDetails } from "../jellyseerr/server/models/Movie";
|
import type { MovieDetails } from "../jellyseerr/server/models/Movie";
|
||||||
import type { TvDetails } from "../jellyseerr/server/models/Tv";
|
import type { TvDetails } from "../jellyseerr/server/models/Tv";
|
||||||
|
|
||||||
export const useJellyseerrCanRequest = (
|
export const useSeerrCanRequest = (
|
||||||
item?: MovieResult | TvResult | MovieDetails | TvDetails | PersonCreditCast,
|
item?: MovieResult | TvResult | MovieDetails | TvDetails | PersonCreditCast,
|
||||||
) => {
|
) => {
|
||||||
const { jellyseerrUser } = useJellyseerr();
|
const { seerrUser } = useSeerr();
|
||||||
|
|
||||||
const canRequest = useMemo(() => {
|
const canRequest = useMemo(() => {
|
||||||
if (!jellyseerrUser || !item) return false;
|
if (!seerrUser || !item) return false;
|
||||||
|
|
||||||
const canNotRequest =
|
const canNotRequest =
|
||||||
item?.mediaInfo?.requests?.some(
|
item?.mediaInfo?.requests?.some(
|
||||||
@@ -46,22 +46,22 @@ export const useJellyseerrCanRequest = (
|
|||||||
? Permission.REQUEST_MOVIE
|
? Permission.REQUEST_MOVIE
|
||||||
: Permission.REQUEST_TV,
|
: Permission.REQUEST_TV,
|
||||||
],
|
],
|
||||||
jellyseerrUser.permissions,
|
seerrUser.permissions,
|
||||||
{ type: "or" },
|
{ type: "or" },
|
||||||
);
|
);
|
||||||
|
|
||||||
return userHasPermission && !canNotRequest;
|
return userHasPermission && !canNotRequest;
|
||||||
}, [item, jellyseerrUser]);
|
}, [item, seerrUser]);
|
||||||
|
|
||||||
const hasAdvancedRequestPermission = useMemo(() => {
|
const hasAdvancedRequestPermission = useMemo(() => {
|
||||||
if (!jellyseerrUser) return false;
|
if (!seerrUser) return false;
|
||||||
|
|
||||||
return hasPermission(
|
return hasPermission(
|
||||||
[Permission.REQUEST_ADVANCED, Permission.MANAGE_REQUESTS],
|
[Permission.REQUEST_ADVANCED, Permission.MANAGE_REQUESTS],
|
||||||
jellyseerrUser.permissions,
|
seerrUser.permissions,
|
||||||
{ type: "or" },
|
{ type: "or" },
|
||||||
);
|
);
|
||||||
}, [jellyseerrUser]);
|
}, [seerrUser]);
|
||||||
|
|
||||||
return [canRequest, hasAdvancedRequestPermission];
|
return [canRequest, hasAdvancedRequestPermission];
|
||||||
};
|
};
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user