mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-01-16 08:08:18 +00:00
Compare commits
48 Commits
feat/new-b
...
build-perf
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d02007c213 | ||
|
|
3e20050b64 | ||
|
|
a5552db377 | ||
|
|
59e9913c78 | ||
|
|
cf203a7c28 | ||
|
|
2b2797005a | ||
|
|
c53acb16fc | ||
|
|
d7958296a5 | ||
|
|
53570a5ee5 | ||
|
|
e3b7dd8241 | ||
|
|
786d082706 | ||
|
|
164de0af0d | ||
|
|
820b30b7e2 | ||
|
|
5bc4c4a856 | ||
|
|
f7e0667416 | ||
|
|
8c68283c56 | ||
|
|
bb0149406c | ||
|
|
b1d5630025 | ||
|
|
a5f5531bb9 | ||
|
|
c2a3817fa8 | ||
|
|
700bb2dc79 | ||
|
|
d741ca3ecc | ||
|
|
ae9f6b1ce4 | ||
|
|
3f3f95571c | ||
|
|
cd3f1a8cee | ||
|
|
be745dc136 | ||
|
|
7b6fe0a6c0 | ||
|
|
fc44283f09 | ||
|
|
b2f6edc54e | ||
|
|
b42d033b87 | ||
|
|
1fb166bcd1 | ||
|
|
de6133581b | ||
|
|
9e26196fb3 | ||
|
|
e6f69e0c7b | ||
|
|
e8bf2b721e | ||
|
|
84d7ad72a6 | ||
|
|
edc9c8640d | ||
|
|
49ece8d34e | ||
|
|
98d571187e | ||
|
|
d1e55ca506 | ||
|
|
adec78832a | ||
|
|
19f604e986 | ||
|
|
4398810b6c | ||
|
|
0a8068e1b3 | ||
|
|
4b7986a125 | ||
|
|
3eaeaa3b4a | ||
|
|
9cd9861253 | ||
|
|
5e9755ea3c |
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(find:*)",
|
||||
"Bash(bun install:*)",
|
||||
"Bash(bunx expo prebuild:*)",
|
||||
"Bash(bunx expo run:*)",
|
||||
"Bash(npx expo prebuild:*)",
|
||||
"Bash(npx expo run:*)",
|
||||
"Bash(xcodebuild:*)"
|
||||
],
|
||||
"deny": []
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
description: Don't write code directly in the ios folder.
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
We never write code directly in the ios folder. This code is generated by expo plugins.
|
||||
@@ -1 +1,15 @@
|
||||
EXPO_PUBLIC_WRITE_DEBUG=1
|
||||
# Streamyfin-specific debug flag
|
||||
EXPO_PUBLIC_WRITE_DEBUG=1
|
||||
|
||||
# Performance optimization (official Metro flag)
|
||||
EXPO_USE_METRO_REQUIRE=1
|
||||
|
||||
# TV development support (used in metro.config.js)
|
||||
# EXPO_TV=1
|
||||
# Uncomment the above line ONLY when working on TV features. Leave commented for mobile-only development to avoid issues.
|
||||
|
||||
# Fast resolver optimization (2025 feature)
|
||||
EXPO_USE_FAST_RESOLVER=1
|
||||
|
||||
# Bundle analysis for monitoring
|
||||
EXPO_ATLAS=1
|
||||
@@ -1 +1,26 @@
|
||||
EXPO_PUBLIC_WRITE_DEBUG=0
|
||||
# Streamyfin Production Configuration
|
||||
EXPO_PUBLIC_WRITE_DEBUG=0
|
||||
|
||||
# Production Performance Optimizations
|
||||
NODE_ENV=production
|
||||
EXPO_USE_METRO_REQUIRE=1
|
||||
EXPO_USE_FAST_RESOLVER=1
|
||||
|
||||
# Production Build Optimizations
|
||||
EXPO_OPTIMIZE_BUNDLE_SIZE=1
|
||||
EXPO_NO_CLIENT_ENV_VARS=1
|
||||
EXPO_LEGACY_BUNDLER=0
|
||||
|
||||
# Bundle Analysis (for monitoring)
|
||||
EXPO_ATLAS=0
|
||||
|
||||
# Production Cache Optimizations
|
||||
METRO_CACHE=1
|
||||
|
||||
# Security & Performance
|
||||
EXPO_NO_DOTENV=1
|
||||
FAST_REFRESH=0
|
||||
|
||||
# Production Bundle Features
|
||||
EXPO_USE_HERMES=1
|
||||
EXPO_MINIFY=1
|
||||
98
.github/copilot-instructions.md
vendored
98
.github/copilot-instructions.md
vendored
@@ -3,58 +3,94 @@
|
||||
## Project Overview
|
||||
|
||||
Streamyfin is a cross-platform Jellyfin video streaming client built with Expo (React Native).
|
||||
It supports mobile (iOS/Android) and TV platforms, and integrates with Jellyfin and Jellyseerr APIs.
|
||||
It supports mobile (iOS/Android) and TV platforms, integrates with Jellyfin and Jellyseerr APIs,
|
||||
and provides seamless media streaming with offline capabilities and Chromecast support.
|
||||
|
||||
## Main Technologies
|
||||
|
||||
- React Native (Expo)
|
||||
- TypeScript
|
||||
- React Query
|
||||
- Jotai (state management)
|
||||
- Jellyfin SDK (TypeScript)
|
||||
- BiomeJS (code formatting/linting)
|
||||
- EAS (Expo Application Services)
|
||||
- Shell scripting (for automation)
|
||||
- GitHub Actions (CI/CD)
|
||||
- **Runtime**: Bun (JavaScript/TypeScript execution)
|
||||
- **Framework**: React Native (Expo)
|
||||
- **Language**: TypeScript (strict mode)
|
||||
- **State Management**: Jotai (global state) + React Query (server state)
|
||||
- **API SDK**: Jellyfin SDK (TypeScript)
|
||||
- **Navigation**: Expo Router (file-based routing)
|
||||
- **Code Quality**: BiomeJS (formatting/linting)
|
||||
- **Build Platform**: EAS (Expo Application Services)
|
||||
- **CI/CD**: GitHub Actions with Bun
|
||||
|
||||
## Package Management
|
||||
|
||||
**CRITICAL: ALWAYS use `bun` for all package management operations**
|
||||
|
||||
- **NEVER use `npm`, `yarn` or `npx` commands**
|
||||
- Use `bun install` instead of `npm install` or `yarn install`
|
||||
- Use `bun add <package>` instead of `npm install <package>`
|
||||
- Use `bun remove <package>` instead of `npm uninstall <package>`
|
||||
- Use `bun run <script>` instead of `npm run <script>`
|
||||
- Use `bunx <command>` instead of `npx <command>`
|
||||
- For Expo: use `bunx create-expo-app` or `bunx @expo/cli`
|
||||
|
||||
## Code Structure
|
||||
|
||||
- `app/` – Main application code (screens, navigation, etc.)
|
||||
- `components/` – Reusable UI components
|
||||
- `providers/` – Context and API providers (e.g., JellyfinProvider.tsx)
|
||||
- `utils/` – Utility functions and atoms
|
||||
- `utils/` – Utility functions and Jotai atoms
|
||||
- `assets/` – Images and static assets
|
||||
- `scripts/` – Automation scripts (Node.js, Bash)
|
||||
- `plugins/` – Expo/Metro plugins
|
||||
- `README.md` – Project documentation
|
||||
|
||||
## Coding Conventions
|
||||
## Coding Standards
|
||||
|
||||
- Use TypeScript for all new code.
|
||||
- Prefer functional React components.
|
||||
- Use hooks for state and side effects.
|
||||
- Use Jotai for global state.
|
||||
- Use React Query for data fetching/caching.
|
||||
- Use BiomeJS for formatting and linting.
|
||||
- Follow the established folder structure for screens/components.
|
||||
- Use TypeScript for ALL files (no .js files)
|
||||
- Use descriptive English names for variables, functions, and components
|
||||
- Prefer functional React components with hooks
|
||||
- Use Jotai atoms for global state management
|
||||
- Use React Query for server state and caching
|
||||
- Follow BiomeJS formatting and linting rules
|
||||
- Use `const` over `let`, avoid `var` entirely
|
||||
- Implement proper error boundaries
|
||||
- Use React.memo() for performance optimization
|
||||
- Handle both mobile and TV navigation patterns
|
||||
|
||||
## API Usage
|
||||
## API Integration
|
||||
|
||||
- Use the Jellyfin SDK for all server interactions.
|
||||
- Use the `apiAtom` and `userAtom` from `JellyfinProvider` for authenticated API calls.
|
||||
- For navigation, use `expo-router`.
|
||||
- Use Jellyfin SDK for all server interactions
|
||||
- Access authenticated APIs via `apiAtom` and `userAtom` from JellyfinProvider
|
||||
- Implement proper loading states and error handling
|
||||
- Use React Query for caching and background updates
|
||||
- Handle offline scenarios gracefully
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
- Leverage Bun's superior runtime performance
|
||||
- Optimize FlatList components with proper props
|
||||
- Use lazy loading for non-critical components
|
||||
- Implement proper image caching strategies
|
||||
- Monitor bundle size and use tree-shaking effectively
|
||||
|
||||
## Testing
|
||||
|
||||
- Use Bun's built-in test runner when possible
|
||||
- Test files: `*.test.ts` or `*.test.tsx`
|
||||
- Run tests with: `bun test`
|
||||
- Mock external APIs in tests
|
||||
- Focus on testing business logic and custom hooks
|
||||
|
||||
## Commit Messages
|
||||
|
||||
- Use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) (e.g., `feat:`, `fix:`, `chore:`).
|
||||
- Example: `feat(player): add Chromecast support`
|
||||
Use [Conventional Commits](https://www.conventionalcommits.org/):
|
||||
Exemples:
|
||||
- `feat(player): add Chromecast support`
|
||||
- `fix(auth): handle expired JWT tokens`
|
||||
- `chore(deps): update Jellyfin SDK`
|
||||
|
||||
## Special Instructions
|
||||
|
||||
- When suggesting code, prefer using existing atoms, hooks, and utility functions.
|
||||
- When adding new features, ensure they are accessible via both mobile and TV navigation if relevant.
|
||||
- When updating dependencies or scripts, check for compatibility with Expo and EAS.
|
||||
|
||||
---
|
||||
- Prioritize cross-platform compatibility (mobile + TV)
|
||||
- Ensure accessibility for TV remote navigation
|
||||
- Use existing atoms, hooks, and utilities before creating new ones
|
||||
- Maintain compatibility with Expo and EAS workflows
|
||||
- Always verify Bun compatibility when suggesting new dependencies
|
||||
|
||||
**Copilot: Please use these instructions to provide context-aware suggestions and code completions for this repository.**
|
||||
12
.github/crowdin.yml
vendored
Normal file
12
.github/crowdin.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
"project_id_env": "CROWDIN_PROJECT_ID"
|
||||
"api_token_env": "CROWDIN_PERSONAL_TOKEN"
|
||||
"base_path": "."
|
||||
|
||||
"preserve_hierarchy": true
|
||||
|
||||
"files": [
|
||||
{
|
||||
"source": "translations/en.json",
|
||||
"translation": "translations/%two_letters_code%.json"
|
||||
}
|
||||
]
|
||||
2
.github/workflows/artifact-comment.yml
vendored
2
.github/workflows/artifact-comment.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: 🔍 Get PR and Artifacts
|
||||
uses: actions/github-script@v8
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
|
||||
with:
|
||||
script: |
|
||||
// Check if we're running from a fork (more precise detection)
|
||||
|
||||
6
.github/workflows/ci-codeql.yml
vendored
6
.github/workflows/ci-codeql.yml
vendored
@@ -31,13 +31,13 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 🏁 Initialize CodeQL
|
||||
uses: github/codeql-action/init@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5
|
||||
uses: github/codeql-action/init@e296a935590eb16afc0c0108289f68c87e2a89a5 # v4.30.7
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
queries: +security-extended,security-and-quality
|
||||
|
||||
- name: 🛠️ Autobuild
|
||||
uses: github/codeql-action/autobuild@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5
|
||||
uses: github/codeql-action/autobuild@e296a935590eb16afc0c0108289f68c87e2a89a5 # v4.30.7
|
||||
|
||||
- name: 🧪 Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.30.5
|
||||
uses: github/codeql-action/analyze@e296a935590eb16afc0c0108289f68c87e2a89a5 # v4.30.7
|
||||
|
||||
50
.github/workflows/crowdin.yml
vendored
50
.github/workflows/crowdin.yml
vendored
@@ -1,34 +1,50 @@
|
||||
name: Crowdin Action
|
||||
name: 🌐 Translation Sync
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
branches: [develop]
|
||||
paths:
|
||||
- "translations/**"
|
||||
- "crowdin.yml"
|
||||
- "i18n.ts"
|
||||
- ".github/workflows/crowdin.yml"
|
||||
# Run weekly to pull new translations
|
||||
schedule:
|
||||
- cron: "0 2 * * 1" # Every Monday at 2 AM UTC
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
synchronize-with-crowdin:
|
||||
sync-translations:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: 📥 Checkout Repository
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: crowdin action
|
||||
uses: crowdin/github-action@v2
|
||||
- name: 🌐 Sync Translations with Crowdin
|
||||
uses: crowdin/github-action@0749939f635900a2521aa6aac7a3766642b2dc71 # v2.11.0
|
||||
with:
|
||||
upload_sources: true
|
||||
upload_translations: true
|
||||
download_translations: true
|
||||
localization_branch_name: l10n_crowdin_translations
|
||||
localization_branch_name: I10n_crowdin_translations
|
||||
create_pull_request: true
|
||||
pull_request_title: 'feat: New Crowdin Translations'
|
||||
pull_request_body: 'New Crowdin translations by [Crowdin GH Action](https://github.com/crowdin/github-action)'
|
||||
pull_request_base_branch_name: 'develop'
|
||||
pull_request_title: "feat: New Crowdin Translations"
|
||||
pull_request_body: "New Crowdin translations by [Crowdin GH Action](https://github.com/crowdin/github-action)"
|
||||
pull_request_base_branch_name: "develop"
|
||||
pull_request_labels: "🌐 translation"
|
||||
# Quality control options
|
||||
skip_untranslated_strings: true
|
||||
export_only_approved: false
|
||||
# Commit customization
|
||||
commit_message: "feat(i18n): update translations from Crowdin"
|
||||
env:
|
||||
# A classic GitHub Personal Access Token with the 'repo' scope selected (the user should have write access to the repository).
|
||||
GITHUB_TOKEN: ${{ secrets.CROWDIN_GITHUB_TOKEN }}
|
||||
|
||||
# A numeric ID, found at https://crowdin.com/project/<projectName>/tools/api
|
||||
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
|
||||
|
||||
# Visit https://crowdin.com/settings#api-key to create this token
|
||||
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
|
||||
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
|
||||
|
||||
25
.github/workflows/notification.yml
vendored
25
.github/workflows/notification.yml
vendored
@@ -1,13 +1,18 @@
|
||||
name: 🛎️ Discord Pull Request Notification
|
||||
name: 🛎️ Discord Notification
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened]
|
||||
branches: [develop]
|
||||
workflow_run:
|
||||
workflows: ["*"]
|
||||
types: [completed]
|
||||
branches: [develop]
|
||||
|
||||
jobs:
|
||||
notify:
|
||||
runs-on: ubuntu-24.04
|
||||
if: github.event_name == 'pull_request'
|
||||
steps:
|
||||
- name: 🛎️ Notify Discord
|
||||
uses: Ilshidur/action-discord@d2594079a10f1d6739ee50a2471f0ca57418b554 # 0.4.0
|
||||
@@ -21,3 +26,21 @@ jobs:
|
||||
**By:** ${{ github.event.pull_request.user.login }}
|
||||
**Branch:** ${{ github.event.pull_request.head.ref }}
|
||||
🔗 ${{ github.event.pull_request.html_url }}
|
||||
|
||||
notify-on-failure:
|
||||
runs-on: ubuntu-24.04
|
||||
if: github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'failure'
|
||||
steps:
|
||||
- name: 🚨 Notify Discord on Failure
|
||||
uses: Ilshidur/action-discord@d2594079a10f1d6739ee50a2471f0ca57418b554 # 0.4.0
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.WEBHOOK_FAILED_JOB_URL }}
|
||||
DISCORD_AVATAR: https://avatars.githubusercontent.com/u/193271640
|
||||
with:
|
||||
args: |
|
||||
🚨 **Workflow Failed** in **${{ github.repository }}**
|
||||
**Workflow:** ${{ github.event.workflow_run.name }}
|
||||
**Branch:** ${{ github.event.workflow_run.head_branch }}
|
||||
**Triggered by:** ${{ github.event.workflow_run.triggering_actor.login }}
|
||||
**Commit:** ${{ github.event.workflow_run.head_commit.message }}
|
||||
🔗 ${{ github.event.workflow_run.html_url }}
|
||||
|
||||
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: 🔄 Mark/Close Stale Issues
|
||||
uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0
|
||||
uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0
|
||||
with:
|
||||
# Global settings
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
76
.gitignore
vendored
76
.gitignore
vendored
@@ -1,27 +1,16 @@
|
||||
# Dependencies and Package Managers
|
||||
node_modules/
|
||||
.expo/
|
||||
dist/
|
||||
npm-debug.*
|
||||
*.jks
|
||||
*.p8
|
||||
*.p12
|
||||
*.key
|
||||
*.mobileprovision
|
||||
*.orig.*
|
||||
web-build/
|
||||
modules/vlc-player/android/build
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
|
||||
expo-env.d.ts
|
||||
|
||||
Streamyfin.app
|
||||
|
||||
*.mp4
|
||||
Streamyfin.app
|
||||
bun.lock
|
||||
bun.lockb
|
||||
package-lock.json
|
||||
|
||||
# Expo and React Native Build Artifacts
|
||||
.expo/
|
||||
dist/
|
||||
web-build/
|
||||
.tsbuildinfo
|
||||
|
||||
# Platform-specific Build Directories
|
||||
/ios
|
||||
/android
|
||||
/iostv
|
||||
@@ -29,21 +18,50 @@ package-lock.json
|
||||
/androidmobile
|
||||
/androidtv
|
||||
|
||||
# Module-specific Builds
|
||||
modules/vlc-player/android/build
|
||||
modules/player/android
|
||||
modules/hls-downloader/android/build
|
||||
|
||||
pc-api-7079014811501811218-719-3b9f15aeccf8.json
|
||||
credentials.json
|
||||
# Generated Applications
|
||||
Streamyfin.app
|
||||
*.apk
|
||||
*.ipa
|
||||
.continuerc.json
|
||||
*.aab
|
||||
|
||||
# Certificates and Keys
|
||||
*.jks
|
||||
*.p8
|
||||
*.p12
|
||||
*.key
|
||||
*.mobileprovision
|
||||
|
||||
# Debug and Temporary Files
|
||||
npm-debug.*
|
||||
*.orig.*
|
||||
*.mp4
|
||||
|
||||
# OS-specific Files
|
||||
# macOS
|
||||
.DS_Store
|
||||
|
||||
# IDE and Editor Files
|
||||
.vscode/
|
||||
.idea/
|
||||
.ruby-lsp
|
||||
modules/hls-downloader/android/build
|
||||
streamyfin-4fec1-firebase-adminsdk.json
|
||||
.cursor/
|
||||
.claude/
|
||||
|
||||
# Environment and Configuration
|
||||
expo-env.d.ts
|
||||
.continuerc.json
|
||||
.env
|
||||
.env.local
|
||||
*.aab
|
||||
/version-backup-*
|
||||
bun.lockb
|
||||
|
||||
# Secrets and Credentials
|
||||
pc-api-7079014811501811218-719-3b9f15aeccf8.json
|
||||
credentials.json
|
||||
streamyfin-4fec1-firebase-adminsdk.json
|
||||
|
||||
# Version and Backup Files
|
||||
/version-backup-*
|
||||
24
.vscode/extensions.json
vendored
Normal file
24
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
// ==========================================
|
||||
// Streamyfin - Recommended VS Code Extensions
|
||||
// ==========================================
|
||||
// Essential extensions for working with Streamyfin
|
||||
// See .github/copilot-instructions.md for coding standards
|
||||
|
||||
"recommendations": [
|
||||
// Code Quality & Formatting
|
||||
"biomejs.biome", // Fast formatter and linter for JavaScript/TypeScript - replaces ESLint + Prettier
|
||||
|
||||
// React Native & Expo
|
||||
"expo.vscode-expo-tools", // Official Expo extension - provides commands, debugging, and config IntelliSense
|
||||
"msjsdiag.vscode-react-native", // React Native debugging and IntelliSense - essential for RN development
|
||||
|
||||
// Developer Experience
|
||||
"bradlc.vscode-tailwindcss", // Tailwind CSS IntelliSense - autocomplete for NativeWind classes
|
||||
"yoavbls.pretty-ts-errors", // Makes TypeScript error messages human-readable with formatting and highlights
|
||||
"usernamehw.errorlens", // Shows errors and warnings inline in the editor - faster debugging
|
||||
|
||||
// Bun Support
|
||||
"oven.bun-vscode" // Official Bun extension - provides debugging and language support for Bun runtime
|
||||
]
|
||||
}
|
||||
176
.vscode/settings.json
vendored
176
.vscode/settings.json
vendored
@@ -1,24 +1,178 @@
|
||||
{
|
||||
// ==========================================
|
||||
// FORMATTING & LINTING
|
||||
// ==========================================
|
||||
|
||||
// Biome as default formatter
|
||||
"editor.defaultFormatter": "biomejs.biome",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnPaste": true,
|
||||
"editor.formatOnType": false,
|
||||
|
||||
// Language-specific formatters
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "biomejs.biome",
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "biomejs.biome",
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "biomejs.biome",
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"prettier.printWidth": 120,
|
||||
"[swift]": {
|
||||
"editor.defaultFormatter": "sswg.swift-lang"
|
||||
},
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "biomejs.biome",
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "biomejs.biome",
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[javascriptreact]": {
|
||||
"editor.defaultFormatter": "biomejs.biome",
|
||||
"editor.formatOnSave": true
|
||||
}
|
||||
},
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "biomejs.biome",
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[jsonc]": {
|
||||
"editor.defaultFormatter": "biomejs.biome",
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[swift]": {
|
||||
"editor.insertSpaces": true,
|
||||
"editor.tabSize": 2
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// TYPESCRIPT & JAVASCRIPT
|
||||
// ==========================================
|
||||
|
||||
// TypeScript performance optimizations
|
||||
"typescript.preferences.includePackageJsonAutoImports": "auto",
|
||||
"typescript.suggest.autoImports": true,
|
||||
"typescript.updateImportsOnFileMove.enabled": "always",
|
||||
"typescript.preferences.preferTypeOnlyAutoImports": true,
|
||||
"typescript.preferences.importModuleSpecifier": "relative",
|
||||
"typescript.preferences.includeCompletionsForImportStatements": true,
|
||||
"typescript.preferences.includeCompletionsWithSnippetText": true,
|
||||
|
||||
// JavaScript settings
|
||||
"javascript.preferences.importModuleSpecifier": "relative",
|
||||
"javascript.suggest.autoImports": true,
|
||||
"javascript.updateImportsOnFileMove.enabled": "always",
|
||||
|
||||
// ==========================================
|
||||
// REACT NATIVE & EXPO
|
||||
// ==========================================
|
||||
|
||||
// File associations for React Native
|
||||
"files.associations": {
|
||||
"*.expo.ts": "typescript",
|
||||
"*.expo.tsx": "typescriptreact",
|
||||
"*.expo.js": "javascript",
|
||||
"*.expo.jsx": "javascriptreact",
|
||||
"metro.config.js": "javascript",
|
||||
"babel.config.js": "javascript",
|
||||
"app.config.js": "javascript",
|
||||
"eas.json": "jsonc"
|
||||
},
|
||||
|
||||
// React Native specific settings
|
||||
"emmet.includeLanguages": {
|
||||
"typescriptreact": "html",
|
||||
"javascriptreact": "html"
|
||||
},
|
||||
"emmet.triggerExpansionOnTab": true,
|
||||
|
||||
// Exclude build directories from search
|
||||
"search.exclude": {
|
||||
"**/node_modules": true
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// EDITOR PERFORMANCE & UX
|
||||
// ==========================================
|
||||
|
||||
// Performance optimizations
|
||||
"editor.largeFileOptimizations": true,
|
||||
"files.watcherExclude": {
|
||||
"**/.git/objects/**": true,
|
||||
"**/.git/subtree-cache/**": true,
|
||||
"**/node_modules/**": true,
|
||||
"**/.expo/**": true,
|
||||
"**/ios/**": true,
|
||||
"**/android/**": true,
|
||||
"**/build/**": true,
|
||||
"**/dist/**": true
|
||||
},
|
||||
|
||||
// Better editor behavior
|
||||
"editor.suggestSelection": "first",
|
||||
"editor.quickSuggestions": {
|
||||
"strings": true,
|
||||
"comments": true,
|
||||
"other": true
|
||||
},
|
||||
"editor.snippetSuggestions": "top",
|
||||
"editor.tabCompletion": "on",
|
||||
"editor.wordBasedSuggestions": "off",
|
||||
|
||||
// ==========================================
|
||||
// TERMINAL & DEVELOPMENT
|
||||
// ==========================================
|
||||
|
||||
// Terminal settings for Bun (Windows-specific)
|
||||
"terminal.integrated.profiles.windows": {
|
||||
"Command Prompt": {
|
||||
"path": "C:\\Windows\\System32\\cmd.exe",
|
||||
"env": {
|
||||
"PATH": "${env:PATH};./node_modules/.bin"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// ==========================================
|
||||
// WORKSPACE & NAVIGATION
|
||||
// ==========================================
|
||||
|
||||
// Better workspace navigation
|
||||
"explorer.fileNesting.enabled": true,
|
||||
"explorer.fileNesting.expand": false,
|
||||
"explorer.fileNesting.patterns": {
|
||||
"*.ts": "${capture}.js",
|
||||
"*.tsx": "${capture}.js",
|
||||
"*.js": "${capture}.js,${capture}.js.map,${capture}.min.js,${capture}.d.ts",
|
||||
"*.jsx": "${capture}.js",
|
||||
"package.json": "package-lock.json,yarn.lock,bun.lock,bun.lockb,.yarnrc,.yarnrc.yml",
|
||||
"tsconfig.json": "tsconfig.*.json",
|
||||
".env": ".env.*",
|
||||
"app.json": "app.config.js,eas.json,expo-env.d.ts",
|
||||
"README.md": "LICENSE.txt,SECURITY.md,CODE_OF_CONDUCT.md,CONTRIBUTING.md"
|
||||
},
|
||||
|
||||
// Better breadcrumbs and navigation
|
||||
"breadcrumbs.enabled": true,
|
||||
"outline.showVariables": true,
|
||||
"outline.showConstants": true,
|
||||
|
||||
// ==========================================
|
||||
// GIT & VERSION CONTROL
|
||||
// ==========================================
|
||||
|
||||
// Git integration
|
||||
"git.autofetch": true,
|
||||
"git.enableSmartCommit": true,
|
||||
"git.confirmSync": false,
|
||||
"git.ignoreLimitWarning": true,
|
||||
|
||||
// ==========================================
|
||||
// CODE QUALITY & ERRORS
|
||||
// ==========================================
|
||||
|
||||
// Better error detection
|
||||
"typescript.validate.enable": true,
|
||||
"javascript.validate.enable": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll": "explicit",
|
||||
"source.organizeImports": "explicit"
|
||||
},
|
||||
|
||||
// Problem matcher for better error display
|
||||
"typescript.tsc.autoDetect": "on"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
module.exports = ({ config }) => {
|
||||
if (process.env.EXPO_TV !== "1") {
|
||||
if (process.env.EXPO_TV === "1") {
|
||||
// Add TV-specific plugin for TV builds
|
||||
config.plugins.push("@react-native-tvos/config-tv");
|
||||
} else {
|
||||
// Add non-TV specific plugins for phone builds
|
||||
config.plugins.push("expo-background-task");
|
||||
|
||||
config.plugins.push([
|
||||
|
||||
1
app.json
1
app.json
@@ -53,7 +53,6 @@
|
||||
"googleServicesFile": "./google-services.json"
|
||||
},
|
||||
"plugins": [
|
||||
"@react-native-tvos/config-tv",
|
||||
"expo-router",
|
||||
"expo-font",
|
||||
[
|
||||
|
||||
@@ -115,4 +115,4 @@
|
||||
<path id="path259-2-6-4-6-7-0-1-0-5-9-4-7-1-5-7-6-2" class="cls-11" d="M46.97,39.46c5.94,0,10.75,4.81,10.75,10.75s-4.81,10.75-10.75,10.75-10.75-4.81-10.75-10.75c0-1.1.16-2.16.47-3.17.84,1.87,2.72,3.17,4.9,3.17,2.97,0,5.37-2.41,5.37-5.37,0-2.18-1.3-4.06-3.17-4.9,1-.31,2.06-.47,3.17-.47h.01Z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/2.2.4/schema.json",
|
||||
"$schema": "https://biomejs.dev/schemas/2.2.5/schema.json",
|
||||
"files": {
|
||||
"includes": [
|
||||
"**/*",
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import { View, type ViewProps } from "react-native";
|
||||
import { Text } from "@/components/common/Text";
|
||||
|
||||
interface Props extends ViewProps {}
|
||||
|
||||
export const TitleHeader: React.FC<Props> = ({ ...props }) => {
|
||||
return (
|
||||
<View {...props}>
|
||||
<Text />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
@@ -2,6 +2,7 @@ import { Feather, Ionicons } from "@expo/vector-icons";
|
||||
import type { Api } from "@jellyfin/sdk";
|
||||
import type {
|
||||
BaseItemDto,
|
||||
BaseItemDtoQueryResult,
|
||||
BaseItemKind,
|
||||
} from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import {
|
||||
@@ -356,6 +357,16 @@ export const HomeIndex = () => {
|
||||
});
|
||||
return response.data || [];
|
||||
}
|
||||
if (section.custom) {
|
||||
const response = await api.get<BaseItemDtoQueryResult>(
|
||||
section.custom.endpoint,
|
||||
{
|
||||
params: { ...(section.custom.query || {}), userId: user?.Id },
|
||||
headers: section.custom.headers || {},
|
||||
},
|
||||
);
|
||||
return response.data.Items || [];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
type: "ScrollingCollectionList",
|
||||
|
||||
@@ -28,13 +28,13 @@ export const SubtitleToggles: React.FC<Props> = ({ ...props }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
// Get VLC subtitle settings from the settings system
|
||||
const textColor = pluginSettings?.vlcTextColor ?? "White";
|
||||
const backgroundColor = pluginSettings?.vlcBackgroundColor ?? "Black";
|
||||
const outlineColor = pluginSettings?.vlcOutlineColor ?? "Black";
|
||||
const outlineThickness = pluginSettings?.vlcOutlineThickness ?? "Normal";
|
||||
const backgroundOpacity = pluginSettings?.vlcBackgroundOpacity ?? 128;
|
||||
const outlineOpacity = pluginSettings?.vlcOutlineOpacity ?? 255;
|
||||
const isBold = pluginSettings?.vlcIsBold ?? false;
|
||||
const textColor = settings?.vlcTextColor ?? "White";
|
||||
const backgroundColor = settings?.vlcBackgroundColor ?? "Black";
|
||||
const outlineColor = settings?.vlcOutlineColor ?? "Black";
|
||||
const outlineThickness = settings?.vlcOutlineThickness ?? "Normal";
|
||||
const backgroundOpacity = settings?.vlcBackgroundOpacity ?? 128;
|
||||
const outlineOpacity = settings?.vlcOutlineOpacity ?? 255;
|
||||
const isBold = settings?.vlcIsBold ?? false;
|
||||
|
||||
if (isTv) return null;
|
||||
if (!settings) return null;
|
||||
|
||||
64
eas.json
64
eas.json
@@ -4,6 +4,17 @@
|
||||
},
|
||||
"build": {
|
||||
"development": {
|
||||
"resourceClass": "medium",
|
||||
"cache": {
|
||||
"key": "dev-{{ checksum \"bun.lock\" \"app.config.js\" }}",
|
||||
"paths": [
|
||||
"~/.bun/install/cache",
|
||||
"node_modules",
|
||||
".expo",
|
||||
"android/.gradle",
|
||||
"ios/Pods"
|
||||
]
|
||||
},
|
||||
"environment": "development",
|
||||
"developmentClient": true,
|
||||
"distribution": "internal",
|
||||
@@ -15,6 +26,11 @@
|
||||
}
|
||||
},
|
||||
"development_tv": {
|
||||
"resourceClass": "medium",
|
||||
"cache": {
|
||||
"key": "development-tv-{{ checksum \"bun.lock\" \"package.json\" }}",
|
||||
"paths": ["~/.bun/install/cache", "node_modules", ".expo"]
|
||||
},
|
||||
"environment": "development",
|
||||
"developmentClient": true,
|
||||
"distribution": "internal",
|
||||
@@ -27,6 +43,11 @@
|
||||
}
|
||||
},
|
||||
"development-simulator": {
|
||||
"resourceClass": "medium",
|
||||
"cache": {
|
||||
"key": "development-simulator-{{ checksum \"bun.lock\" \"package.json\" }}",
|
||||
"paths": ["~/.bun/install/cache", "node_modules", ".expo"]
|
||||
},
|
||||
"environment": "development",
|
||||
"developmentClient": true,
|
||||
"distribution": "internal",
|
||||
@@ -38,19 +59,57 @@
|
||||
}
|
||||
},
|
||||
"preview": {
|
||||
"resourceClass": "large",
|
||||
"cache": {
|
||||
"key": "preview-{{ checksum \"bun.lock\" \"app.config.js\" }}",
|
||||
"paths": [
|
||||
"~/.bun/install/cache",
|
||||
"node_modules",
|
||||
".expo",
|
||||
"android/.gradle",
|
||||
"ios/Pods",
|
||||
".next"
|
||||
]
|
||||
},
|
||||
"distribution": "internal",
|
||||
"env": {
|
||||
"EXPO_OPTIMIZE_BUNDLE_SIZE": "1",
|
||||
"NODE_ENV": "production",
|
||||
"EXPO_PUBLIC_WRITE_DEBUG": "1"
|
||||
}
|
||||
},
|
||||
"production": {
|
||||
"resourceClass": "large",
|
||||
"cache": {
|
||||
"key": "production-{{ checksum \"bun.lock\" \"app.config.js\" }}",
|
||||
"paths": [
|
||||
"~/.bun/install/cache",
|
||||
"node_modules",
|
||||
".expo",
|
||||
"android/.gradle",
|
||||
"ios/Pods"
|
||||
]
|
||||
},
|
||||
"environment": "production",
|
||||
"channel": "0.39.0",
|
||||
"android": {
|
||||
"buildType": "app-bundle",
|
||||
"image": "latest"
|
||||
},
|
||||
"ios": {
|
||||
"image": "latest"
|
||||
},
|
||||
"env": {
|
||||
"EXPO_OPTIMIZE_BUNDLE_SIZE": "1",
|
||||
"NODE_ENV": "production"
|
||||
}
|
||||
},
|
||||
"production-apk": {
|
||||
"resourceClass": "large",
|
||||
"cache": {
|
||||
"key": "production-apk-{{ checksum \"bun.lock\" \"package.json\" }}",
|
||||
"paths": ["~/.bun/install/cache", "node_modules", ".expo"]
|
||||
},
|
||||
"environment": "production",
|
||||
"channel": "0.39.0",
|
||||
"android": {
|
||||
@@ -59,6 +118,11 @@
|
||||
}
|
||||
},
|
||||
"production-apk-tv": {
|
||||
"resourceClass": "large",
|
||||
"cache": {
|
||||
"key": "production-apk-tv-{{ checksum \"bun.lock\" \"package.json\" }}",
|
||||
"paths": ["~/.bun/install/cache", "node_modules", ".expo"]
|
||||
},
|
||||
"environment": "production",
|
||||
"channel": "0.39.0",
|
||||
"android": {
|
||||
|
||||
6
i18n.ts
6
i18n.ts
@@ -1,6 +1,7 @@
|
||||
import { getLocales } from "expo-localization";
|
||||
import i18n from "i18next";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
import ar from "./translations/ar.json";
|
||||
import ca from "./translations/ca.json";
|
||||
import da from "./translations/da.json";
|
||||
import de from "./translations/de.json";
|
||||
@@ -9,6 +10,7 @@ import eo from "./translations/eo.json";
|
||||
import es from "./translations/es.json";
|
||||
import fi from "./translations/fi.json";
|
||||
import fr from "./translations/fr.json";
|
||||
import hu from "./translations/hu.json";
|
||||
import it from "./translations/it.json";
|
||||
import ja from "./translations/ja.json";
|
||||
import nb from "./translations/nb.json";
|
||||
@@ -29,6 +31,7 @@ import zhTW from "./translations/zh-TW.json";
|
||||
|
||||
export const APP_LANGUAGES = [
|
||||
{ label: "Catalan", value: "ca" },
|
||||
{ label: "العربية", value: "ar" },
|
||||
{ label: "Dansk", value: "da" },
|
||||
{ label: "Deutsch", value: "de" },
|
||||
{ label: "English", value: "en" },
|
||||
@@ -39,6 +42,7 @@ export const APP_LANGUAGES = [
|
||||
{ label: "日本語", value: "ja" },
|
||||
{ label: "Klingon", value: "tlh" },
|
||||
{ label: "Türkçe", value: "tr" },
|
||||
{ label: "Magyar", value: "hu" },
|
||||
{ label: "Nederlands", value: "nl" },
|
||||
{ label: "Polski", value: "pl" },
|
||||
{ label: "Português (Brasil)", value: "pt-BR" },
|
||||
@@ -59,12 +63,14 @@ i18n.use(initReactI18next).init({
|
||||
compatibilityJSON: "v4",
|
||||
resources: {
|
||||
ca: { translation: ca },
|
||||
ar: { translation: ar },
|
||||
da: { translation: da },
|
||||
de: { translation: de },
|
||||
en: { translation: en },
|
||||
es: { translation: es },
|
||||
eo: { translation: eo },
|
||||
fr: { translation: fr },
|
||||
hu: { translation: hu },
|
||||
it: { translation: it },
|
||||
ja: { translation: ja },
|
||||
nl: { translation: nl },
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
# login.yaml
|
||||
|
||||
appId: your.app.id
|
||||
---
|
||||
- launchApp
|
||||
- tapOn: "Text on the screen"
|
||||
237
metro.config.js
237
metro.config.js
@@ -1,28 +1,243 @@
|
||||
// Learn more https://docs.expo.io/guides/customizing-metro
|
||||
const { getDefaultConfig } = require("expo/metro-config");
|
||||
const path = require("node:path");
|
||||
|
||||
/** @type {import('expo/metro-config').MetroConfig} */
|
||||
const config = getDefaultConfig(__dirname); // eslint-disable-line no-undef
|
||||
const config = getDefaultConfig(__dirname);
|
||||
|
||||
// Add Hermes parser
|
||||
// =======================================================
|
||||
// STREAMYFIN METRO CONFIG - PERFORMANCE OPTIMIZED 🚀
|
||||
// =======================================================
|
||||
// Advanced configuration for multi-platform Jellyfin client
|
||||
// Optimized for media streaming, TV support, and Bun
|
||||
// =======================================================
|
||||
|
||||
// HERMES + ADVANCED PERFORMANCE
|
||||
// ==============================
|
||||
config.transformer.hermesParser = true;
|
||||
|
||||
// When enabled, the optional code below will allow Metro to resolve
|
||||
// and bundle source files with TV-specific extensions
|
||||
// (e.g., *.ios.tv.tsx, *.android.tv.tsx, *.tv.tsx)
|
||||
//
|
||||
// Metro will still resolve source files with standard extensions
|
||||
// as usual if TV-specific files are not found for a module.
|
||||
//
|
||||
// CPU optimization (your existing setting)
|
||||
const os = require("node:os");
|
||||
config.maxWorkers = Math.max(1, os.cpus().length - 1);
|
||||
|
||||
// JAVASCRIPT OPTIMIZATION (Safe & Stable)
|
||||
// ========================================
|
||||
config.transformer = {
|
||||
...config.transformer,
|
||||
hermesParser: true,
|
||||
|
||||
// NEW: Inline requires for 15-30% startup improvement
|
||||
inlineRequires: true,
|
||||
|
||||
// ADVANCED: Hermes-optimized minification for streaming apps
|
||||
minifierConfig: {
|
||||
mangle: {
|
||||
keep_fnames: process.env.NODE_ENV === "development",
|
||||
},
|
||||
output: {
|
||||
ascii_only: true,
|
||||
beautify: false,
|
||||
semicolons: false,
|
||||
},
|
||||
compress: {
|
||||
// Production-only optimizations
|
||||
drop_console: process.env.NODE_ENV === "production",
|
||||
dead_code: true,
|
||||
drop_debugger: true,
|
||||
conditionals: true,
|
||||
evaluate: true,
|
||||
unused: true,
|
||||
reduce_vars: true,
|
||||
// Keep class names for error reporting
|
||||
keep_classnames: true,
|
||||
// Preserve function names for performance profiling in dev
|
||||
keep_fnames: process.env.NODE_ENV === "development",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// RESOLVER OPTIMIZATIONS ENHANCED
|
||||
// ===============================
|
||||
config.resolver = {
|
||||
...config.resolver,
|
||||
|
||||
// NEW: Package exports (stable in recent Metro)
|
||||
unstable_enablePackageExports: true,
|
||||
|
||||
// ENHANCED: Extensions optimized for Streamyfin
|
||||
assetExts: [
|
||||
...config.resolver.assetExts,
|
||||
// Video formats (enhanced for Jellyfin)
|
||||
"mkv",
|
||||
"mp4",
|
||||
"avi",
|
||||
"mov",
|
||||
"wmv",
|
||||
"flv",
|
||||
"webm",
|
||||
"m4v",
|
||||
"mpg",
|
||||
"mpeg",
|
||||
// Audio formats
|
||||
"mp3",
|
||||
"wav",
|
||||
"flac",
|
||||
"aac",
|
||||
"m4a",
|
||||
"ogg",
|
||||
"wma",
|
||||
"opus",
|
||||
// Subtitle files (complete for media)
|
||||
"srt",
|
||||
"vtt",
|
||||
"ass",
|
||||
"ssa",
|
||||
"sub",
|
||||
"idx",
|
||||
"sbv",
|
||||
"ttml",
|
||||
// Database/Cache
|
||||
"db",
|
||||
"sqlite",
|
||||
"realm",
|
||||
"json5",
|
||||
// Fonts (TV optimized)
|
||||
"woff2",
|
||||
"woff",
|
||||
"eot",
|
||||
"otf",
|
||||
// Images (enhanced for thumbnails)
|
||||
"avif",
|
||||
"heic",
|
||||
"heif", // Modern formats
|
||||
],
|
||||
|
||||
sourceExts: [
|
||||
...config.resolver.sourceExts,
|
||||
"mjs",
|
||||
"cjs", // Modern JS support
|
||||
],
|
||||
|
||||
// NEW: Platform prioritization for performance
|
||||
platforms: ["ios", "android", "native", "web", "tv"],
|
||||
};
|
||||
|
||||
// SERIALIZER OPTIMIZATIONS (Production)
|
||||
// ====================================
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
config.serializer = {
|
||||
...config.serializer,
|
||||
|
||||
// NEW: Module IDs optimized for caching
|
||||
createModuleIdFactory: () => {
|
||||
return (path) => {
|
||||
// Shorter module IDs for smaller bundles
|
||||
return require("node:crypto")
|
||||
.createHash("sha1")
|
||||
.update(path)
|
||||
.digest("hex")
|
||||
.substring(0, 8);
|
||||
};
|
||||
},
|
||||
|
||||
// NEW: Bundle pre-loading for streaming apps
|
||||
getModulesRunBeforeMainModule: (_entryFilePath) => [
|
||||
// Pre-load critical modules for faster TTI
|
||||
require.resolve("react-native/Libraries/Core/InitializeCore"),
|
||||
],
|
||||
|
||||
// Web bundle splitting (if applicable)
|
||||
...(process.env.EXPO_PLATFORM === "web" && {
|
||||
// Experimental code splitting for web
|
||||
experimentalSerializerHook: () => {},
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
// TV PLATFORM ENHANCEMENTS
|
||||
// ========================
|
||||
if (process.env?.EXPO_TV === "1") {
|
||||
const originalSourceExts = config.resolver.sourceExts;
|
||||
const tvSourceExts = [
|
||||
...originalSourceExts.map((e) => `tv.${e}`),
|
||||
...originalSourceExts.map((ext) => `tv.${ext}`),
|
||||
...originalSourceExts,
|
||||
];
|
||||
config.resolver.sourceExts = tvSourceExts;
|
||||
|
||||
// NEW: TV-specific optimizations
|
||||
config.transformer = {
|
||||
...config.transformer,
|
||||
// Optimize transforms for TV hardware
|
||||
experimentalImportSupport: false, // Reduce complexity on TV
|
||||
// Use legacy transformer for better TV compatibility
|
||||
allowOptionalDependencies: true,
|
||||
};
|
||||
|
||||
console.log("📺 TV platform optimized for Streamyfin");
|
||||
console.log(`📁 TV extensions: ${tvSourceExts.slice(0, 6).join(", ")}...`);
|
||||
}
|
||||
|
||||
// config.resolver.unstable_enablePackageExports = false;
|
||||
// DEVELOPMENT ENHANCEMENTS
|
||||
// ========================
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
// NEW: Enhanced error reporting
|
||||
config.reporter = {
|
||||
update: (event) => {
|
||||
if (event.type === "bundle_build_failed") {
|
||||
console.error(
|
||||
"🔴 Streamyfin Bundle Build Failed:",
|
||||
event.error.message,
|
||||
);
|
||||
} else if (event.type === "bundle_build_done") {
|
||||
console.log(
|
||||
"✅ Streamyfin Bundle Ready:",
|
||||
event.bundleDetails?.bundleSize || "Unknown size",
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
console.log("🚀 Streamyfin Metro Config - OPTIMIZED VERSION");
|
||||
console.log(`📦 Workers: ${config.maxWorkers}`);
|
||||
console.log(`🎯 Hermes: ${config.transformer.hermesParser}`);
|
||||
console.log(`⚡ Inline requires: ${config.transformer.inlineRequires}`);
|
||||
console.log(
|
||||
`📺 TV support: ${process.env.EXPO_TV === "1" ? "ENABLED" : "DISABLED"}`,
|
||||
);
|
||||
}
|
||||
|
||||
// STREAMING APP SPECIFIC OPTIMIZATIONS
|
||||
// ===================================
|
||||
|
||||
// NEW: Cache hints for better performance
|
||||
if (typeof config.cacheStores === "undefined") {
|
||||
// Only add if not causing issues
|
||||
try {
|
||||
const MetroCache = require("metro-cache");
|
||||
config.cacheStores = [
|
||||
new MetroCache.FileStore({
|
||||
root: path.join(os.tmpdir(), "streamyfin-metro-cache"),
|
||||
}),
|
||||
];
|
||||
} catch (_e) {
|
||||
// Fallback: use default cache
|
||||
console.log("ℹ️ Using default Metro cache (custom cache not available)");
|
||||
}
|
||||
}
|
||||
|
||||
// NEW: Network request optimizations for streaming
|
||||
config.server = {
|
||||
...config.server,
|
||||
// Enhanced request handling for media assets
|
||||
enhanceMiddleware: (middleware, _server) => {
|
||||
// Add caching headers for static assets
|
||||
return (req, res, next) => {
|
||||
if (req.url?.match(/\.(mp4|mkv|jpg|jpeg|png|webp)$/)) {
|
||||
res.setHeader("Cache-Control", "public, max-age=31536000");
|
||||
}
|
||||
return middleware(req, res, next);
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {
|
||||
import type {
|
||||
ChapterInfo,
|
||||
PlaybackStatePayload,
|
||||
ProgressUpdatePayload,
|
||||
@@ -12,16 +12,20 @@ import {
|
||||
} from "./VlcPlayer.types";
|
||||
import VlcPlayerView from "./VlcPlayerView";
|
||||
|
||||
export {
|
||||
VlcPlayerView,
|
||||
VlcPlayerViewProps,
|
||||
VlcPlayerViewRef,
|
||||
// Component
|
||||
export { VlcPlayerView };
|
||||
|
||||
// Component Types
|
||||
export type { VlcPlayerViewProps, VlcPlayerViewRef };
|
||||
|
||||
// Media Types
|
||||
export type { ChapterInfo, TrackInfo, VlcPlayerSource };
|
||||
|
||||
// Playback Events (alphabetically sorted)
|
||||
export type {
|
||||
PlaybackStatePayload,
|
||||
ProgressUpdatePayload,
|
||||
VideoLoadStartPayload,
|
||||
VideoStateChangePayload,
|
||||
VideoProgressPayload,
|
||||
VlcPlayerSource,
|
||||
TrackInfo,
|
||||
ChapterInfo,
|
||||
VideoStateChangePayload,
|
||||
};
|
||||
|
||||
29
package.json
29
package.json
@@ -39,12 +39,12 @@
|
||||
"expo": "^53.0.23",
|
||||
"expo-application": "~6.1.4",
|
||||
"expo-asset": "~11.1.7",
|
||||
"expo-atlas": "^0.4.0",
|
||||
"expo-background-task": "~0.2.8",
|
||||
"expo-blur": "~14.1.4",
|
||||
"expo-brightness": "~13.1.4",
|
||||
"expo-build-properties": "~0.14.6",
|
||||
"expo-constants": "~17.1.5",
|
||||
"expo-dev-client": "^5.2.0",
|
||||
"expo-device": "~7.1.4",
|
||||
"expo-font": "~13.3.1",
|
||||
"expo-haptics": "~14.1.4",
|
||||
@@ -101,21 +101,21 @@
|
||||
"zod": "^4.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.20.0",
|
||||
"@biomejs/biome": "^2.2.4",
|
||||
"@react-native-community/cli": "^20.0.0",
|
||||
"@react-native-tvos/config-tv": "^0.1.1",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/lodash": "^4.17.15",
|
||||
"@babel/core": "7.28.4",
|
||||
"@biomejs/biome": "2.2.5",
|
||||
"@react-native-community/cli": "20.0.2",
|
||||
"@react-native-tvos/config-tv": "0.1.4",
|
||||
"@types/jest": "30.0.0",
|
||||
"@types/lodash": "4.17.20",
|
||||
"@types/react": "~19.0.10",
|
||||
"@types/react-test-renderer": "^19.0.0",
|
||||
"expo-doctor": "^1.17.0",
|
||||
"cross-env": "^10.0.0",
|
||||
"husky": "^9.1.7",
|
||||
"lint-staged": "^16.1.5",
|
||||
"postinstall-postinstall": "^2.1.0",
|
||||
"@types/react-test-renderer": "19.1.0",
|
||||
"cross-env": "10.1.0",
|
||||
"expo-dev-client": "5.2.4",
|
||||
"expo-doctor": "1.17.9",
|
||||
"husky": "9.1.7",
|
||||
"lint-staged": "16.2.3",
|
||||
"react-test-renderer": "19.1.1",
|
||||
"typescript": "~5.8.3"
|
||||
"typescript": "5.8.3"
|
||||
},
|
||||
"expo": {
|
||||
"install": {
|
||||
@@ -149,7 +149,6 @@
|
||||
]
|
||||
},
|
||||
"trustedDependencies": [
|
||||
"postinstall-postinstall",
|
||||
"unrs-resolver"
|
||||
]
|
||||
}
|
||||
|
||||
500
translations/ar.json
Normal file
500
translations/ar.json
Normal file
@@ -0,0 +1,500 @@
|
||||
{
|
||||
"login": {
|
||||
"username_required": "اسم المستخدم مطلوب",
|
||||
"error_title": "خطأ",
|
||||
"login_title": "تسجيل الدخول",
|
||||
"login_to_title": "تسجيل الدخول إلى",
|
||||
"username_placeholder": "اسم المستخدم",
|
||||
"password_placeholder": "كلمة المرور",
|
||||
"login_button": "تسجيل الدخول",
|
||||
"quick_connect": "اتصال سريع",
|
||||
"enter_code_to_login": "أدخل الرمز {{code}} لتسجيل الدخول",
|
||||
"failed_to_initiate_quick_connect": "فشل في بدء الاتصال السريع",
|
||||
"got_it": "حسنًا",
|
||||
"connection_failed": "فشل الاتصال",
|
||||
"could_not_connect_to_server": "تعذر الاتصال بالخادم. يرجى التحقق من الرابط واتصال الشبكة.",
|
||||
"an_unexpected_error_occured": "حدث خطأ غير متوقع",
|
||||
"change_server": "تغيير الخادم",
|
||||
"invalid_username_or_password": "اسم المستخدم أو كلمة المرور غير صالحة",
|
||||
"user_does_not_have_permission_to_log_in": "ليس لدى المستخدم صلاحية تسجيل الدخول",
|
||||
"server_is_taking_too_long_to_respond_try_again_later": "يستغرق الخادم وقتًا طويلاً للرد، يرجى المحاولة مرة أخرى لاحقًا",
|
||||
"server_received_too_many_requests_try_again_later": "تلقى الخادم عددًا كبيرًا جدًا من الطلبات، يرجى المحاولة مرة أخرى لاحقًا.",
|
||||
"there_is_a_server_error": "هناك خطأ في الخادم",
|
||||
"an_unexpected_error_occured_did_you_enter_the_correct_url": "حدث خطأ غير متوقع. هل أدخلت رابط الخادم بشكل صحيح؟",
|
||||
"too_old_server_text": "تم اكتشاف خادم jellyfin غير مدعوم",
|
||||
"too_old_server_description": "يرجى تحديث jellyfin إلى أحدث إصدار"
|
||||
},
|
||||
"server": {
|
||||
"enter_url_to_jellyfin_server": "أدخل رابط خادم Jellyfin الخاص بك",
|
||||
"server_url_placeholder": "http(s)://your-server.com",
|
||||
"connect_button": "اتصال",
|
||||
"previous_servers": "الخوادم السابقة",
|
||||
"clear_button": "مسح",
|
||||
"search_for_local_servers": "البحث عن الخوادم المحلية",
|
||||
"searching": "يبحث...",
|
||||
"servers": "الخوادم"
|
||||
},
|
||||
"home": {
|
||||
"no_internet": "لا يوجد اتصال بالإنترنت",
|
||||
"no_items": "لا توجد عناصر",
|
||||
"no_internet_message": "لا تقلق، لا يزال بإمكانك مشاهدة المحتوى الذي تم تنزيله.",
|
||||
"go_to_downloads": "الذهاب إلى التنزيلات",
|
||||
"oops": "عفوًا!",
|
||||
"error_message": "حدث خطأ ما.\nيرجى تسجيل الخروج ثم الدخول مرة أخرى.",
|
||||
"continue_watching": "متابعة المشاهدة",
|
||||
"next_up": "التالي",
|
||||
"recently_added_in": "أضيف مؤخراً في {{libraryName}}",
|
||||
"suggested_movies": "أفلام مقترحة",
|
||||
"suggested_episodes": "حلقات مقترحة",
|
||||
"intro": {
|
||||
"welcome_to_streamyfin": "مرحبًا بك في Streamyfin",
|
||||
"a_free_and_open_source_client_for_jellyfin": "عميل مجاني ومفتوح المصدر لـ Jellyfin.",
|
||||
"features_title": "الميزات",
|
||||
"features_description": "يحتوي Streamyfin على مجموعة من الميزات ويتكامل مع مجموعة واسعة من البرامج التي يمكنك العثور عليها في قائمة الإعدادات، وتشمل:",
|
||||
"jellyseerr_feature_description": "اتصل بمثيل Jellyseerr الخاص بك واطلب الأفلام مباشرة في التطبيق.",
|
||||
"downloads_feature_title": "التنزيلات",
|
||||
"downloads_feature_description": "قم بتنزيل الأفلام والمسلسلات التلفزيونية لمشاهدتها في وضع عدم الاتصال. استخدم إما الطريقة الافتراضية أو قم بتثبيت الخادم المحسن لتنزيل الملفات في الخلفية.",
|
||||
"chromecast_feature_description": "قم ببث الأفلام والبرامج التلفزيونية على أجهزة Chromecast الخاصة بك.",
|
||||
"centralised_settings_plugin_title": "إضافة الإعدادات المركزية",
|
||||
"centralised_settings_plugin_description": "قم بتكوين الإعدادات من موقع مركزي على خادم Jellyfin الخاص بك. ستتم مزامنة جميع إعدادات العميل لجميع المستخدمين تلقائيًا.",
|
||||
"done_button": "تم",
|
||||
"go_to_settings_button": "الذهاب إلى الإعدادات",
|
||||
"read_more": "اقرأ المزيد"
|
||||
},
|
||||
"settings": {
|
||||
"settings_title": "الإعدادات",
|
||||
"log_out_button": "تسجيل الخروج",
|
||||
"user_info": {
|
||||
"user_info_title": "معلومات المستخدم",
|
||||
"user": "المستخدم",
|
||||
"server": "الخادم",
|
||||
"token": "الرمز",
|
||||
"app_version": "إصدار التطبيق"
|
||||
},
|
||||
"quick_connect": {
|
||||
"quick_connect_title": "اتصال سريع",
|
||||
"authorize_button": "تفويض الاتصال السريع",
|
||||
"enter_the_quick_connect_code": "أدخل رمز الاتصال السريع...",
|
||||
"success": "نجاح",
|
||||
"quick_connect_autorized": "تم تفويض الاتصال السريع",
|
||||
"error": "خطأ",
|
||||
"invalid_code": "رمز غير صالح",
|
||||
"authorize": "تفويض"
|
||||
},
|
||||
"media_controls": {
|
||||
"media_controls_title": "عناصر التحكم بالوسائط",
|
||||
"forward_skip_length": "مدة التقديم السريع",
|
||||
"rewind_length": "مدة الترجيع",
|
||||
"seconds_unit": "ث"
|
||||
},
|
||||
"gesture_controls": {
|
||||
"gesture_controls_title": "التحكم بالإيماءات",
|
||||
"horizontal_swipe_skip": "السحب الأفقي للتخطي",
|
||||
"horizontal_swipe_skip_description": "اسحب لليسار/لليمين عندما تكون عناصر التحكم مخفية للتخطي",
|
||||
"left_side_brightness": "التحكم في السطوع من الجانب الأيسر",
|
||||
"left_side_brightness_description": "اسحب لأعلى/لأسفل على الجانب الأيسر لضبط السطوع",
|
||||
"right_side_volume": "التحكم في مستوى الصوت من الجانب الأيمن",
|
||||
"right_side_volume_description": "اسحب لأعلى/لأسفل على الجانب الأيمن لضبط مستوى الصوت"
|
||||
},
|
||||
"audio": {
|
||||
"audio_title": "الصوت",
|
||||
"set_audio_track": "تعيين مسار الصوت من العنصر السابق",
|
||||
"audio_language": "لغة الصوت",
|
||||
"audio_hint": "اختر لغة صوت افتراضية.",
|
||||
"none": "لا شيء",
|
||||
"language": "اللغة"
|
||||
},
|
||||
"subtitles": {
|
||||
"subtitle_title": "الترجمة",
|
||||
"subtitle_language": "لغة الترجمة",
|
||||
"subtitle_mode": "وضع الترجمة",
|
||||
"set_subtitle_track": "تعيين مسار الترجمة من العنصر السابق",
|
||||
"subtitle_size": "حجم الترجمة",
|
||||
"subtitle_hint": "تكوين تفضيلات الترجمة.",
|
||||
"none": "لا شيء",
|
||||
"language": "اللغة",
|
||||
"loading": "جار التحميل",
|
||||
"modes": {
|
||||
"Default": "افتراضي",
|
||||
"Smart": "ذكي",
|
||||
"Always": "دائماً",
|
||||
"None": "لا شيء",
|
||||
"OnlyForced": "فقط الإجبارية"
|
||||
}
|
||||
},
|
||||
"other": {
|
||||
"other_title": "أخرى",
|
||||
"follow_device_orientation": "تدوير تلقائي",
|
||||
"video_orientation": "اتجاه الفيديو",
|
||||
"orientation": "الاتجاه",
|
||||
"orientations": {
|
||||
"DEFAULT": "افتراضي",
|
||||
"ALL": "الكل",
|
||||
"PORTRAIT": "عمودي",
|
||||
"PORTRAIT_UP": "عمودي لأعلى",
|
||||
"PORTRAIT_DOWN": "عمودي لأسفل",
|
||||
"LANDSCAPE": "أفقي",
|
||||
"LANDSCAPE_LEFT": "أفقي لليسار",
|
||||
"LANDSCAPE_RIGHT": "أفقي لليمين",
|
||||
"OTHER": "أخرى",
|
||||
"UNKNOWN": "غير معروف"
|
||||
},
|
||||
"safe_area_in_controls": "مساحة آمنة في عناصر التحكم",
|
||||
"video_player": "مشغل الفيديو",
|
||||
"video_players": {
|
||||
"VLC_3": "VLC 3",
|
||||
"VLC_4": "VLC 4 (تجريبي + صورة داخل صورة)"
|
||||
},
|
||||
"show_custom_menu_links": "إظهار روابط القائمة المخصصة",
|
||||
"hide_libraries": "إخفاء المكتبات",
|
||||
"select_liraries_you_want_to_hide": "حدد المكتبات التي تريد إخفاءها من علامة تبويب المكتبة وأقسام الصفحة الرئيسية.",
|
||||
"disable_haptic_feedback": "تعطيل ردود الفعل اللمسية",
|
||||
"default_quality": "الجودة الافتراضية",
|
||||
"max_auto_play_episode_count": "الحد الأقصى لعدد الحلقات التي يتم تشغيلها تلقائيًا",
|
||||
"disabled": "معطل"
|
||||
},
|
||||
"downloads": {
|
||||
"downloads_title": "التنزيلات",
|
||||
"download_method": "طريقة التنزيل",
|
||||
"remux_max_download": "الحد الأقصى لتنزيل الريمكس",
|
||||
"auto_download": "تنزيل تلقائي",
|
||||
"optimized_versions_server": "خادم الإصدارات المحسّنة",
|
||||
"save_button": "حفظ",
|
||||
"optimized_server": "الخادم المحسن",
|
||||
"optimized": "محسن",
|
||||
"default": "افتراضي",
|
||||
"optimized_version_hint": "أدخل رابط الخادم المحسن. يجب أن يتضمن الرابط http أو https ويمكن أن يتضمن المنفذ اختياريًا.",
|
||||
"read_more_about_optimized_server": "اقرأ المزيد عن الخادم المحسن.",
|
||||
"url": "الرابط",
|
||||
"server_url_placeholder": "http(s)://domain.org:port"
|
||||
},
|
||||
"plugins": {
|
||||
"plugins_title": "الإضافات",
|
||||
"jellyseerr": {
|
||||
"jellyseerr_warning": "هذا التكامل في مراحله الأولى. توقع أن تتغير الأمور.",
|
||||
"server_url": "رابط الخادم",
|
||||
"server_url_hint": "مثال: http(s)://your-host.url\n(أضف المنفذ إذا لزم الأمر)",
|
||||
"server_url_placeholder": "رابط Jellyseerr...",
|
||||
"password": "كلمة المرور",
|
||||
"password_placeholder": "أدخل كلمة المرور لمستخدم Jellyfin {{username}}",
|
||||
"save_button": "حفظ",
|
||||
"clear_button": "مسح",
|
||||
"login_button": "تسجيل الدخول",
|
||||
"total_media_requests": "إجمالي طلبات الوسائط",
|
||||
"movie_quota_limit": "حد حصة الأفلام",
|
||||
"movie_quota_days": "أيام حصة الأفلام",
|
||||
"tv_quota_limit": "حد حصة المسلسلات",
|
||||
"tv_quota_days": "أيام حصة المسلسلات",
|
||||
"reset_jellyseerr_config_button": "إعادة تعيين تكوين Jellyseerr",
|
||||
"unlimited": "غير محدود",
|
||||
"plus_n_more": "+{{n}} المزيد",
|
||||
"order_by": {
|
||||
"DEFAULT": "افتراضي",
|
||||
"VOTE_COUNT_AND_AVERAGE": "عدد الأصوات والمعدل",
|
||||
"POPULARITY": "الشعبية"
|
||||
}
|
||||
},
|
||||
"marlin_search": {
|
||||
"enable_marlin_search": "تمكين بحث مارلن",
|
||||
"url": "الرابط",
|
||||
"server_url_placeholder": "http(s)://domain.org:port",
|
||||
"marlin_search_hint": "أدخل رابط خادم مارلن. يجب أن يتضمن الرابط http أو https ويمكن أن يتضمن المنفذ اختياريًا.",
|
||||
"read_more_about_marlin": "اقرأ المزيد عن مارلن.",
|
||||
"save_button": "حفظ",
|
||||
"toasts": {
|
||||
"saved": "تم الحفظ"
|
||||
}
|
||||
}
|
||||
},
|
||||
"storage": {
|
||||
"storage_title": "التخزين",
|
||||
"app_usage": "التطبيق {{usedSpace}}%",
|
||||
"device_usage": "الجهاز {{availableSpace}}%",
|
||||
"size_used": "تم استخدام {{used}} من {{total}}",
|
||||
"delete_all_downloaded_files": "حذف جميع الملفات التي تم تنزيلها"
|
||||
},
|
||||
"intro": {
|
||||
"show_intro": "إظهار المقدمة",
|
||||
"reset_intro": "إعادة تعيين المقدمة"
|
||||
},
|
||||
"logs": {
|
||||
"logs_title": "السجلات",
|
||||
"export_logs": "تصدير السجلات",
|
||||
"click_for_more_info": "انقر لمزيد من المعلومات",
|
||||
"level": "المستوى",
|
||||
"no_logs_available": "لا توجد سجلات متاحة",
|
||||
"delete_all_logs": "حذف جميع السجلات"
|
||||
},
|
||||
"languages": {
|
||||
"title": "اللغات",
|
||||
"app_language": "لغة التطبيق",
|
||||
"app_language_description": "حدد لغة التطبيق.",
|
||||
"system": "النظام"
|
||||
},
|
||||
"toasts": {
|
||||
"error_deleting_files": "خطأ في حذف الملفات",
|
||||
"background_downloads_enabled": "تمكين التنزيلات في الخلفية",
|
||||
"background_downloads_disabled": "تعطيل التنزيلات في الخلفية",
|
||||
"connected": "متصل",
|
||||
"could_not_connect": "تعذر الاتصال",
|
||||
"invalid_url": "رابط غير صالح"
|
||||
}
|
||||
},
|
||||
"sessions": {
|
||||
"title": "الجلسات",
|
||||
"no_active_sessions": "لا توجد جلسات نشطة"
|
||||
},
|
||||
"downloads": {
|
||||
"downloads_title": "التنزيلات",
|
||||
"tvseries": "مسلسلات",
|
||||
"movies": "أفلام",
|
||||
"queue": "قائمة الانتظار",
|
||||
"queue_hint": "ستفقد قائمة الانتظار والتنزيلات عند إعادة تشغيل التطبيق",
|
||||
"no_items_in_queue": "لا توجد عناصر في قائمة الانتظار",
|
||||
"no_downloaded_items": "لا توجد عناصر تم تنزيلها",
|
||||
"delete_all_movies_button": "حذف جميع الأفلام",
|
||||
"delete_all_tvseries_button": "حذف جميع المسلسلات",
|
||||
"delete_all_button": "حذف الكل",
|
||||
"active_download": "تنزيل نشط",
|
||||
"no_active_downloads": "لا توجد تنزيلات نشطة",
|
||||
"active_downloads": "تنزيلات نشطة",
|
||||
"new_app_version_requires_re_download": "يتطلب إصدار التطبيق الجديد إعادة التنزيل",
|
||||
"new_app_version_requires_re_download_description": "يتطلب التحديث الجديد تنزيل المحتوى مرة أخرى. يرجى إزالة كل المحتوى الذي تم تنزيله والمحاولة مرة أخرى.",
|
||||
"back": "رجوع",
|
||||
"delete": "حذف",
|
||||
"something_went_wrong": "حدث خطأ ما",
|
||||
"could_not_get_stream_url_from_jellyfin": "تعذر الحصول على رابط البث من Jellyfin",
|
||||
"eta": "الوقت المتبقي {{eta}}",
|
||||
"methods": "الطرق",
|
||||
"toasts": {
|
||||
"you_are_not_allowed_to_download_files": "غير مسموح لك بتنزيل الملفات.",
|
||||
"deleted_all_movies_successfully": "تم حذف جميع الأفلام بنجاح!",
|
||||
"failed_to_delete_all_movies": "فشل حذف جميع الأفلام",
|
||||
"deleted_all_tvseries_successfully": "تم حذف جميع المسلسلات بنجاح!",
|
||||
"failed_to_delete_all_tvseries": "فشل حذف جميع المسلسلات",
|
||||
"download_deleted": "تم حذف التنزيل",
|
||||
"could_not_delete_download": "تعذر حذف التنزيل",
|
||||
"download_paused": "تم إيقاف التنزيل مؤقتًا",
|
||||
"could_not_pause_download": "تعذر إيقاف التنزيل مؤقتًا",
|
||||
"download_resumed": "تم استئناف التنزيل",
|
||||
"could_not_resume_download": "تعذر استئناف التنزيل",
|
||||
"download_completed": "اكتمل التنزيل",
|
||||
"download_started_for": "بدأ تنزيل {{item}}",
|
||||
"item_is_ready_to_be_downloaded": "{{item}} جاهز للتنزيل",
|
||||
"download_stated_for_item": "بدأ تنزيل {{item}}",
|
||||
"download_failed_for_item": "فشل تنزيل {{item}} - {{error}}",
|
||||
"download_completed_for_item": "اكتمل تنزيل {{item}}",
|
||||
"queued_item_for_optimization": "تمت إضافة {{item}} إلى قائمة الانتظار للتحسين",
|
||||
"failed_to_start_download_for_item": "فشل بدء تنزيل {{item}}: {{message}}",
|
||||
"server_responded_with_status_code": "استجاب الخادم بالحالة {{statusCode}}",
|
||||
"no_response_received_from_server": "لم يتم تلقي أي رد من الخادم",
|
||||
"error_setting_up_the_request": "خطأ في إعداد الطلب",
|
||||
"failed_to_start_download_for_item_unexpected_error": "فشل بدء تنزيل {{item}}: خطأ غير متوقع",
|
||||
"all_files_folders_and_jobs_deleted_successfully": "تم حذف جميع الملفات والمجلدات والمهام بنجاح",
|
||||
"an_error_occured_while_deleting_files_and_jobs": "حدث خطأ أثناء حذف الملفات والمهام",
|
||||
"go_to_downloads": "الذهاب إلى التنزيلات"
|
||||
}
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"search_here": "ابحث هنا...",
|
||||
"search": "بحث...",
|
||||
"x_items": "{{count}} عناصر",
|
||||
"library": "المكتبة",
|
||||
"discover": "اكتشف",
|
||||
"no_results": "لا توجد نتائج",
|
||||
"no_results_found_for": "لم يتم العثور على نتائج لـ",
|
||||
"movies": "أفلام",
|
||||
"series": "مسلسلات",
|
||||
"episodes": "حلقات",
|
||||
"collections": "مجموعات",
|
||||
"actors": "ممثلون",
|
||||
"request_movies": "طلب أفلام",
|
||||
"request_series": "طلب مسلسلات",
|
||||
"recently_added": "أضيف مؤخراً",
|
||||
"recent_requests": "الطلبات الأخيرة",
|
||||
"plex_watchlist": "قائمة مشاهدة Plex",
|
||||
"trending": "شائع",
|
||||
"popular_movies": "أفلام شائعة",
|
||||
"movie_genres": "أنواع الأفلام",
|
||||
"upcoming_movies": "أفلام قادمة",
|
||||
"studios": "استوديوهات",
|
||||
"popular_tv": "مسلسلات شائعة",
|
||||
"tv_genres": "أنواع المسلسلات",
|
||||
"upcoming_tv": "مسلسلات قادمة",
|
||||
"networks": "شبكات",
|
||||
"tmdb_movie_keyword": "كلمة مفتاحية لفيلم TMDB",
|
||||
"tmdb_movie_genre": "نوع فيلم TMDB",
|
||||
"tmdb_tv_keyword": "كلمة مفتاحية لمسلسل TMDB",
|
||||
"tmdb_tv_genre": "نوع مسلسل TMDB",
|
||||
"tmdb_search": "بحث TMDB",
|
||||
"tmdb_studio": "استوديو TMDB",
|
||||
"tmdb_network": "شبكة TMDB",
|
||||
"tmdb_movie_streaming_services": "خدمات بث الأفلام TMDB",
|
||||
"tmdb_tv_streaming_services": "خدمات بث المسلسلات TMDB"
|
||||
},
|
||||
"library": {
|
||||
"no_items_found": "لم يتم العثور على عناصر",
|
||||
"no_results": "لا توجد نتائج",
|
||||
"no_libraries_found": "لم يتم العثور على مكتبات",
|
||||
"item_types": {
|
||||
"movies": "أفلام",
|
||||
"series": "مسلسلات",
|
||||
"boxsets": "مجموعات",
|
||||
"items": "عناصر"
|
||||
},
|
||||
"options": {
|
||||
"display": "عرض",
|
||||
"row": "صف",
|
||||
"list": "قائمة",
|
||||
"image_style": "نمط الصورة",
|
||||
"poster": "ملصق",
|
||||
"cover": "غلاف",
|
||||
"show_titles": "إظهار العناوين",
|
||||
"show_stats": "إظهار الإحصائيات"
|
||||
},
|
||||
"filters": {
|
||||
"genres": "الأنواع",
|
||||
"years": "السنوات",
|
||||
"sort_by": "ترتيب حسب",
|
||||
"sort_order": "ترتيب",
|
||||
"asc": "تصاعدي",
|
||||
"desc": "تنازلي",
|
||||
"tags": "الوسوم"
|
||||
}
|
||||
},
|
||||
"favorites": {
|
||||
"series": "مسلسلات",
|
||||
"movies": "أفلام",
|
||||
"episodes": "حلقات",
|
||||
"videos": "فيديوهات",
|
||||
"boxsets": "مجموعات",
|
||||
"playlists": "قوائم التشغيل",
|
||||
"noDataTitle": "لا توجد مفضلات بعد",
|
||||
"noData": "ضع علامة على العناصر كمفضلة لتظهر هنا للوصول السريع."
|
||||
},
|
||||
"custom_links": {
|
||||
"no_links": "لا توجد روابط"
|
||||
},
|
||||
"player": {
|
||||
"error": "خطأ",
|
||||
"failed_to_get_stream_url": "فشل في الحصول على رابط البث",
|
||||
"an_error_occured_while_playing_the_video": "حدث خطأ أثناء تشغيل الفيديو. تحقق من السجلات في الإعدادات.",
|
||||
"client_error": "خطأ في العميل",
|
||||
"could_not_create_stream_for_chromecast": "تعذر إنشاء بث لـ Chromecast",
|
||||
"message_from_server": "رسالة من الخادم: {{message}}",
|
||||
"video_has_finished_playing": "انتهى تشغيل الفيديو!",
|
||||
"no_video_source": "لا يوجد مصدر فيديو...",
|
||||
"next_episode": "الحلقة التالية",
|
||||
"refresh_tracks": "تحديث المسارات",
|
||||
"subtitle_tracks": "مسارات الترجمة:",
|
||||
"audio_tracks": "مسارات الصوت:",
|
||||
"playback_state": "حالة التشغيل:",
|
||||
"no_data_available": "لا توجد بيانات متاحة",
|
||||
"index": "الفهرس:",
|
||||
"continue_watching": "متابعة المشاهدة",
|
||||
"go_back": "رجوع"
|
||||
},
|
||||
"item_card": {
|
||||
"next_up": "التالي",
|
||||
"no_items_to_display": "لا توجد عناصر لعرضها",
|
||||
"cast_and_crew": "طاقم العمل",
|
||||
"series": "مسلسلات",
|
||||
"seasons": "مواسم",
|
||||
"season": "موسم",
|
||||
"no_episodes_for_this_season": "لا توجد حلقات لهذا الموسم",
|
||||
"overview": "نظرة عامة",
|
||||
"more_with": "المزيد مع {{name}}",
|
||||
"similar_items": "عناصر مشابهة",
|
||||
"no_similar_items_found": "لم يتم العثور على عناصر مشابهة",
|
||||
"video": "فيديو",
|
||||
"more_details": "المزيد من التفاصيل",
|
||||
"quality": "الجودة",
|
||||
"audio": "الصوت",
|
||||
"subtitles": "الترجمة",
|
||||
"show_more": "عرض المزيد",
|
||||
"show_less": "عرض أقل",
|
||||
"appeared_in": "ظهر في",
|
||||
"could_not_load_item": "تعذر تحميل العنصر",
|
||||
"none": "لا شيء",
|
||||
"download": {
|
||||
"download_season": "تنزيل الموسم",
|
||||
"download_series": "تنزيل المسلسل",
|
||||
"download_episode": "تنزيل الحلقة",
|
||||
"download_movie": "تنزيل الفيلم",
|
||||
"download_x_item": "تنزيل {{item_count}} عناصر",
|
||||
"download_unwatched_only": "فقط غير المشاهدة",
|
||||
"download_button": "تنزيل",
|
||||
"using_optimized_server": "استخدام الخادم المحسن",
|
||||
"using_default_method": "استخدام الطريقة الافتراضية"
|
||||
}
|
||||
},
|
||||
"live_tv": {
|
||||
"next": "التالي",
|
||||
"previous": "السابق",
|
||||
"live_tv": "بث مباشر",
|
||||
"coming_soon": "قريباً",
|
||||
"on_now": "يعرض الآن",
|
||||
"shows": "برامج",
|
||||
"movies": "أفلام",
|
||||
"sports": "رياضة",
|
||||
"for_kids": "للأطفال",
|
||||
"news": "أخبار"
|
||||
},
|
||||
"jellyseerr": {
|
||||
"confirm": "تأكيد",
|
||||
"cancel": "إلغاء",
|
||||
"yes": "نعم",
|
||||
"whats_wrong": "ما المشكلة؟",
|
||||
"issue_type": "نوع المشكلة",
|
||||
"select_an_issue": "حدد مشكلة",
|
||||
"types": "الأنواع",
|
||||
"describe_the_issue": "(اختياري) صف المشكلة...",
|
||||
"submit_button": "إرسال",
|
||||
"report_issue_button": "الإبلاغ عن مشكلة",
|
||||
"request_button": "طلب",
|
||||
"are_you_sure_you_want_to_request_all_seasons": "هل أنت متأكد أنك تريد طلب جميع المواسم؟",
|
||||
"failed_to_login": "فشل تسجيل الدخول",
|
||||
"cast": "طاقم العمل",
|
||||
"details": "التفاصيل",
|
||||
"status": "الحالة",
|
||||
"original_title": "العنوان الأصلي",
|
||||
"series_type": "نوع المسلسل",
|
||||
"release_dates": "تواريخ الإصدار",
|
||||
"first_air_date": "تاريخ أول عرض",
|
||||
"next_air_date": "تاريخ العرض التالي",
|
||||
"revenue": "الإيرادات",
|
||||
"budget": "الميزانية",
|
||||
"original_language": "اللغة الأصلية",
|
||||
"production_country": "بلد الإنتاج",
|
||||
"studios": "استوديوهات",
|
||||
"network": "شبكة",
|
||||
"currently_streaming_on": "يتم بثه حاليًا على",
|
||||
"advanced": "متقدم",
|
||||
"request_as": "طلب باسم",
|
||||
"tags": "الوسوم",
|
||||
"quality_profile": "ملف تعريف الجودة",
|
||||
"root_folder": "المجلد الجذر",
|
||||
"season_all": "الموسم (الكل)",
|
||||
"season_number": "الموسم {{season_number}}",
|
||||
"number_episodes": "{{episode_number}} حلقات",
|
||||
"born": "مواليد",
|
||||
"appearances": "المشاركات",
|
||||
"toasts": {
|
||||
"jellyseer_does_not_meet_requirements": "خادم Jellyseerr لا يفي بالحد الأدنى من متطلبات الإصدار! يرجى التحديث إلى 2.0.0 على الأقل",
|
||||
"jellyseerr_test_failed": "فشل اختبار Jellyseerr. يرجى المحاولة مرة أخرى.",
|
||||
"failed_to_test_jellyseerr_server_url": "فشل اختبار رابط خادم jellyseerr",
|
||||
"issue_submitted": "تم إرسال المشكلة!",
|
||||
"requested_item": "تم طلب {{item}}!",
|
||||
"you_dont_have_permission_to_request": "ليس لديك إذن للطلب!",
|
||||
"something_went_wrong_requesting_media": "حدث خطأ ما أثناء طلب الوسائط!"
|
||||
}
|
||||
},
|
||||
"tabs": {
|
||||
"home": "الرئيسية",
|
||||
"search": "بحث",
|
||||
"library": "المكتبة",
|
||||
"custom_links": "روابط مخصصة",
|
||||
"favorites": "المفضلة"
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,7 @@
|
||||
"a_free_and_open_source_client_for_jellyfin": "A Free and Open-Source Client for Jellyfin.",
|
||||
"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:",
|
||||
"jellyseerr_feature_description": "Connect to your Jellyseerr instance and request movies directly in the app.",
|
||||
"jellyseerr_feature_description": "Connect to your Seerr instance and request movies directly in the app.",
|
||||
"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.",
|
||||
"chromecast_feature_description": "Cast movies and tv-shows to your Chromecast devices.",
|
||||
@@ -199,7 +199,7 @@
|
||||
"jellyseerr_warning": "This integration is in its early stages. Expect things to change.",
|
||||
"server_url": "Server URL",
|
||||
"server_url_hint": "Example: http(s)://your-host.url\n(add port if required)",
|
||||
"server_url_placeholder": "Jellyseerr URL...",
|
||||
"server_url_placeholder": "Seerr URL",
|
||||
"password": "Password",
|
||||
"password_placeholder": "Enter password for Jellyfin user {{username}}",
|
||||
"login_button": "Login",
|
||||
@@ -208,7 +208,7 @@
|
||||
"movie_quota_days": "Movie Quota Days",
|
||||
"tv_quota_limit": "TV Quota Limit",
|
||||
"tv_quota_days": "TV Quota Days",
|
||||
"reset_jellyseerr_config_button": "Reset Jellyseerr Config",
|
||||
"reset_jellyseerr_config_button": "Reset Seerr Config",
|
||||
"unlimited": "Unlimited",
|
||||
"plus_n_more": "+{{n}} More",
|
||||
"order_by": {
|
||||
@@ -388,7 +388,7 @@
|
||||
"movies": "Movies",
|
||||
"episodes": "Episodes",
|
||||
"videos": "Videos",
|
||||
"boxsets": "Boxsets",
|
||||
"boxsets": "Box Sets",
|
||||
"playlists": "Playlists",
|
||||
"noDataTitle": "No Favorites Yet",
|
||||
"noData": "Mark items as favorites to see them appear here for quick access."
|
||||
@@ -494,9 +494,9 @@
|
||||
"born": "Born",
|
||||
"appearances": "Appearances",
|
||||
"toasts": {
|
||||
"jellyseer_does_not_meet_requirements": "Jellyseerr server does not meet minimum version requirements! Please update to at least 2.0.0",
|
||||
"jellyseerr_test_failed": "Jellyseerr test failed. Please try again.",
|
||||
"failed_to_test_jellyseerr_server_url": "Failed to test jellyseerr server url",
|
||||
"jellyseer_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.",
|
||||
"failed_to_test_jellyseerr_server_url": "Failed to test Seerr server url",
|
||||
"issue_submitted": "Issue Submitted!",
|
||||
"requested_item": "Requested {{item}}!",
|
||||
"you_dont_have_permission_to_request": "You don't have permission to request!",
|
||||
|
||||
@@ -6,7 +6,17 @@
|
||||
"jsxImportSource": "react",
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"incremental": true,
|
||||
"tsBuildInfoFile": ".tsbuildinfo",
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true
|
||||
},
|
||||
"include": [
|
||||
"app/**/*",
|
||||
|
||||
@@ -93,6 +93,7 @@ export type HomeSection = {
|
||||
items?: HomeSectionItemResolver;
|
||||
nextUp?: HomeSectionNextUpResolver;
|
||||
latest?: HomeSectionLatestResolver;
|
||||
custom?: HomeSectionCustomEndpointResolver;
|
||||
};
|
||||
|
||||
export type HomeSectionItemResolver = {
|
||||
@@ -106,6 +107,13 @@ export type HomeSectionItemResolver = {
|
||||
filters?: Array<ItemFilter>;
|
||||
};
|
||||
|
||||
export type HomeSectionCustomEndpointResolver = {
|
||||
title?: string;
|
||||
endpoint: string;
|
||||
headers?: any;
|
||||
query?: any;
|
||||
};
|
||||
|
||||
export type HomeSectionNextUpResolver = {
|
||||
parentId?: string;
|
||||
limit?: number;
|
||||
|
||||
Reference in New Issue
Block a user