mirror of
https://github.com/anultravioletaurora/Jellify.git
synced 2025-12-16 18:55:44 -06:00
[skip actions]
run prettier
This commit is contained in:
@@ -10,10 +10,13 @@ module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
jest: true,
|
||||
},
|
||||
rules: {
|
||||
'react/react-in-jsx-scope': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-require-imports': 'off',
|
||||
'@typescript-eslint/no-empty-object-type': 'off',
|
||||
'react/prop-types': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'error', // Disallow usage of any
|
||||
'no-mixed-spaces-and-tabs': 'off', // refer https://github.com/prettier/prettier/issues/4199
|
||||
|
||||
1
.prettierignore
Normal file
1
.prettierignore
Normal file
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
169
README.md
169
README.md
@@ -1,56 +1,65 @@
|
||||

|
||||
|
||||
# 🪼 Jellify
|
||||
|
||||
[](https://github.com/anultravioletaurora/Jellify/actions/workflows/publish-beta.yml)
|
||||
|
||||
### 🔗 Quick Links
|
||||
|
||||
[Discord Server](https://discord.gg/yf8fBatktn)
|
||||
|
||||
[TestFlight](https://testflight.apple.com/join/etVSc7ZQ)
|
||||
|
||||
### ℹ️ About
|
||||
|
||||
> **jellify** (verb) - *to make gelatinous* <br>
|
||||
[see also](https://www.merriam-webster.com/dictionary/jellify)
|
||||
> **jellify** (verb) - _to make gelatinous_ <br>
|
||||
> [see also](https://www.merriam-webster.com/dictionary/jellify)
|
||||
|
||||
*Jellify* is a free and open source music player for [Jellyfin](https://jellyfin.org/). Built with [React Native](https://reactnative.dev/), *Jellify* provides a user experience that feels familar to other popular music apps and a has featureset to match
|
||||
_Jellify_ is a free and open source music player for [Jellyfin](https://jellyfin.org/). Built with [React Native](https://reactnative.dev/), _Jellify_ provides a user experience that feels familar to other popular music apps and a has featureset to match
|
||||
|
||||
> *Jellify* requires a connection to a [Jellyfin](https://jellyfin.org/) server to work.
|
||||
> _Jellify_ requires a connection to a [Jellyfin](https://jellyfin.org/) server to work.
|
||||
|
||||
### 🤓 Background
|
||||
I was after a music app for Jellyfin that showcased my music with artwork, had a user interface congruent with what the big guys do, and had the ability to algorithmically curate music (not that you have to use *Jellify* that way). I also wanted to create a music app that could handle my extremely large music libraries (i.e., 100K+ songs) and not get bogged down.
|
||||
|
||||
This app was designed with me and my dad in mind, since I wanted to give him a sleek, one stop shop for live recordings of bands he likes (read: the Grateful Dead). The UI was designed so that he'd find it instantly familiar and useful. CarPlay / Android Auto support was also a must for us, as we both use CarPlay religiously.
|
||||
I was after a music app for Jellyfin that showcased my music with artwork, had a user interface congruent with what the big guys do, and had the ability to algorithmically curate music (not that you have to use _Jellify_ that way). I also wanted to create a music app that could handle my extremely large music libraries (i.e., 100K+ songs) and not get bogged down.
|
||||
|
||||
**TL;DR** Designed to be lightweight and scalable, *Jellify* caters to those who want a mobile Jellyfin music experience similar to what's provided by the big music streaming services.
|
||||
This app was designed with me and my dad in mind, since I wanted to give him a sleek, one stop shop for live recordings of bands he likes (read: the Grateful Dead). The UI was designed so that he'd find it instantly familiar and useful. CarPlay / Android Auto support was also a must for us, as we both use CarPlay religiously.
|
||||
|
||||
**TL;DR** Designed to be lightweight and scalable, _Jellify_ caters to those who want a mobile Jellyfin music experience similar to what's provided by the big music streaming services.
|
||||
|
||||
## 💡 Features
|
||||
|
||||
### ✨ Current
|
||||
- Available via Testflight and Android APK
|
||||
- APKs are associated with each [release](https://github.com/anultravioletaurora/Jellify/releases)
|
||||
- Light and Dark modes
|
||||
- Home screen access to previously played tracks, artists, and your playlists
|
||||
- Quick access to similar artists and items for discovering music in your library
|
||||
- Jellyfin playback reporting and [Last.FM Plugin](https://github.com/jesseward/jellyfin-plugin-lastfm) support
|
||||
- Library of Favorited Music, not too dissimilar to how streaming services handle your 'library'
|
||||
- Full playlist support, including creating, updating, and reordering
|
||||
|
||||
- Available via Testflight and Android APK
|
||||
- APKs are associated with each [release](https://github.com/anultravioletaurora/Jellify/releases)
|
||||
- Light and Dark modes
|
||||
- Home screen access to previously played tracks, artists, and your playlists
|
||||
- Quick access to similar artists and items for discovering music in your library
|
||||
- Jellyfin playback reporting and [Last.FM Plugin](https://github.com/jesseward/jellyfin-plugin-lastfm) support
|
||||
- Library of Favorited Music, not too dissimilar to how streaming services handle your 'library'
|
||||
- Full playlist support, including creating, updating, and reordering
|
||||
|
||||
### 🛠 Roadmap
|
||||
- [Offline Playback](https://github.com/anultravioletaurora/Jellify/issues/10)
|
||||
- [CarPlay / Android Auto Support](https://github.com/anultravioletaurora/Jellify/issues/5)
|
||||
- [Support for Jellyfin Instant Mixes](https://github.com/anultravioletaurora/Jellify/issues/50)
|
||||
- [Shared, Public, and Collaborative Playlists](https://github.com/anultravioletaurora/Jellify/issues/175)
|
||||
- [Web / Desktop support](https://github.com/anultravioletaurora/Jellify/issues/71)
|
||||
- [Watch (Apple Watch / WearOS) Support](https://github.com/anultravioletaurora/Jellify/issues/61)
|
||||
- [TV (Android, Apple, Samsung) Support](https://github.com/anultravioletaurora/Jellify/issues/85)
|
||||
|
||||
- [Offline Playback](https://github.com/anultravioletaurora/Jellify/issues/10)
|
||||
- [CarPlay / Android Auto Support](https://github.com/anultravioletaurora/Jellify/issues/5)
|
||||
- [Support for Jellyfin Instant Mixes](https://github.com/anultravioletaurora/Jellify/issues/50)
|
||||
- [Shared, Public, and Collaborative Playlists](https://github.com/anultravioletaurora/Jellify/issues/175)
|
||||
- [Web / Desktop support](https://github.com/anultravioletaurora/Jellify/issues/71)
|
||||
- [Watch (Apple Watch / WearOS) Support](https://github.com/anultravioletaurora/Jellify/issues/61)
|
||||
- [TV (Android, Apple, Samsung) Support](https://github.com/anultravioletaurora/Jellify/issues/85)
|
||||
|
||||
## 👀 Lemme see!
|
||||
|
||||
### Home
|
||||
|
||||
Home
|
||||
|
||||
<img src="screenshots/home.png" alt="Jellify Home" width="275" height="600">
|
||||
|
||||
### Library
|
||||
|
||||
Library
|
||||
|
||||
<img src="screenshots/library.png" alt="Library" width="275" height="600">
|
||||
@@ -76,7 +85,7 @@ Album Artists
|
||||
<img src="screenshots/album_artists.png" alt="Album Artists" width="275" height="600">
|
||||
|
||||
Track Options
|
||||
|
||||
|
||||
<img src="screenshots/track_options.png" alt="Track Options" width="275" height="600">
|
||||
|
||||
Playlist
|
||||
@@ -84,9 +93,11 @@ Playlist
|
||||
<img src="screenshots/playlist.png" alt="Playlist" width="275" height="600">
|
||||
|
||||
### Search
|
||||
|
||||
<img src="screenshots/search.png" alt="Search" width="275" height="600">
|
||||
|
||||
### Player
|
||||
|
||||
<img src="screenshots/player.png" alt="Player" width="275" height="600">
|
||||
|
||||
<img src="screenshots/player_queue.png" alt="Queue" width="275" height="600">
|
||||
@@ -94,14 +105,19 @@ Playlist
|
||||
<img src='screenshots/favorite_track.png' alt='Favorite Track' width='275' height='600”'>
|
||||
|
||||
### CarPlay (Sneak Preview)
|
||||
|
||||
<img src="screenshots/carplay_nowplaying.jpeg" alt="Now Playing (CarPlay)" width="500" height="350">
|
||||
|
||||
### On the Server
|
||||
|
||||
<img src="https://github.com/user-attachments/assets/741884a2-b9b7-4081-b3a0-6655d08071dc" alt="Playback Tracking" width="300" height="200">
|
||||
|
||||
## 🏗 Built with good stuff
|
||||
|
||||
[](https://reactjs.org “Go to React homepage”) [](https://typescriptlang.org “Go to TypeScript homepage”)
|
||||
|
||||
### 🎨 Frontend
|
||||
|
||||
[Tamagui](https://tamagui.dev/)\
|
||||
[Burnt](https://github.com/nandorojo/burnt)\
|
||||
[React Navigation](https://reactnavigation.org/)\
|
||||
@@ -109,9 +125,11 @@ Playlist
|
||||
[React Native Draggable Flatlist](https://github.com/computerjazz/react-native-draggable-flatlist)\
|
||||
[React Native Reanimated](https://docs.swmansion.com/react-native-reanimated/)\
|
||||
[React Native Vector Icons](https://github.com/oblador/react-native-vector-icons)
|
||||
- Specifically using [Material Community Icons](https://oblador.github.io/react-native-vector-icons/#MaterialCommunityIcons)
|
||||
|
||||
- Specifically using [Material Community Icons](https://oblador.github.io/react-native-vector-icons/#MaterialCommunityIcons)
|
||||
|
||||
### 🎛️ Backend
|
||||
|
||||
[Expo SDK](https://expo.dev/)\
|
||||
[Jellyfin SDK](https://typescript-sdk.jellyfin.org/)\
|
||||
[Tanstack Query](https://tanstack.com/query/latest/docs/framework/react/react-native)\
|
||||
@@ -122,78 +140,95 @@ Playlist
|
||||
[React Native URL Polyfill](https://github.com/charpeni/react-native-url-polyfill)
|
||||
|
||||
### 👩💻 Monitoring
|
||||
|
||||
[GlitchTip](https://glitchtip.com/)
|
||||
|
||||
### 💜 Love from Wisconsin 🧀
|
||||
|
||||
This is undoubtedly a passion project of [mine](https://github.com/anultravioletaurora), and I've learned a lot from working on it (and the many failed attempts before it). I hope you enjoy using it! Feature requests and bug reports are welcome :)
|
||||
|
||||
## 🏃♀️Running Locally
|
||||
|
||||
### ⚛️ Universal Dependencies
|
||||
- [Ruby](https://www.ruby-lang.org/en/documentation/installation/) for Fastlane
|
||||
- [NodeJS v22](https://nodejs.org/en/download) for React Native
|
||||
|
||||
- [Ruby](https://www.ruby-lang.org/en/documentation/installation/) for Fastlane
|
||||
- [NodeJS v22](https://nodejs.org/en/download) for React Native
|
||||
|
||||
### 🍎 iOS
|
||||
|
||||
#### Dependencies
|
||||
- [Xcode](https://developer.apple.com/xcode/) for building
|
||||
|
||||
- [Xcode](https://developer.apple.com/xcode/) for building
|
||||
|
||||
#### Instructions
|
||||
|
||||
##### Setup
|
||||
- Clone this repository
|
||||
- Run `npm run init` to initialize the project
|
||||
- This will install `npm` packages, install `bundler` and required gems, and installs CocoaPods
|
||||
- In the `ios` directory, run `fastlane match development --readonly` to fetch the development signing certificates
|
||||
- *You will need access to the *Jellify Signing* private repository*
|
||||
|
||||
- Clone this repository
|
||||
- Run `npm run init` to initialize the project
|
||||
- This will install `npm` packages, install `bundler` and required gems, and installs CocoaPods
|
||||
- In the `ios` directory, run `fastlane match development --readonly` to fetch the development signing certificates
|
||||
- *You will need access to the *Jellify Signing* private repository*
|
||||
|
||||
##### Running
|
||||
- Run `npm run start` to start the dev server
|
||||
- Open the `Jellify.xcodeworkspace` with Xcode, *not* the `Jellify.xcodeproject`
|
||||
- Run either on a device or in the simulator
|
||||
- *You will need to wait for Xcode to finish it's "Indexing" step*
|
||||
|
||||
- Run `npm run start` to start the dev server
|
||||
- Open the `Jellify.xcodeworkspace` with Xcode, _not_ the `Jellify.xcodeproject`
|
||||
- Run either on a device or in the simulator
|
||||
- _You will need to wait for Xcode to finish it's "Indexing" step_
|
||||
|
||||
##### Building
|
||||
- To create a build, run `npm run fastlane:ios:build` to use fastlane to compile an `.ipa` for you
|
||||
|
||||
- To create a build, run `npm run fastlane:ios:build` to use fastlane to compile an `.ipa` for you
|
||||
|
||||
### 🤖 Android
|
||||
|
||||
#### Dependencies
|
||||
- [Android Studio](https://developer.android.com/studio)
|
||||
- [Java Development Kit](https://www.oracle.com/th/java/technologies/downloads/)
|
||||
|
||||
- [Android Studio](https://developer.android.com/studio)
|
||||
- [Java Development Kit](https://www.oracle.com/th/java/technologies/downloads/)
|
||||
|
||||
#### Instructions
|
||||
|
||||
##### Setup
|
||||
- Clone this repository
|
||||
- Run `npm i` to install `npm` packages
|
||||
|
||||
- Clone this repository
|
||||
- Run `npm i` to install `npm` packages
|
||||
|
||||
##### Running
|
||||
- Run `npm run start` to start the dev server
|
||||
- Open the `android` folder with Android Studio
|
||||
- *Android Studio should automatically grab the "Run Configurations" and initialize Gradle*
|
||||
- Run either on a device or in the simulator
|
||||
|
||||
- Run `npm run start` to start the dev server
|
||||
- Open the `android` folder with Android Studio
|
||||
- _Android Studio should automatically grab the "Run Configurations" and initialize Gradle_
|
||||
- Run either on a device or in the simulator
|
||||
|
||||
##### Building
|
||||
- To create a build, run `npm run fastlane:android:build` to use fastlane to compile an `.apk` for you
|
||||
|
||||
- To create a build, run `npm run fastlane:android:build` to use fastlane to compile an `.apk` for you
|
||||
|
||||
#### References
|
||||
- [Setting up Android SDK](https://developer.android.com/about/versions/14/setup-sdk)
|
||||
- [ANDROID_HOME not being set](https://stackoverflow.com/questions/26356359/error-android-home-is-not-set-and-android-command-not-in-your-path-you-must/54888107#54888107)
|
||||
|
||||
- [Setting up Android SDK](https://developer.android.com/about/versions/14/setup-sdk)
|
||||
- [ANDROID_HOME not being set](https://stackoverflow.com/questions/26356359/error-android-home-is-not-set-and-android-command-not-in-your-path-you-must/54888107#54888107)
|
||||
|
||||
## 🙏 Special Thanks To
|
||||
- The [Jellyfin Team](https://jellyfin.org/) for making this possible with their software, SDKs, and unequivocal helpfulness.
|
||||
- Extra thanks to [Niels](https://github.com/nielsvanvelzen) and [Bill](https://github.com/thornbill)
|
||||
- [James](https://github.com/jmshrv) and all other contributors of [Finamp](https://github.com/jmshrv/finamp). *Jellify* draws inspiration and wisdom from it, and is another fantastic music app for Jellyfin.
|
||||
- James’ [API Blog Post](https://jmshrv.com/posts/jellyfin-api/) proved to be exceptionally valuable during development
|
||||
- The folks in the [Margelo Community Discord](https://discord.com/invite/6CSHz2qAvA) for their assistance
|
||||
- Extra thanks to [Ritesh](https://github.com/riteshshukla04) for your help, knowledge, and guidance
|
||||
- [Nicolas Charpentier](https://github.com/charpeni) for his [React Native URL Polyfill](https://github.com/charpeni/react-native-url-polyfill) module and for his assistance with getting Jest working
|
||||
- My fellow [contributors](https://github.com/anultravioletaurora/Jellify/graphs/contributors) who have poured so much heart and a lot of sweat into making *Jellify* a great experience
|
||||
- Extra thanks to [John Grant](https://github.com/johngrantdev) for shaping and designing the user experience in many places
|
||||
- The friends I made along the way that have been critical in fostering an amazing community around *Jellify*
|
||||
- [Thalia](https://github.com/PercyGabriel1129)
|
||||
- [BotBlake](https://github.com/BotBlake)
|
||||
- My long time friends that have heard me talk about *Jellify* for literally **eons**. Thank you for testing *Jellify* during it's infancy and for supporting me all the way back at the beginning of this project
|
||||
- Tony (iOS, Android)
|
||||
- Trevor (Android)
|
||||
- [Laine](https://github.com/lainie-ftw) (Android)
|
||||
- [Jordan](https://github.com/jordanbleu) (iOS)
|
||||
- My best(est) friend [Alyssa](https://www.instagram.com/uhh.lyssarae?igsh=MTRmczExempnbjBwZw==), for your design knowledge and for making various artwork for *Jellify*.
|
||||
- You’ve been instrumental in shaping it’s user experience, my rock during development, and an overall inspiration in my life
|
||||
|
||||
- The [Jellyfin Team](https://jellyfin.org/) for making this possible with their software, SDKs, and unequivocal helpfulness.
|
||||
- Extra thanks to [Niels](https://github.com/nielsvanvelzen) and [Bill](https://github.com/thornbill)
|
||||
- [James](https://github.com/jmshrv) and all other contributors of [Finamp](https://github.com/jmshrv/finamp). _Jellify_ draws inspiration and wisdom from it, and is another fantastic music app for Jellyfin.
|
||||
- James’ [API Blog Post](https://jmshrv.com/posts/jellyfin-api/) proved to be exceptionally valuable during development
|
||||
- The folks in the [Margelo Community Discord](https://discord.com/invite/6CSHz2qAvA) for their assistance
|
||||
- Extra thanks to [Ritesh](https://github.com/riteshshukla04) for your help, knowledge, and guidance
|
||||
- [Nicolas Charpentier](https://github.com/charpeni) for his [React Native URL Polyfill](https://github.com/charpeni/react-native-url-polyfill) module and for his assistance with getting Jest working
|
||||
- My fellow [contributors](https://github.com/anultravioletaurora/Jellify/graphs/contributors) who have poured so much heart and a lot of sweat into making _Jellify_ a great experience
|
||||
- Extra thanks to [John Grant](https://github.com/johngrantdev) for shaping and designing the user experience in many places
|
||||
- The friends I made along the way that have been critical in fostering an amazing community around _Jellify_
|
||||
- [Thalia](https://github.com/PercyGabriel1129)
|
||||
- [BotBlake](https://github.com/BotBlake)
|
||||
- My long time friends that have heard me talk about _Jellify_ for literally **eons**. Thank you for testing _Jellify_ during it's infancy and for supporting me all the way back at the beginning of this project
|
||||
- Tony (iOS, Android)
|
||||
- Trevor (Android)
|
||||
- [Laine](https://github.com/lainie-ftw) (Android)
|
||||
- [Jordan](https://github.com/jordanbleu) (iOS)
|
||||
- My best(est) friend [Alyssa](https://www.instagram.com/uhh.lyssarae?igsh=MTRmczExempnbjBwZw==), for your design knowledge and for making various artwork for _Jellify_.
|
||||
- You’ve been instrumental in shaping it’s user experience, my rock during development, and an overall inspiration in my life
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
fastlane documentation
|
||||
----
|
||||
## fastlane documentation
|
||||
|
||||
# Installation
|
||||
|
||||
@@ -29,8 +28,6 @@ Runs all the tests
|
||||
[bundle exec] fastlane android build
|
||||
```
|
||||
|
||||
|
||||
|
||||
### android deploy
|
||||
|
||||
```sh
|
||||
@@ -39,7 +36,7 @@ Runs all the tests
|
||||
|
||||
Deploy a new version to the Google Play
|
||||
|
||||
----
|
||||
---
|
||||
|
||||
This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.
|
||||
|
||||
|
||||
@@ -1,69 +1,69 @@
|
||||
{
|
||||
"migIndex": 1,
|
||||
"data": [
|
||||
{
|
||||
"path": "assets/fonts/Aileron-Black.otf",
|
||||
"sha1": "f9c3d80856ec2f23c8733b63edc9bd9d3051d999"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-BlackItalic.otf",
|
||||
"sha1": "ffb617e90f50dfe1eb2bd4df80736d55ccacbb73"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-Bold.otf",
|
||||
"sha1": "9daa863f1c9a0f9efacd19fe9329c0fb9332ca7a"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-BoldItalic.otf",
|
||||
"sha1": "13dbc6d1c10932eeacac7c680a7f71a25f6f821e"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-Heavy.otf",
|
||||
"sha1": "56a9def7cf4ad3efefec7485be8cd95a265ab1f6"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-HeavyItalic.otf",
|
||||
"sha1": "23255fa29564f9757f779ba29c1d5649c2bf4259"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-Italic.otf",
|
||||
"sha1": "338b043581d997314a4a03924ed30ff6461fd37e"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-Light.otf",
|
||||
"sha1": "bf29e850d4c6dc3c73e46eb322f367c81ca07aad"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-LightItalic.otf",
|
||||
"sha1": "48a4355b8792657845b3b0cd39c42994923a117a"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-Regular.otf",
|
||||
"sha1": "5a78965873fbce38941cd3da109280af89a42de5"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-SemiBold.otf",
|
||||
"sha1": "3c4affc8a57d6915e1255fd6c5312d1443bcc824"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-SemiBoldItalic.otf",
|
||||
"sha1": "46f85a5b66cf813651057ff2ed623527bdcd4b6f"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-Thin.otf",
|
||||
"sha1": "ee9d845c2b370a3ac00cfe402079233f8621ef9c"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-ThinItalic.otf",
|
||||
"sha1": "31db89d81d0f354cc67dfc53bad54be5bfd44214"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-UltraLight.otf",
|
||||
"sha1": "ee4b6ef0bb1606ef950ba9acca0e78bb2cc2dc24"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-UltraLightItalic.otf",
|
||||
"sha1": "8a34c35019102ac48f86fc0255d06c8ca05933d0"
|
||||
}
|
||||
]
|
||||
"migIndex": 1,
|
||||
"data": [
|
||||
{
|
||||
"path": "assets/fonts/Aileron-Black.otf",
|
||||
"sha1": "f9c3d80856ec2f23c8733b63edc9bd9d3051d999"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-BlackItalic.otf",
|
||||
"sha1": "ffb617e90f50dfe1eb2bd4df80736d55ccacbb73"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-Bold.otf",
|
||||
"sha1": "9daa863f1c9a0f9efacd19fe9329c0fb9332ca7a"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-BoldItalic.otf",
|
||||
"sha1": "13dbc6d1c10932eeacac7c680a7f71a25f6f821e"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-Heavy.otf",
|
||||
"sha1": "56a9def7cf4ad3efefec7485be8cd95a265ab1f6"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-HeavyItalic.otf",
|
||||
"sha1": "23255fa29564f9757f779ba29c1d5649c2bf4259"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-Italic.otf",
|
||||
"sha1": "338b043581d997314a4a03924ed30ff6461fd37e"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-Light.otf",
|
||||
"sha1": "bf29e850d4c6dc3c73e46eb322f367c81ca07aad"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-LightItalic.otf",
|
||||
"sha1": "48a4355b8792657845b3b0cd39c42994923a117a"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-Regular.otf",
|
||||
"sha1": "5a78965873fbce38941cd3da109280af89a42de5"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-SemiBold.otf",
|
||||
"sha1": "3c4affc8a57d6915e1255fd6c5312d1443bcc824"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-SemiBoldItalic.otf",
|
||||
"sha1": "46f85a5b66cf813651057ff2ed623527bdcd4b6f"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-Thin.otf",
|
||||
"sha1": "ee9d845c2b370a3ac00cfe402079233f8621ef9c"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-ThinItalic.otf",
|
||||
"sha1": "31db89d81d0f354cc67dfc53bad54be5bfd44214"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-UltraLight.otf",
|
||||
"sha1": "ee4b6ef0bb1606ef950ba9acca0e78bb2cc2dc24"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-UltraLightItalic.otf",
|
||||
"sha1": "8a34c35019102ac48f86fc0255d06c8ca05933d0"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
4
app.json
4
app.json
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "Jellify",
|
||||
"displayName": "Jellify"
|
||||
"name": "Jellify",
|
||||
"displayName": "Jellify"
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
module.exports = {
|
||||
presets: ['module:@react-native/babel-preset'],
|
||||
plugins: [
|
||||
'react-native-boost/plugin',
|
||||
// react-native-reanimated/plugin has to be listed last
|
||||
'react-native-reanimated/plugin',
|
||||
]
|
||||
};
|
||||
presets: ['module:@react-native/babel-preset'],
|
||||
plugins: [
|
||||
'react-native-boost/plugin',
|
||||
// react-native-reanimated/plugin has to be listed last
|
||||
'react-native-reanimated/plugin',
|
||||
],
|
||||
}
|
||||
|
||||
@@ -72,7 +72,8 @@ export default function BlurhashedImage({
|
||||
<View
|
||||
minHeight={height ?? width}
|
||||
minWidth={width}
|
||||
borderRadius={borderRadius ? borderRadius : 25}>
|
||||
borderRadius={borderRadius ? borderRadius : 25}
|
||||
>
|
||||
{isSuccess ? (
|
||||
<Image
|
||||
source={{
|
||||
|
||||
@@ -24,7 +24,8 @@ export default function Login(): React.JSX.Element {
|
||||
? 'ServerAuthentication'
|
||||
: 'LibrarySelection'
|
||||
}
|
||||
screenOptions={{ headerShown: false }}>
|
||||
screenOptions={{ headerShown: false }}
|
||||
>
|
||||
<Stack.Screen
|
||||
name='ServerAddress'
|
||||
options={{
|
||||
|
||||
@@ -50,7 +50,8 @@ export default function ServerLibrary(): React.JSX.Element {
|
||||
type='single'
|
||||
disableDeactivation={true}
|
||||
value={libraryId}
|
||||
onValueChange={setLibraryId}>
|
||||
onValueChange={setLibraryId}
|
||||
>
|
||||
{libraries!
|
||||
.filter((library) => library.CollectionType === 'music')
|
||||
.map((library) => {
|
||||
@@ -63,7 +64,8 @@ export default function ServerLibrary(): React.JSX.Element {
|
||||
libraryId == library.Id!
|
||||
? getToken('$color.purpleGray')
|
||||
: 'unset'
|
||||
}>
|
||||
}
|
||||
>
|
||||
<Text>{library.Name ?? 'Unnamed Library'}</Text>
|
||||
</ToggleGroup.Item>
|
||||
)
|
||||
@@ -88,7 +90,8 @@ export default function ServerLibrary(): React.JSX.Element {
|
||||
playlistLibraryPrimaryImageId: playlistLibrary?.ImageTags!.Primary,
|
||||
})
|
||||
setLoggedIn(true)
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{`Let's Go!`}
|
||||
</Button>
|
||||
|
||||
@@ -96,7 +99,8 @@ export default function ServerLibrary(): React.JSX.Element {
|
||||
onPress={() => {
|
||||
Client.switchUser()
|
||||
setUser(undefined)
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
Switch User
|
||||
</Button>
|
||||
</SafeAreaView>
|
||||
|
||||
@@ -80,7 +80,8 @@ export const JellifyProvider: ({ children }: { children: ReactNode }) => React.J
|
||||
loggedIn,
|
||||
setLoggedIn,
|
||||
carPlayConnected,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</JellifyContext.Provider>
|
||||
)
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
import { fonts } from "@tamagui/config/v4";
|
||||
import { createFont } from "tamagui";
|
||||
import { fonts } from '@tamagui/config/v4'
|
||||
import { createFont } from 'tamagui'
|
||||
|
||||
const aileronFace = {
|
||||
100: { normal: 'Aileron-UltraLight', italic: 'Aileron UltraLight Italic' },
|
||||
200: { normal: 'Aileron-Thin', italic: 'Aileron Thin Italic' },
|
||||
300: { normal: 'Aileron-Light', italic: 'Aileron Light Italic' },
|
||||
400: { normal: 'Aileron-Regular', italic: 'Aileron Italic'} ,
|
||||
500: { normal: 'Aileron-Regular', italic: 'Aileron Italic' },
|
||||
600: { normal: 'Aileron-SemiBold', italic: 'Aileron SemiBold Italic' },
|
||||
700: { normal: 'Aileron-Bold', italic: 'Aileron Bold Italic' },
|
||||
800: { normal: 'Aileron-Heavy', italic: 'Aileron Heavy Italic' },
|
||||
900: { normal: 'Aileron-Black', italic: 'Aileron-BlackItalic' }
|
||||
};
|
||||
100: { normal: 'Aileron-UltraLight', italic: 'Aileron UltraLight Italic' },
|
||||
200: { normal: 'Aileron-Thin', italic: 'Aileron Thin Italic' },
|
||||
300: { normal: 'Aileron-Light', italic: 'Aileron Light Italic' },
|
||||
400: { normal: 'Aileron-Regular', italic: 'Aileron Italic' },
|
||||
500: { normal: 'Aileron-Regular', italic: 'Aileron Italic' },
|
||||
600: { normal: 'Aileron-SemiBold', italic: 'Aileron SemiBold Italic' },
|
||||
700: { normal: 'Aileron-Bold', italic: 'Aileron Bold Italic' },
|
||||
800: { normal: 'Aileron-Heavy', italic: 'Aileron Heavy Italic' },
|
||||
900: { normal: 'Aileron-Black', italic: 'Aileron-BlackItalic' },
|
||||
}
|
||||
|
||||
export const bodyFont = createFont({
|
||||
family: "Aileron-Bold",
|
||||
size: fonts.body.size,
|
||||
lineHeight: fonts.body.lineHeight,
|
||||
weight: fonts.body.weight,
|
||||
letterSpacing: fonts.body.letterSpacing,
|
||||
face: aileronFace
|
||||
family: 'Aileron-Bold',
|
||||
size: fonts.body.size,
|
||||
lineHeight: fonts.body.lineHeight,
|
||||
weight: fonts.body.weight,
|
||||
letterSpacing: fonts.body.letterSpacing,
|
||||
face: aileronFace,
|
||||
})
|
||||
|
||||
export const headingFont = createFont({
|
||||
family: "Aileron-Black",
|
||||
size: fonts.heading.size,
|
||||
lineHeight: fonts.heading.lineHeight,
|
||||
weight: fonts.heading.weight,
|
||||
letterSpacing: fonts.heading.letterSpacing,
|
||||
face: aileronFace
|
||||
})
|
||||
family: 'Aileron-Black',
|
||||
size: fonts.heading.size,
|
||||
lineHeight: fonts.heading.lineHeight,
|
||||
weight: fonts.heading.weight,
|
||||
letterSpacing: fonts.heading.letterSpacing,
|
||||
face: aileronFace,
|
||||
})
|
||||
|
||||
@@ -1 +1 @@
|
||||
// Don't import react-native-gesture-handler on web
|
||||
// Don't import react-native-gesture-handler on web
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
// Only import react-native-gesture-handler on native platforms
|
||||
import 'react-native-gesture-handler';
|
||||
import 'react-native-gesture-handler'
|
||||
|
||||
21
index.js
21
index.js
@@ -1,19 +1,20 @@
|
||||
import 'react-native-gesture-handler';
|
||||
import {AppRegistry} from 'react-native';
|
||||
import App from './App';
|
||||
import {name as appName} from './app.json';
|
||||
import 'react-native-gesture-handler'
|
||||
import { AppRegistry } from 'react-native'
|
||||
import App from './App'
|
||||
import { name as appName } from './app.json'
|
||||
import { PlaybackService } from './player/service'
|
||||
import TrackPlayer from 'react-native-track-player';
|
||||
import Client from './api/client';
|
||||
import TrackPlayer from 'react-native-track-player'
|
||||
import Client from './api/client'
|
||||
|
||||
// Initialize API client instance
|
||||
Client.instance;
|
||||
/* eslint-disable @typescript-eslint/no-unused-expressions */
|
||||
Client.instance
|
||||
|
||||
// Enable React Navigation freeze for detaching inactive screens
|
||||
// enableFreeze();
|
||||
|
||||
AppRegistry.registerComponent(appName, () => App);
|
||||
AppRegistry.registerComponent('RNCarPlayScene', () => App);
|
||||
AppRegistry.registerComponent(appName, () => App)
|
||||
AppRegistry.registerComponent('RNCarPlayScene', () => App)
|
||||
|
||||
// Register RNTP playback service for remote controls
|
||||
TrackPlayer.registerPlaybackService(() => PlaybackService);
|
||||
TrackPlayer.registerPlaybackService(() => PlaybackService)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
"info": {
|
||||
"version": 1,
|
||||
"author": "xcode"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
fastlane documentation
|
||||
----
|
||||
## fastlane documentation
|
||||
|
||||
# Installation
|
||||
|
||||
@@ -23,7 +22,7 @@ For _fastlane_ installation instructions, see [Installing _fastlane_](https://do
|
||||
|
||||
Push a new beta build to TestFlight
|
||||
|
||||
----
|
||||
---
|
||||
|
||||
This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run.
|
||||
|
||||
|
||||
@@ -1,69 +1,69 @@
|
||||
{
|
||||
"migIndex": 1,
|
||||
"data": [
|
||||
{
|
||||
"path": "assets/fonts/Aileron-Black.otf",
|
||||
"sha1": "f9c3d80856ec2f23c8733b63edc9bd9d3051d999"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-BlackItalic.otf",
|
||||
"sha1": "ffb617e90f50dfe1eb2bd4df80736d55ccacbb73"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-Bold.otf",
|
||||
"sha1": "9daa863f1c9a0f9efacd19fe9329c0fb9332ca7a"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-BoldItalic.otf",
|
||||
"sha1": "13dbc6d1c10932eeacac7c680a7f71a25f6f821e"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-Heavy.otf",
|
||||
"sha1": "56a9def7cf4ad3efefec7485be8cd95a265ab1f6"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-HeavyItalic.otf",
|
||||
"sha1": "23255fa29564f9757f779ba29c1d5649c2bf4259"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-Italic.otf",
|
||||
"sha1": "338b043581d997314a4a03924ed30ff6461fd37e"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-Light.otf",
|
||||
"sha1": "bf29e850d4c6dc3c73e46eb322f367c81ca07aad"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-LightItalic.otf",
|
||||
"sha1": "48a4355b8792657845b3b0cd39c42994923a117a"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-Regular.otf",
|
||||
"sha1": "5a78965873fbce38941cd3da109280af89a42de5"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-SemiBold.otf",
|
||||
"sha1": "3c4affc8a57d6915e1255fd6c5312d1443bcc824"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-SemiBoldItalic.otf",
|
||||
"sha1": "46f85a5b66cf813651057ff2ed623527bdcd4b6f"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-Thin.otf",
|
||||
"sha1": "ee9d845c2b370a3ac00cfe402079233f8621ef9c"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-ThinItalic.otf",
|
||||
"sha1": "31db89d81d0f354cc67dfc53bad54be5bfd44214"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-UltraLight.otf",
|
||||
"sha1": "ee4b6ef0bb1606ef950ba9acca0e78bb2cc2dc24"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-UltraLightItalic.otf",
|
||||
"sha1": "8a34c35019102ac48f86fc0255d06c8ca05933d0"
|
||||
}
|
||||
]
|
||||
"migIndex": 1,
|
||||
"data": [
|
||||
{
|
||||
"path": "assets/fonts/Aileron-Black.otf",
|
||||
"sha1": "f9c3d80856ec2f23c8733b63edc9bd9d3051d999"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-BlackItalic.otf",
|
||||
"sha1": "ffb617e90f50dfe1eb2bd4df80736d55ccacbb73"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-Bold.otf",
|
||||
"sha1": "9daa863f1c9a0f9efacd19fe9329c0fb9332ca7a"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-BoldItalic.otf",
|
||||
"sha1": "13dbc6d1c10932eeacac7c680a7f71a25f6f821e"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-Heavy.otf",
|
||||
"sha1": "56a9def7cf4ad3efefec7485be8cd95a265ab1f6"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-HeavyItalic.otf",
|
||||
"sha1": "23255fa29564f9757f779ba29c1d5649c2bf4259"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-Italic.otf",
|
||||
"sha1": "338b043581d997314a4a03924ed30ff6461fd37e"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-Light.otf",
|
||||
"sha1": "bf29e850d4c6dc3c73e46eb322f367c81ca07aad"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-LightItalic.otf",
|
||||
"sha1": "48a4355b8792657845b3b0cd39c42994923a117a"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-Regular.otf",
|
||||
"sha1": "5a78965873fbce38941cd3da109280af89a42de5"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-SemiBold.otf",
|
||||
"sha1": "3c4affc8a57d6915e1255fd6c5312d1443bcc824"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-SemiBoldItalic.otf",
|
||||
"sha1": "46f85a5b66cf813651057ff2ed623527bdcd4b6f"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-Thin.otf",
|
||||
"sha1": "ee9d845c2b370a3ac00cfe402079233f8621ef9c"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-ThinItalic.otf",
|
||||
"sha1": "31db89d81d0f354cc67dfc53bad54be5bfd44214"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-UltraLight.otf",
|
||||
"sha1": "ee4b6ef0bb1606ef950ba9acca0e78bb2cc2dc24"
|
||||
},
|
||||
{
|
||||
"path": "assets/fonts/Aileron-UltraLightItalic.otf",
|
||||
"sha1": "8a34c35019102ac48f86fc0255d06c8ca05933d0"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
// https://docs.swmansion.com/react-native-gesture-handler/docs/guides/testing
|
||||
module.exports = {
|
||||
preset: 'jest-expo',
|
||||
setupFiles: ["./node_modules/react-native-gesture-handler/jestSetup.js"],
|
||||
setupFilesAfterEnv: [
|
||||
"./jest/setup.js",
|
||||
"./jest/setup-carplay.js",
|
||||
"./jest/setup-blurhash.js",
|
||||
"./jest/setup-reanimated.js",
|
||||
"./tamagui.config.ts",
|
||||
],
|
||||
extensionsToTreatAsEsm: ['.ts', '.tsx'],
|
||||
transformIgnorePatterns: [
|
||||
'node_modules/(?!(@)?(react-native|react-native-.*|react-navigation|jellyfin|burnt|expo|expo-.*)/)',
|
||||
],
|
||||
};
|
||||
preset: 'jest-expo',
|
||||
setupFiles: ['./node_modules/react-native-gesture-handler/jestSetup.js'],
|
||||
setupFilesAfterEnv: [
|
||||
'./jest/setup.js',
|
||||
'./jest/setup-carplay.js',
|
||||
'./jest/setup-blurhash.js',
|
||||
'./jest/setup-reanimated.js',
|
||||
'./tamagui.config.ts',
|
||||
],
|
||||
extensionsToTreatAsEsm: ['.ts', '.tsx'],
|
||||
transformIgnorePatterns: [
|
||||
'node_modules/(?!(@)?(react-native|react-native-.*|react-navigation|jellyfin|burnt|expo|expo-.*)/)',
|
||||
],
|
||||
}
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
* @format
|
||||
*/
|
||||
|
||||
import 'react-native';
|
||||
import React from 'react';
|
||||
import App from '../App';
|
||||
import 'react-native'
|
||||
import React from 'react'
|
||||
import App from '../App'
|
||||
|
||||
// Note: import explicitly to use the types shipped with jest.
|
||||
import {it} from '@jest/globals';
|
||||
import { it } from '@jest/globals'
|
||||
|
||||
// Note: test renderer must be required after react-native.
|
||||
import renderer from 'react-test-renderer';
|
||||
import renderer from 'react-test-renderer'
|
||||
|
||||
it('renders correctly', () => {
|
||||
renderer.create(<App />);
|
||||
});
|
||||
renderer.create(<App />)
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
jest.mock('react-native-blurhash',() => {
|
||||
return {
|
||||
Blurhash: ()=> null
|
||||
};
|
||||
});
|
||||
jest.mock('react-native-blurhash', () => {
|
||||
return {
|
||||
Blurhash: () => null,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
jest.mock('react-native-carplay', () => {
|
||||
return {
|
||||
default: {
|
||||
checkForConnection: jest.fn()
|
||||
}
|
||||
}
|
||||
})
|
||||
return {
|
||||
default: {
|
||||
checkForConnection: jest.fn(),
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
jest.mock('react-native-reanimated', () => ({
|
||||
...jest.requireActual('react-native-reanimated/mock'),
|
||||
createAnimatedPropAdapter: jest.fn,
|
||||
useReducedMotion: jest.fn,
|
||||
LayoutAnimationConfig: jest.fn,
|
||||
}));
|
||||
...jest.requireActual('react-native-reanimated/mock'),
|
||||
createAnimatedPropAdapter: jest.fn,
|
||||
useReducedMotion: jest.fn,
|
||||
LayoutAnimationConfig: jest.fn,
|
||||
}))
|
||||
|
||||
197
jest/setup.js
197
jest/setup.js
@@ -1,110 +1,109 @@
|
||||
// https://github.com/react-native-device-info/react-native-device-info/issues/1360
|
||||
import mockRNDeviceInfo from 'react-native-device-info/jest/react-native-device-info-mock';
|
||||
import mockRNDeviceInfo from 'react-native-device-info/jest/react-native-device-info-mock'
|
||||
|
||||
jest.mock('react-native/Libraries/EventEmitter/NativeEventEmitter');
|
||||
jest.mock('react-native/Libraries/EventEmitter/NativeEventEmitter')
|
||||
|
||||
jest.mock('react-native-device-info', () => mockRNDeviceInfo);
|
||||
jest.mock('react-native-device-info', () => mockRNDeviceInfo)
|
||||
|
||||
jest.mock('react-native-haptic-feedback', () => {
|
||||
return {
|
||||
default: {
|
||||
trigger: jest.fn()
|
||||
}
|
||||
}
|
||||
});
|
||||
return {
|
||||
default: {
|
||||
trigger: jest.fn(),
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
jest.mock('burnt', () => {
|
||||
return {
|
||||
default: {
|
||||
alert: jest.fn()
|
||||
}
|
||||
}
|
||||
return {
|
||||
default: {
|
||||
alert: jest.fn(),
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
// https://github.com/doublesymmetry/react-native-track-player/issues/501
|
||||
jest.mock('react-native-track-player', () => {
|
||||
return {
|
||||
__esModule: true,
|
||||
default: {
|
||||
addEventListener: () => ({
|
||||
remove: jest.fn(),
|
||||
}),
|
||||
registerEventHandler: jest.fn(),
|
||||
registerPlaybackService: jest.fn(),
|
||||
setupPlayer: jest.fn().mockResolvedValue(undefined),
|
||||
destroy: jest.fn(),
|
||||
updateOptions: jest.fn(),
|
||||
reset: jest.fn(),
|
||||
add: jest.fn(),
|
||||
remove: jest.fn(),
|
||||
skip: jest.fn(),
|
||||
skipToNext: jest.fn(),
|
||||
skipToPrevious: jest.fn(),
|
||||
removeUpcomingTracks: jest.fn(),
|
||||
// playback commands
|
||||
play: jest.fn(),
|
||||
pause: jest.fn(),
|
||||
stop: jest.fn(),
|
||||
seekTo: jest.fn(),
|
||||
setVolume: jest.fn(),
|
||||
setRate: jest.fn(),
|
||||
// player getters
|
||||
getQueue: jest.fn(),
|
||||
getTrack: jest.fn(),
|
||||
getActiveTrackIndex: jest.fn(),
|
||||
getCurrentTrack: jest.fn(),
|
||||
getVolume: jest.fn(),
|
||||
getDuration: jest.fn(),
|
||||
getPosition: jest.fn(),
|
||||
getBufferedPosition: jest.fn(),
|
||||
getState: jest.fn(),
|
||||
getRate: jest.fn(),
|
||||
},
|
||||
useProgress: () => ({
|
||||
position: 100,
|
||||
buffered: 150,
|
||||
duration: 200,
|
||||
}),
|
||||
Capability: {
|
||||
Play: 1,
|
||||
PlayFromId: 2,
|
||||
PlayFromSearch: 4,
|
||||
Pause: 8,
|
||||
Stop: 16,
|
||||
SeekTo: 32,
|
||||
Skip: 64,
|
||||
SkipToNext: 128,
|
||||
SkipToPrevious: 256,
|
||||
},
|
||||
IOSCategoryOptions: {
|
||||
MixWithOthers: 'mixWithOthers',
|
||||
DuckOthers: 'duckOthers',
|
||||
InterruptSpokenAudioAndMixWithOthers:
|
||||
'interruptSpokenAudioAndMixWithOthers',
|
||||
AllowBluetooth: 'allowBluetooth',
|
||||
AllowBluetoothA2DP: 'allowBluetoothA2DP',
|
||||
AllowAirPlay: 'allowAirPlay',
|
||||
DefaultToSpeaker: 'defaultToSpeaker',
|
||||
},
|
||||
IOSCategoryMode: {
|
||||
Default: 'default',
|
||||
GameChat: 'gameChat',
|
||||
Measurement: 'measurement',
|
||||
MoviePlayback: 'moviePlayback',
|
||||
SpokenAudio: 'spokenAudio',
|
||||
VideoChat: 'videoChat',
|
||||
VideoRecording: 'videoRecording',
|
||||
VoiceChat: 'voiceChat',
|
||||
VoicePrompt: 'voicePrompt',
|
||||
},
|
||||
IOSCategory: {
|
||||
Playback: 'playback',
|
||||
PlaybackAndRecord: 'playbackAndRecord',
|
||||
MultiRoute: 'multiRoute',
|
||||
Ambient: 'ambient',
|
||||
SoloAmbient: 'soloAmbient',
|
||||
Record: 'record',
|
||||
PlayAndRecord: 'playAndRecord',
|
||||
},
|
||||
}
|
||||
});
|
||||
return {
|
||||
__esModule: true,
|
||||
default: {
|
||||
addEventListener: () => ({
|
||||
remove: jest.fn(),
|
||||
}),
|
||||
registerEventHandler: jest.fn(),
|
||||
registerPlaybackService: jest.fn(),
|
||||
setupPlayer: jest.fn().mockResolvedValue(undefined),
|
||||
destroy: jest.fn(),
|
||||
updateOptions: jest.fn(),
|
||||
reset: jest.fn(),
|
||||
add: jest.fn(),
|
||||
remove: jest.fn(),
|
||||
skip: jest.fn(),
|
||||
skipToNext: jest.fn(),
|
||||
skipToPrevious: jest.fn(),
|
||||
removeUpcomingTracks: jest.fn(),
|
||||
// playback commands
|
||||
play: jest.fn(),
|
||||
pause: jest.fn(),
|
||||
stop: jest.fn(),
|
||||
seekTo: jest.fn(),
|
||||
setVolume: jest.fn(),
|
||||
setRate: jest.fn(),
|
||||
// player getters
|
||||
getQueue: jest.fn(),
|
||||
getTrack: jest.fn(),
|
||||
getActiveTrackIndex: jest.fn(),
|
||||
getCurrentTrack: jest.fn(),
|
||||
getVolume: jest.fn(),
|
||||
getDuration: jest.fn(),
|
||||
getPosition: jest.fn(),
|
||||
getBufferedPosition: jest.fn(),
|
||||
getState: jest.fn(),
|
||||
getRate: jest.fn(),
|
||||
},
|
||||
useProgress: () => ({
|
||||
position: 100,
|
||||
buffered: 150,
|
||||
duration: 200,
|
||||
}),
|
||||
Capability: {
|
||||
Play: 1,
|
||||
PlayFromId: 2,
|
||||
PlayFromSearch: 4,
|
||||
Pause: 8,
|
||||
Stop: 16,
|
||||
SeekTo: 32,
|
||||
Skip: 64,
|
||||
SkipToNext: 128,
|
||||
SkipToPrevious: 256,
|
||||
},
|
||||
IOSCategoryOptions: {
|
||||
MixWithOthers: 'mixWithOthers',
|
||||
DuckOthers: 'duckOthers',
|
||||
InterruptSpokenAudioAndMixWithOthers: 'interruptSpokenAudioAndMixWithOthers',
|
||||
AllowBluetooth: 'allowBluetooth',
|
||||
AllowBluetoothA2DP: 'allowBluetoothA2DP',
|
||||
AllowAirPlay: 'allowAirPlay',
|
||||
DefaultToSpeaker: 'defaultToSpeaker',
|
||||
},
|
||||
IOSCategoryMode: {
|
||||
Default: 'default',
|
||||
GameChat: 'gameChat',
|
||||
Measurement: 'measurement',
|
||||
MoviePlayback: 'moviePlayback',
|
||||
SpokenAudio: 'spokenAudio',
|
||||
VideoChat: 'videoChat',
|
||||
VideoRecording: 'videoRecording',
|
||||
VoiceChat: 'voiceChat',
|
||||
VoicePrompt: 'voicePrompt',
|
||||
},
|
||||
IOSCategory: {
|
||||
Playback: 'playback',
|
||||
PlaybackAndRecord: 'playbackAndRecord',
|
||||
MultiRoute: 'multiRoute',
|
||||
Ambient: 'ambient',
|
||||
SoloAmbient: 'soloAmbient',
|
||||
Record: 'record',
|
||||
PlayAndRecord: 'playAndRecord',
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
@@ -2,18 +2,14 @@
|
||||
const { getDefaultConfig } = require('@react-native/metro-config')
|
||||
|
||||
const config = getDefaultConfig(__dirname, {
|
||||
// [Web-only]: Enables CSS support in Metro.
|
||||
isCSSEnabled: true,
|
||||
// [Web-only]: Enables CSS support in Metro.
|
||||
isCSSEnabled: true,
|
||||
})
|
||||
|
||||
// Expo 49 issue: default metro config needs to include "mjs"
|
||||
// https://github.com/expo/expo/issues/23180
|
||||
config.resolver.sourceExts.push('mjs')
|
||||
|
||||
config.watchFolders = [
|
||||
"components",
|
||||
"api",
|
||||
"player"
|
||||
]
|
||||
config.watchFolders = ['components', 'api', 'player']
|
||||
|
||||
module.exports = config;
|
||||
module.exports = config
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models'
|
||||
|
||||
export class ArtistModel {
|
||||
name?: string | undefined | null
|
||||
|
||||
name?: string | undefined | null;
|
||||
|
||||
constructor(itemDto : BaseItemDto) {
|
||||
|
||||
this.name = itemDto.Name
|
||||
}
|
||||
}
|
||||
constructor(itemDto: BaseItemDto) {
|
||||
this.name = itemDto.Name
|
||||
}
|
||||
}
|
||||
|
||||
43398
package-lock.json
generated
43398
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
226
package.json
226
package.json
@@ -1,114 +1,114 @@
|
||||
{
|
||||
"name": "jellify",
|
||||
"version": "0.10.91",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"init": "npm i",
|
||||
"android": "react-native run-android",
|
||||
"ios": "react-native run-ios",
|
||||
"lint": "eslint .",
|
||||
"start": "react-native start",
|
||||
"test": "jest",
|
||||
"clean:ios": "cd ios && pod deintegrate",
|
||||
"clean:android": "cd android && rm -rf app/ build/",
|
||||
"pod:install": "cd ios && bundle install && RCT_NEW_ARCH_ENABLED=0 bundle exec pod install",
|
||||
"pod:install-new-arch": "cd ios && bundle install && RCT_NEW_ARCH_ENABLED=1 bundle exec pod install",
|
||||
"fastlane:ios:build": "cd ios && bundle exec fastlane build",
|
||||
"fastlane:ios:beta": "cd ios && bundle exec fastlane beta",
|
||||
"fastlane:android:build": "cd android && bundle install && bundle exec fastlane build",
|
||||
"androidBuild": "cd android && ./gradlew clean && ./gradlew assembleRelease && cd .. && echo 'find apk in android/app/build/outputs/apk/release'",
|
||||
"prepare": "husky"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jellyfin/sdk": "^0.11.0",
|
||||
"@react-native-community/blur": "^4.4.1",
|
||||
"@react-native-community/cli": "^15.1.3",
|
||||
"@react-native-masked-view/masked-view": "^0.3.2",
|
||||
"@react-navigation/bottom-tabs": "^7.2.0",
|
||||
"@react-navigation/native": "^7.0.14",
|
||||
"@react-navigation/native-stack": "^7.1.1",
|
||||
"@react-navigation/stack": "^7.1.0",
|
||||
"@tamagui/config": "^1.124.17",
|
||||
"@tamagui/toast": "^1.124.17",
|
||||
"@tanstack/query-sync-storage-persister": "^5.66.0",
|
||||
"@tanstack/react-query": "^5.66.0",
|
||||
"@tanstack/react-query-persist-client": "^5.66.0",
|
||||
"axios": "^1.7.9",
|
||||
"bundle": "^2.1.0",
|
||||
"bundler": "^0.8.0",
|
||||
"burnt": "^0.12.2",
|
||||
"expo": "^52.0.0",
|
||||
"expo-image": "^2.0.7",
|
||||
"gem": "^2.4.3",
|
||||
"invert-color": "^2.0.0",
|
||||
"jest-expo": "^52.0.6",
|
||||
"lodash": "^4.17.21",
|
||||
"npm-bundle": "^3.0.3",
|
||||
"react": "18.3.1",
|
||||
"react-freeze": "^1.0.4",
|
||||
"react-native": "0.77.0",
|
||||
"react-native-background-actions": "^4.0.1",
|
||||
"react-native-blurhash": "^2.1.1",
|
||||
"react-native-boost": "^0.5.5",
|
||||
"react-native-carplay": "^2.4.1-beta.0",
|
||||
"react-native-device-info": "^14.0.4",
|
||||
"react-native-draggable-flatlist": "^4.0.1",
|
||||
"react-native-file-access": "^3.1.1",
|
||||
"react-native-gesture-handler": "^2.23.0",
|
||||
"react-native-haptic-feedback": "^2.3.3",
|
||||
"react-native-mmkv": "^2.12.2",
|
||||
"react-native-reanimated": "^3.17.2",
|
||||
"react-native-safe-area-context": "^5.2.0",
|
||||
"react-native-screens": "^4.6.0",
|
||||
"react-native-swipeable-item": "^2.0.9",
|
||||
"react-native-text-ticker": "^1.14.0",
|
||||
"react-native-track-player": "^4.1.1",
|
||||
"react-native-url-polyfill": "^2.0.0",
|
||||
"react-native-uuid": "^2.0.3",
|
||||
"react-native-vector-icons": "^10.2.0",
|
||||
"ruby": "^0.6.1",
|
||||
"tamagui": "^1.124.17"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.25.2",
|
||||
"@babel/preset-env": "^7.25.3",
|
||||
"@babel/runtime": "^7.25.0",
|
||||
"@react-native-community/cli-platform-android": "15.1.3",
|
||||
"@react-native-community/cli-platform-ios": "15.1.3",
|
||||
"@react-native/babel-preset": "0.77.0",
|
||||
"@react-native/eslint-config": "0.77.0",
|
||||
"@react-native/metro-config": "0.77.0",
|
||||
"@react-native/typescript-config": "0.77.0",
|
||||
"@types/jest": "^29.5.13",
|
||||
"@types/lodash": "^4.17.10",
|
||||
"@types/react": "^18.2.6",
|
||||
"@types/react-native-vector-icons": "^6.4.18",
|
||||
"@types/react-test-renderer": "^18.3.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.29.1",
|
||||
"@typescript-eslint/parser": "^8.29.1",
|
||||
"babel-plugin-module-resolver": "^5.0.2",
|
||||
"eslint": "^8.57.1",
|
||||
"eslint-config-prettier": "^10.1.2",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint-plugin-prettier": "^5.2.6",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-native": "^5.0.0",
|
||||
"husky": "^9.1.7",
|
||||
"jest": "^29.6.3",
|
||||
"jscodeshift": "^0.15.2",
|
||||
"lint-staged": "^15.5.0",
|
||||
"prettier": "^2.8.8",
|
||||
"react-native-cli-bump-version": "^1.5.1",
|
||||
"react-test-renderer": "18.3.1",
|
||||
"typescript": "5.7.3"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,ts,tsx}": [
|
||||
"prettier --write",
|
||||
"eslint --fix"
|
||||
]
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
}
|
||||
"name": "jellify",
|
||||
"version": "0.10.91",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"init": "npm i",
|
||||
"android": "react-native run-android",
|
||||
"ios": "react-native run-ios",
|
||||
"lint": "eslint .",
|
||||
"start": "react-native start",
|
||||
"test": "jest",
|
||||
"clean:ios": "cd ios && pod deintegrate",
|
||||
"clean:android": "cd android && rm -rf app/ build/",
|
||||
"pod:install": "cd ios && bundle install && RCT_NEW_ARCH_ENABLED=0 bundle exec pod install",
|
||||
"pod:install-new-arch": "cd ios && bundle install && RCT_NEW_ARCH_ENABLED=1 bundle exec pod install",
|
||||
"fastlane:ios:build": "cd ios && bundle exec fastlane build",
|
||||
"fastlane:ios:beta": "cd ios && bundle exec fastlane beta",
|
||||
"fastlane:android:build": "cd android && bundle install && bundle exec fastlane build",
|
||||
"androidBuild": "cd android && ./gradlew clean && ./gradlew assembleRelease && cd .. && echo 'find apk in android/app/build/outputs/apk/release'",
|
||||
"prepare": "husky"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jellyfin/sdk": "^0.11.0",
|
||||
"@react-native-community/blur": "^4.4.1",
|
||||
"@react-native-community/cli": "^15.1.3",
|
||||
"@react-native-masked-view/masked-view": "^0.3.2",
|
||||
"@react-navigation/bottom-tabs": "^7.2.0",
|
||||
"@react-navigation/native": "^7.0.14",
|
||||
"@react-navigation/native-stack": "^7.1.1",
|
||||
"@react-navigation/stack": "^7.1.0",
|
||||
"@tamagui/config": "^1.124.17",
|
||||
"@tamagui/toast": "^1.124.17",
|
||||
"@tanstack/query-sync-storage-persister": "^5.66.0",
|
||||
"@tanstack/react-query": "^5.66.0",
|
||||
"@tanstack/react-query-persist-client": "^5.66.0",
|
||||
"axios": "^1.7.9",
|
||||
"bundle": "^2.1.0",
|
||||
"bundler": "^0.8.0",
|
||||
"burnt": "^0.12.2",
|
||||
"expo": "^52.0.0",
|
||||
"expo-image": "^2.0.7",
|
||||
"gem": "^2.4.3",
|
||||
"invert-color": "^2.0.0",
|
||||
"jest-expo": "^52.0.6",
|
||||
"lodash": "^4.17.21",
|
||||
"npm-bundle": "^3.0.3",
|
||||
"react": "18.3.1",
|
||||
"react-freeze": "^1.0.4",
|
||||
"react-native": "0.77.0",
|
||||
"react-native-background-actions": "^4.0.1",
|
||||
"react-native-blurhash": "^2.1.1",
|
||||
"react-native-boost": "^0.5.5",
|
||||
"react-native-carplay": "^2.4.1-beta.0",
|
||||
"react-native-device-info": "^14.0.4",
|
||||
"react-native-draggable-flatlist": "^4.0.1",
|
||||
"react-native-file-access": "^3.1.1",
|
||||
"react-native-gesture-handler": "^2.23.0",
|
||||
"react-native-haptic-feedback": "^2.3.3",
|
||||
"react-native-mmkv": "^2.12.2",
|
||||
"react-native-reanimated": "^3.17.2",
|
||||
"react-native-safe-area-context": "^5.2.0",
|
||||
"react-native-screens": "^4.6.0",
|
||||
"react-native-swipeable-item": "^2.0.9",
|
||||
"react-native-text-ticker": "^1.14.0",
|
||||
"react-native-track-player": "^4.1.1",
|
||||
"react-native-url-polyfill": "^2.0.0",
|
||||
"react-native-uuid": "^2.0.3",
|
||||
"react-native-vector-icons": "^10.2.0",
|
||||
"ruby": "^0.6.1",
|
||||
"tamagui": "^1.124.17"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.25.2",
|
||||
"@babel/preset-env": "^7.25.3",
|
||||
"@babel/runtime": "^7.25.0",
|
||||
"@react-native-community/cli-platform-android": "15.1.3",
|
||||
"@react-native-community/cli-platform-ios": "15.1.3",
|
||||
"@react-native/babel-preset": "0.77.0",
|
||||
"@react-native/eslint-config": "0.77.0",
|
||||
"@react-native/metro-config": "0.77.0",
|
||||
"@react-native/typescript-config": "0.77.0",
|
||||
"@types/jest": "^29.5.13",
|
||||
"@types/lodash": "^4.17.10",
|
||||
"@types/react": "^18.2.6",
|
||||
"@types/react-native-vector-icons": "^6.4.18",
|
||||
"@types/react-test-renderer": "^18.3.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.29.1",
|
||||
"@typescript-eslint/parser": "^8.29.1",
|
||||
"babel-plugin-module-resolver": "^5.0.2",
|
||||
"eslint": "^8.57.1",
|
||||
"eslint-config-prettier": "^10.1.2",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint-plugin-prettier": "^5.2.6",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-native": "^5.0.0",
|
||||
"husky": "^9.1.7",
|
||||
"jest": "^29.6.3",
|
||||
"jscodeshift": "^0.15.2",
|
||||
"lint-staged": "^15.5.0",
|
||||
"prettier": "^2.8.8",
|
||||
"react-native-cli-bump-version": "^1.5.1",
|
||||
"react-test-renderer": "18.3.1",
|
||||
"typescript": "5.7.3"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,ts,tsx}": [
|
||||
"prettier --write",
|
||||
"eslint --fix"
|
||||
]
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
export const UPDATE_INTERVAL: number = 7 // 8 milliseconds
|
||||
export const UPDATE_INTERVAL: number = 7 // 8 milliseconds
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { Capability } from "react-native-track-player";
|
||||
import { Capability } from 'react-native-track-player'
|
||||
|
||||
export const CAPABILITIES: Capability[] = [
|
||||
Capability.Pause,
|
||||
Capability.Play,
|
||||
Capability.PlayFromId,
|
||||
Capability.SeekTo,
|
||||
// Capability.JumpForward,
|
||||
// Capability.JumpBackward,
|
||||
Capability.SkipToNext,
|
||||
Capability.SkipToPrevious,
|
||||
// Capability.Like,
|
||||
// Capability.Dislike
|
||||
]
|
||||
Capability.Pause,
|
||||
Capability.Play,
|
||||
Capability.PlayFromId,
|
||||
Capability.SeekTo,
|
||||
// Capability.JumpForward,
|
||||
// Capability.JumpBackward,
|
||||
Capability.SkipToNext,
|
||||
Capability.SkipToPrevious,
|
||||
// Capability.Like,
|
||||
// Capability.Dislike
|
||||
]
|
||||
|
||||
@@ -1,61 +1,71 @@
|
||||
import { Progress, State } from "react-native-track-player";
|
||||
import { JellifyTrack } from "../types/JellifyTrack";
|
||||
import { PlaystateApi } from "@jellyfin/sdk/lib/generated-client/api/playstate-api";
|
||||
import { convertSecondsToRunTimeTicks } from "../helpers/runtimeticks";
|
||||
import { Progress, State } from 'react-native-track-player'
|
||||
import { JellifyTrack } from '../types/JellifyTrack'
|
||||
import { PlaystateApi } from '@jellyfin/sdk/lib/generated-client/api/playstate-api'
|
||||
import { convertSecondsToRunTimeTicks } from '../helpers/runtimeticks'
|
||||
|
||||
export async function handlePlaybackState(sessionId: string, playstateApi: PlaystateApi, track: JellifyTrack, state: State) {
|
||||
switch (state) {
|
||||
case (State.Playing) : {
|
||||
console.debug("Report playback started")
|
||||
await playstateApi.reportPlaybackStart({
|
||||
playbackStartInfo: {
|
||||
SessionId: sessionId,
|
||||
ItemId: track.item.Id,
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case (State.Ended) :
|
||||
case (State.Paused) :
|
||||
case (State.Stopped) : {
|
||||
console.debug("Report playback stopped")
|
||||
await playstateApi.reportPlaybackStopped({
|
||||
playbackStopInfo: {
|
||||
SessionId: sessionId,
|
||||
ItemId: track.item.Id,
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
export async function handlePlaybackState(
|
||||
sessionId: string,
|
||||
playstateApi: PlaystateApi,
|
||||
track: JellifyTrack,
|
||||
state: State,
|
||||
) {
|
||||
switch (state) {
|
||||
case State.Playing: {
|
||||
console.debug('Report playback started')
|
||||
await playstateApi.reportPlaybackStart({
|
||||
playbackStartInfo: {
|
||||
SessionId: sessionId,
|
||||
ItemId: track.item.Id,
|
||||
},
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
default : {
|
||||
return;
|
||||
}
|
||||
}
|
||||
case State.Ended:
|
||||
case State.Paused:
|
||||
case State.Stopped: {
|
||||
console.debug('Report playback stopped')
|
||||
await playstateApi.reportPlaybackStopped({
|
||||
playbackStopInfo: {
|
||||
SessionId: sessionId,
|
||||
ItemId: track.item.Id,
|
||||
},
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
default: {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function handlePlaybackProgressUpdated(sessionId: string, playstateApi: PlaystateApi, track: JellifyTrack, progress: Progress) {
|
||||
if (Math.floor(progress.duration - progress.position) === 5) {
|
||||
console.debug("Track finished, scrobbling...");
|
||||
await playstateApi.reportPlaybackStopped({
|
||||
playbackStopInfo: {
|
||||
SessionId: sessionId,
|
||||
ItemId: track.item.Id,
|
||||
PositionTicks: convertSecondsToRunTimeTicks(track.duration!)
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// DO NOTHING, reporting playback will just eat up power
|
||||
// Jellyfin can keep track of progress, we're going to intentionally
|
||||
// only give it the "greatest hits" (i.e., anything that involves user interaction)
|
||||
// console.debug("Reporting playback position");
|
||||
// await playstateApi.reportPlaybackProgress({
|
||||
// playbackProgressInfo: {
|
||||
// SessionId: sessionId,
|
||||
// ItemId: track.ItemId,
|
||||
// PositionTicks: convertSecondsToRunTimeTicks(progress.position)
|
||||
// }
|
||||
// });
|
||||
}
|
||||
export async function handlePlaybackProgressUpdated(
|
||||
sessionId: string,
|
||||
playstateApi: PlaystateApi,
|
||||
track: JellifyTrack,
|
||||
progress: Progress,
|
||||
) {
|
||||
if (Math.floor(progress.duration - progress.position) === 5) {
|
||||
console.debug('Track finished, scrobbling...')
|
||||
await playstateApi.reportPlaybackStopped({
|
||||
playbackStopInfo: {
|
||||
SessionId: sessionId,
|
||||
ItemId: track.item.Id,
|
||||
PositionTicks: convertSecondsToRunTimeTicks(track.duration!),
|
||||
},
|
||||
})
|
||||
} else {
|
||||
// DO NOTHING, reporting playback will just eat up power
|
||||
// Jellyfin can keep track of progress, we're going to intentionally
|
||||
// only give it the "greatest hits" (i.e., anything that involves user interaction)
|
||||
// console.debug("Reporting playback position");
|
||||
// await playstateApi.reportPlaybackProgress({
|
||||
// playbackProgressInfo: {
|
||||
// SessionId: sessionId,
|
||||
// ItemId: track.ItemId,
|
||||
// PositionTicks: convertSecondsToRunTimeTicks(progress.position)
|
||||
// }
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { isEmpty } from "lodash";
|
||||
import { QueuingType } from "../../enums/queuing-type";
|
||||
import { JellifyTrack } from "../../types/JellifyTrack";
|
||||
import { getActiveTrackIndex } from "react-native-track-player/lib/src/trackPlayer";
|
||||
import { isEmpty } from 'lodash'
|
||||
import { QueuingType } from '../../enums/queuing-type'
|
||||
import { JellifyTrack } from '../../types/JellifyTrack'
|
||||
import { getActiveTrackIndex } from 'react-native-track-player/lib/src/trackPlayer'
|
||||
|
||||
/**
|
||||
* Finds and returns the index of the player queue to insert additional tracks into
|
||||
@@ -9,29 +9,26 @@ import { getActiveTrackIndex } from "react-native-track-player/lib/src/trackPlay
|
||||
* @returns The index to insert songs to play next at
|
||||
*/
|
||||
export const findPlayNextIndexStart = async (playQueue: JellifyTrack[]) => {
|
||||
if (isEmpty(playQueue))
|
||||
return 0;
|
||||
if (isEmpty(playQueue)) return 0
|
||||
|
||||
return (await getActiveTrackIndex())! + 1;
|
||||
return (await getActiveTrackIndex())! + 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and returns the index of the play queue to insert user queue tracks into
|
||||
* Finds and returns the index of the play queue to insert user queue tracks into
|
||||
* @param playQueue The current player queue
|
||||
* @returns The index to insert songs to add to the user queue
|
||||
*/
|
||||
export const findPlayQueueIndexStart = async (playQueue: JellifyTrack[]) => {
|
||||
if (isEmpty(playQueue)) return 0
|
||||
|
||||
if (isEmpty(playQueue))
|
||||
return 0;
|
||||
const activeIndex = await getActiveTrackIndex()
|
||||
|
||||
const activeIndex = await getActiveTrackIndex();
|
||||
if (playQueue.findIndex((track) => track.QueuingType === QueuingType.FromSelection) === -1)
|
||||
return activeIndex! + 1
|
||||
|
||||
if (playQueue.findIndex(track => track.QueuingType === QueuingType.FromSelection) === -1)
|
||||
return activeIndex! + 1
|
||||
|
||||
return playQueue.findIndex((queuedTrack, index) =>
|
||||
queuedTrack.QueuingType === QueuingType.FromSelection &&
|
||||
index > activeIndex!
|
||||
);
|
||||
}
|
||||
return playQueue.findIndex(
|
||||
(queuedTrack, index) =>
|
||||
queuedTrack.QueuingType === QueuingType.FromSelection && index > activeIndex!,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
import _ from "lodash";
|
||||
import { JellifyTrack } from "../../types/JellifyTrack";
|
||||
import _ from 'lodash'
|
||||
import { JellifyTrack } from '../../types/JellifyTrack'
|
||||
|
||||
export function buildNewQueue(existingQueue: JellifyTrack[], tracksToInsert: JellifyTrack[], insertIndex: number) {
|
||||
export function buildNewQueue(
|
||||
existingQueue: JellifyTrack[],
|
||||
tracksToInsert: JellifyTrack[],
|
||||
insertIndex: number,
|
||||
) {
|
||||
console.debug(`Building new queue`)
|
||||
|
||||
console.debug(`Building new queue`);
|
||||
let newQueue: JellifyTrack[] = []
|
||||
|
||||
let newQueue : JellifyTrack[] = [];
|
||||
if (_.isEmpty(existingQueue)) newQueue = tracksToInsert
|
||||
else {
|
||||
newQueue = _.cloneDeep(existingQueue).splice(insertIndex, 0, ...tracksToInsert)
|
||||
}
|
||||
|
||||
if (_.isEmpty(existingQueue))
|
||||
newQueue = tracksToInsert;
|
||||
else {
|
||||
newQueue = _.cloneDeep(existingQueue).splice(insertIndex, 0, ...tracksToInsert);
|
||||
}
|
||||
console.debug(`Built new queue of ${newQueue.length} items`)
|
||||
|
||||
console.debug(`Built new queue of ${newQueue.length} items`);
|
||||
|
||||
return newQueue;
|
||||
}
|
||||
return newQueue
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import TrackPlayer, { RatingType } from "react-native-track-player"
|
||||
import { CAPABILITIES } from "../constants";
|
||||
import TrackPlayer, { RatingType } from 'react-native-track-player'
|
||||
import { CAPABILITIES } from '../constants'
|
||||
|
||||
export const useUpdateOptions = async (isFavorite: boolean) => {
|
||||
return await TrackPlayer.updateOptions({
|
||||
progressUpdateEventInterval: 1,
|
||||
capabilities: CAPABILITIES,
|
||||
notificationCapabilities: CAPABILITIES,
|
||||
compactCapabilities: CAPABILITIES,
|
||||
ratingType: RatingType.Heart,
|
||||
likeOptions: {
|
||||
isActive: isFavorite,
|
||||
title: "Favorite"
|
||||
},
|
||||
dislikeOptions: {
|
||||
isActive: !isFavorite,
|
||||
title: "Unfavorite"
|
||||
}
|
||||
});
|
||||
}
|
||||
return await TrackPlayer.updateOptions({
|
||||
progressUpdateEventInterval: 1,
|
||||
capabilities: CAPABILITIES,
|
||||
notificationCapabilities: CAPABILITIES,
|
||||
compactCapabilities: CAPABILITIES,
|
||||
ratingType: RatingType.Heart,
|
||||
likeOptions: {
|
||||
isActive: isFavorite,
|
||||
title: 'Favorite',
|
||||
},
|
||||
dislikeOptions: {
|
||||
isActive: !isFavorite,
|
||||
title: 'Unfavorite',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import { JellifyTrack } from "../types/JellifyTrack";
|
||||
import { QueuingType } from "../enums/queuing-type";
|
||||
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { Queue } from "./types/queue-item";
|
||||
import { JellifyTrack } from '../types/JellifyTrack'
|
||||
import { QueuingType } from '../enums/queuing-type'
|
||||
import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models'
|
||||
import { Queue } from './types/queue-item'
|
||||
|
||||
export interface QueueMutation {
|
||||
track: BaseItemDto;
|
||||
index?: number | undefined;
|
||||
tracklist: BaseItemDto[];
|
||||
queue: Queue;
|
||||
queuingType?: QueuingType | undefined;
|
||||
export interface QueueMutation {
|
||||
track: BaseItemDto
|
||||
index?: number | undefined
|
||||
tracklist: BaseItemDto[]
|
||||
queue: Queue
|
||||
queuingType?: QueuingType | undefined
|
||||
}
|
||||
|
||||
export interface AddToQueueMutation {
|
||||
track: BaseItemDto,
|
||||
queuingType?: QueuingType | undefined;
|
||||
track: BaseItemDto
|
||||
queuingType?: QueuingType | undefined
|
||||
}
|
||||
|
||||
export interface QueueOrderMutation {
|
||||
newOrder: JellifyTrack[];
|
||||
from: number;
|
||||
to: number;
|
||||
}
|
||||
newOrder: JellifyTrack[]
|
||||
from: number
|
||||
to: number
|
||||
}
|
||||
|
||||
1087
player/provider.tsx
1087
player/provider.tsx
File diff suppressed because it is too large
Load Diff
@@ -1,71 +1,66 @@
|
||||
import Client from "../api/client";
|
||||
import { JellifyTrack } from "../types/JellifyTrack";
|
||||
import { getUserLibraryApi } from "@jellyfin/sdk/lib/utils/api";
|
||||
import TrackPlayer, { Event, RatingType } from "react-native-track-player";
|
||||
import { getActiveTrack, getActiveTrackIndex } from "react-native-track-player/lib/src/trackPlayer";
|
||||
import Client from '../api/client'
|
||||
import { JellifyTrack } from '../types/JellifyTrack'
|
||||
import { getUserLibraryApi } from '@jellyfin/sdk/lib/utils/api'
|
||||
import TrackPlayer, { Event, RatingType } from 'react-native-track-player'
|
||||
import { getActiveTrack, getActiveTrackIndex } from 'react-native-track-player/lib/src/trackPlayer'
|
||||
|
||||
/**
|
||||
* Jellify Playback Service.
|
||||
*
|
||||
*
|
||||
* Sets up event listeners for remote control events and
|
||||
* runs for the duration of the app lifecycle
|
||||
*/
|
||||
export async function PlaybackService() {
|
||||
TrackPlayer.addEventListener(Event.RemotePlay, async () => {
|
||||
await TrackPlayer.play()
|
||||
})
|
||||
TrackPlayer.addEventListener(Event.RemotePause, async () => {
|
||||
await TrackPlayer.pause()
|
||||
})
|
||||
|
||||
TrackPlayer.addEventListener(Event.RemotePlay, async () => {
|
||||
await TrackPlayer.play();
|
||||
});
|
||||
TrackPlayer.addEventListener(Event.RemotePause, async () => {
|
||||
await TrackPlayer.pause();
|
||||
});
|
||||
TrackPlayer.addEventListener(Event.RemoteNext, async () => {
|
||||
await TrackPlayer.skipToNext()
|
||||
})
|
||||
|
||||
TrackPlayer.addEventListener(Event.RemoteNext, async () => {
|
||||
await TrackPlayer.skipToNext();
|
||||
});
|
||||
TrackPlayer.addEventListener(Event.RemotePrevious, async () => {
|
||||
await TrackPlayer.skipToPrevious()
|
||||
})
|
||||
|
||||
TrackPlayer.addEventListener(Event.RemotePrevious, async () => {
|
||||
await TrackPlayer.skipToPrevious();
|
||||
});
|
||||
TrackPlayer.addEventListener(Event.RemoteSeek, async (event) => {
|
||||
await TrackPlayer.seekTo(event.position)
|
||||
})
|
||||
|
||||
TrackPlayer.addEventListener(Event.RemoteSeek, async (event) => {
|
||||
await TrackPlayer.seekTo(event.position);
|
||||
});
|
||||
// TrackPlayer.addEventListener(Event.RemoteJumpForward, async (event) => {
|
||||
// await TrackPlayer.seekBy(event.interval)
|
||||
// });
|
||||
|
||||
// TrackPlayer.addEventListener(Event.RemoteJumpForward, async (event) => {
|
||||
// await TrackPlayer.seekBy(event.interval)
|
||||
// });
|
||||
// TrackPlayer.addEventListener(Event.RemoteJumpBackward, async (event) => {
|
||||
// await TrackPlayer.seekBy(-event.interval)
|
||||
// });
|
||||
|
||||
// TrackPlayer.addEventListener(Event.RemoteJumpBackward, async (event) => {
|
||||
// await TrackPlayer.seekBy(-event.interval)
|
||||
// });
|
||||
TrackPlayer.addEventListener(Event.RemoteLike, async () => {
|
||||
const nowPlaying = (await getActiveTrack()) as JellifyTrack
|
||||
const nowPlayingIndex = await getActiveTrackIndex()
|
||||
|
||||
TrackPlayer.addEventListener(Event.RemoteLike, async () => {
|
||||
await getUserLibraryApi(Client.api!).markFavoriteItem({
|
||||
itemId: nowPlaying.item.Id!,
|
||||
})
|
||||
|
||||
const nowPlaying = await getActiveTrack() as JellifyTrack;
|
||||
const nowPlayingIndex = await getActiveTrackIndex();
|
||||
await TrackPlayer.updateMetadataForTrack(nowPlayingIndex!, {
|
||||
rating: RatingType.Heart,
|
||||
})
|
||||
})
|
||||
|
||||
await getUserLibraryApi(Client.api!)
|
||||
.markFavoriteItem({
|
||||
itemId: nowPlaying.item.Id!
|
||||
});
|
||||
TrackPlayer.addEventListener(Event.RemoteDislike, async () => {
|
||||
const nowPlaying = (await getActiveTrack()) as JellifyTrack
|
||||
const nowPlayingIndex = await getActiveTrackIndex()
|
||||
|
||||
await TrackPlayer.updateMetadataForTrack(nowPlayingIndex!, {
|
||||
rating: RatingType.Heart
|
||||
})
|
||||
});
|
||||
await getUserLibraryApi(Client.api!).markFavoriteItem({
|
||||
itemId: nowPlaying.item.Id!,
|
||||
})
|
||||
|
||||
TrackPlayer.addEventListener(Event.RemoteDislike, async () => {
|
||||
|
||||
const nowPlaying = await getActiveTrack() as JellifyTrack;
|
||||
const nowPlayingIndex = await getActiveTrackIndex();
|
||||
|
||||
await getUserLibraryApi(Client.api!)
|
||||
.markFavoriteItem({
|
||||
itemId: nowPlaying.item.Id!
|
||||
});
|
||||
|
||||
await TrackPlayer.updateMetadataForTrack(nowPlayingIndex!, {
|
||||
rating: undefined
|
||||
});
|
||||
});
|
||||
}
|
||||
await TrackPlayer.updateMetadataForTrack(nowPlayingIndex!, {
|
||||
rating: undefined,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models'
|
||||
|
||||
export type Queue = BaseItemDto | "Recently Played" | "Search" | "Favorite Tracks";
|
||||
export type Queue = BaseItemDto | 'Recently Played' | 'Search' | 'Favorite Tracks'
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models"
|
||||
import { QueuingType } from "../../enums/queuing-type"
|
||||
import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models'
|
||||
import { QueuingType } from '../../enums/queuing-type'
|
||||
|
||||
export interface QueuingRequest {
|
||||
song: BaseItemDto
|
||||
queuingType: QueuingType
|
||||
atIndex?: number
|
||||
}
|
||||
song: BaseItemDto
|
||||
queuingType: QueuingType
|
||||
atIndex?: number
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
module.exports = {
|
||||
project: {
|
||||
ios: {},
|
||||
android: {},
|
||||
},
|
||||
assets: ['./assets/fonts/'],
|
||||
dependencies: {
|
||||
'react-native-carplay': {
|
||||
platforms: {
|
||||
android: null, // Disable autolinking for Android
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
project: {
|
||||
ios: {},
|
||||
android: {},
|
||||
},
|
||||
assets: ['./assets/fonts/'],
|
||||
dependencies: {
|
||||
'react-native-carplay': {
|
||||
platforms: {
|
||||
android: null, // Disable autolinking for Android
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:recommended"
|
||||
]
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": ["config:recommended"]
|
||||
}
|
||||
|
||||
@@ -3,66 +3,66 @@ import { createTamagui, createTokens } from 'tamagui' // or '@tamagui/core'
|
||||
import { headingFont, bodyFont } from './fonts.config'
|
||||
|
||||
const tokens = createTokens({
|
||||
...TamaguiTokens,
|
||||
color: {
|
||||
danger: "#ff0000",
|
||||
purpleDark: "#0C0622",
|
||||
purple: "#100538",
|
||||
purpleGray: "#66617B",
|
||||
amethyst: "#7E72AF",
|
||||
grape: "#5638BB",
|
||||
telemagenta: "#cc2f71",
|
||||
white: "#ffffff",
|
||||
black: "#000000"
|
||||
},
|
||||
...TamaguiTokens,
|
||||
color: {
|
||||
danger: '#ff0000',
|
||||
purpleDark: '#0C0622',
|
||||
purple: '#100538',
|
||||
purpleGray: '#66617B',
|
||||
amethyst: '#7E72AF',
|
||||
grape: '#5638BB',
|
||||
telemagenta: '#cc2f71',
|
||||
white: '#ffffff',
|
||||
black: '#000000',
|
||||
},
|
||||
})
|
||||
|
||||
const jellifyConfig = createTamagui({
|
||||
animations,
|
||||
fonts:{
|
||||
heading: headingFont,
|
||||
body: bodyFont,
|
||||
},
|
||||
media,
|
||||
shorthands,
|
||||
tokens,
|
||||
themes: {
|
||||
dark: {
|
||||
shadowColor: tokens.color.purple,
|
||||
background: tokens.color.purpleDark,
|
||||
backgroundActive: tokens.color.amethyst,
|
||||
backgroundPress: tokens.color.amethyst,
|
||||
backgroundFocus: tokens.color.amethyst,
|
||||
backgroundHover: tokens.color.purpleGray,
|
||||
borderColor: tokens.color.amethyst,
|
||||
color: tokens.color.white
|
||||
},
|
||||
dark_inverted_purple: {
|
||||
color: tokens.color.purpleDark,
|
||||
borderColor: tokens.color.amethyst,
|
||||
background: tokens.color.amethyst
|
||||
},
|
||||
light: {
|
||||
background: tokens.color.white,
|
||||
backgroundActive: tokens.color.amethyst,
|
||||
borderColor: tokens.color.purpleGray,
|
||||
color: tokens.color.purpleDark
|
||||
},
|
||||
light_inverted_purple: {
|
||||
color: tokens.color.purpleDark,
|
||||
borderColor: tokens.color.purpleDark,
|
||||
background: tokens.color.purpleGray
|
||||
}
|
||||
}
|
||||
});
|
||||
animations,
|
||||
fonts: {
|
||||
heading: headingFont,
|
||||
body: bodyFont,
|
||||
},
|
||||
media,
|
||||
shorthands,
|
||||
tokens,
|
||||
themes: {
|
||||
dark: {
|
||||
shadowColor: tokens.color.purple,
|
||||
background: tokens.color.purpleDark,
|
||||
backgroundActive: tokens.color.amethyst,
|
||||
backgroundPress: tokens.color.amethyst,
|
||||
backgroundFocus: tokens.color.amethyst,
|
||||
backgroundHover: tokens.color.purpleGray,
|
||||
borderColor: tokens.color.amethyst,
|
||||
color: tokens.color.white,
|
||||
},
|
||||
dark_inverted_purple: {
|
||||
color: tokens.color.purpleDark,
|
||||
borderColor: tokens.color.amethyst,
|
||||
background: tokens.color.amethyst,
|
||||
},
|
||||
light: {
|
||||
background: tokens.color.white,
|
||||
backgroundActive: tokens.color.amethyst,
|
||||
borderColor: tokens.color.purpleGray,
|
||||
color: tokens.color.purpleDark,
|
||||
},
|
||||
light_inverted_purple: {
|
||||
color: tokens.color.purpleDark,
|
||||
borderColor: tokens.color.purpleDark,
|
||||
background: tokens.color.purpleGray,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export type JellifyConfig = typeof jellifyConfig
|
||||
|
||||
declare module 'tamagui' {
|
||||
// or '@tamagui/core'
|
||||
// overrides TamaguiCustomConfig so your custom types
|
||||
// work everywhere you import `tamagui`
|
||||
interface TamaguiCustomConfig extends JellifyConfig {}
|
||||
// or '@tamagui/core'
|
||||
// overrides TamaguiCustomConfig so your custom types
|
||||
// work everywhere you import `tamagui`
|
||||
interface TamaguiCustomConfig extends JellifyConfig {}
|
||||
}
|
||||
|
||||
export default jellifyConfig
|
||||
export default jellifyConfig
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
{
|
||||
"extends": "@react-native/typescript-config/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./*"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
"extends": "@react-native/typescript-config/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
|
||||
export interface JellifyLibrary {
|
||||
musicLibraryId: string;
|
||||
musicLibraryName?: string | undefined;
|
||||
musicLibraryPrimaryImageId?: string | undefined;
|
||||
playlistLibraryId?: string | undefined;
|
||||
playlistLibraryPrimaryImageId?: string | undefined;
|
||||
}
|
||||
musicLibraryId: string
|
||||
musicLibraryName?: string | undefined
|
||||
musicLibraryPrimaryImageId?: string | undefined
|
||||
playlistLibraryId?: string | undefined
|
||||
playlistLibraryPrimaryImageId?: string | undefined
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export interface JellifyServer {
|
||||
url: string;
|
||||
address: string;
|
||||
name: string;
|
||||
version: string;
|
||||
startUpComplete: boolean;
|
||||
}
|
||||
url: string
|
||||
address: string
|
||||
name: string
|
||||
version: string
|
||||
startUpComplete: boolean
|
||||
}
|
||||
|
||||
@@ -1,32 +1,34 @@
|
||||
import { PitchAlgorithm, RatingType, Track, TrackType } from "react-native-track-player"
|
||||
import { QueuingType } from "../enums/queuing-type";
|
||||
import { BaseItemDto } from "@jellyfin/sdk/lib/generated-client/models";
|
||||
import { PitchAlgorithm, RatingType, Track, TrackType } from 'react-native-track-player'
|
||||
import { QueuingType } from '../enums/queuing-type'
|
||||
import { BaseItemDto } from '@jellyfin/sdk/lib/generated-client/models'
|
||||
|
||||
export interface JellifyTrack extends Track {
|
||||
url: string;
|
||||
type?: TrackType | undefined;
|
||||
userAgent?: string | undefined;
|
||||
contentType?: string | undefined;
|
||||
pitchAlgorithm?: PitchAlgorithm | undefined;
|
||||
headers?: { [key: string]: any; } | undefined;
|
||||
url: string
|
||||
type?: TrackType | undefined
|
||||
userAgent?: string | undefined
|
||||
contentType?: string | undefined
|
||||
pitchAlgorithm?: PitchAlgorithm | undefined
|
||||
|
||||
title?: string | undefined;
|
||||
album?: string | undefined;
|
||||
artist?: string | undefined;
|
||||
duration?: number | undefined;
|
||||
artwork?: string | undefined;
|
||||
description?: string | undefined;
|
||||
genre?: string | undefined;
|
||||
date?: string | undefined;
|
||||
rating?: RatingType | undefined;
|
||||
isLiveStream?: boolean | undefined;
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
headers?: { [key: string]: any } | undefined
|
||||
|
||||
item: BaseItemDto;
|
||||
title?: string | undefined
|
||||
album?: string | undefined
|
||||
artist?: string | undefined
|
||||
duration?: number | undefined
|
||||
artwork?: string | undefined
|
||||
description?: string | undefined
|
||||
genre?: string | undefined
|
||||
date?: string | undefined
|
||||
rating?: RatingType | undefined
|
||||
isLiveStream?: boolean | undefined
|
||||
|
||||
/**
|
||||
* Represents the type of queuing for this song, be it that it was
|
||||
* queued from the selection chosen, queued by the user directly, or marked
|
||||
* to play next by the user
|
||||
*/
|
||||
QueuingType?: QueuingType | undefined ;
|
||||
}
|
||||
item: BaseItemDto
|
||||
|
||||
/**
|
||||
* Represents the type of queuing for this song, be it that it was
|
||||
* queued from the selection chosen, queued by the user directly, or marked
|
||||
* to play next by the user
|
||||
*/
|
||||
QueuingType?: QueuingType | undefined
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export interface JellifyUser {
|
||||
id: string;
|
||||
name: string;
|
||||
accessToken: string;
|
||||
}
|
||||
id: string
|
||||
name: string
|
||||
accessToken: string
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user