Fix Android Crash When Opening Player (#320)

Adds arithmetic rounding in scrubber for player so that we don't experience loss of precision runtime errors when opening the player or seeking in the track
This commit is contained in:
Violet Caulfield
2025-05-01 16:35:32 -05:00
committed by GitHub
parent b8dc9467e3
commit 67d59db7b6
18 changed files with 853 additions and 426 deletions

1
.yarnrc.yml Normal file
View File

@@ -0,0 +1 @@
checksumBehavior: "update"

View File

@@ -1370,30 +1370,6 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- react-native-blurhash (2.1.1):
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2024.11.18.00)
- RCTRequired
- RCTTypeSafety
- React-Core
- React-debug
- React-Fabric
- React-featureflags
- React-graphics
- React-hermes
- React-ImageManager
- React-jsi
- React-NativeModulesApple
- React-RCTFabric
- React-renderercss
- React-rendererdebug
- React-utils
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- react-native-carplay (2.4.1-beta.0):
- React
- react-native-mmkv (3.2.0):
@@ -2178,7 +2154,6 @@ DEPENDENCIES:
- React-microtasksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`)
- react-native-background-actions (from `../node_modules/react-native-background-actions`)
- "react-native-blur (from `../node_modules/@react-native-community/blur`)"
- react-native-blurhash (from `../node_modules/react-native-blurhash`)
- react-native-carplay (from `../node_modules/react-native-carplay`)
- react-native-mmkv (from `../node_modules/react-native-mmkv`)
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
@@ -2317,8 +2292,6 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-background-actions"
react-native-blur:
:path: "../node_modules/@react-native-community/blur"
react-native-blurhash:
:path: "../node_modules/react-native-blurhash"
react-native-carplay:
:path: "../node_modules/react-native-carplay"
react-native-mmkv:
@@ -2456,7 +2429,6 @@ SPEC CHECKSUMS:
React-microtasksnativemodule: c8ed30f8ec30affbc73411c54207bd67b1125bbb
react-native-background-actions: 48e6bad9e2a47e3b04858634c5a05ea11062f680
react-native-blur: 06d0f9906ecd6cde3a42de16c6cd829a2bf0710c
react-native-blurhash: 773f3726f5932f05917a3f4828648f155f087c7d
react-native-carplay: 8f388f6f73e5e0f73ed154ad8794371343ee20c0
react-native-mmkv: d3cc73d2554fafa20dc5b86386359034d1faf8ff
react-native-netinfo: cec9c4e86083cb5b6aba0e0711f563e2fbbff187

View File

@@ -1,120 +1,120 @@
{
"name": "jellify",
"version": "0.11.20",
"private": true,
"scripts": {
"init-android": "yarn",
"init-ios": "yarn init-ios:new-arch",
"init-ios:new-arch": "yarn && yarn pod:install:new-arch",
"reinstall": "rm -rf ./node_modules && yarn install",
"android": "react-native run-android",
"ios": "react-native run-ios",
"lint": "eslint .",
"start": "react-native start",
"test": "jest",
"tsc": "tsc",
"clean:ios": "cd ios && pod deintegrate",
"clean:android": "cd android && rm -rf app/ build/",
"pod:install": "echo 'Please run `yarn pod:install:new-arch` to enable the new architecture'",
"pod:install:new-arch": "cd ios && bundle install && RCT_NEW_ARCH_ENABLED=1 bundle exec pod install",
"pod:clean": "cd ios && pod deintegrate",
"fastlane:ios:build": "cd ios && bundle exec fastlane build",
"fastlane:ios:match": "cd ios && bundle exec fastlane match development",
"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",
"format:check": "prettier --check .",
"format": "prettier --write .",
"postinstall": "patch-package"
},
"dependencies": {
"@jellyfin/sdk": "^0.11.0",
"@react-native-community/blur": "^4.4.1",
"@react-native-community/cli": "^18.0.0",
"@react-native-community/netinfo": "^11.4.1",
"@react-native-masked-view/masked-view": "^0.3.2",
"@react-navigation/bottom-tabs": "^7.3.10",
"@react-navigation/material-top-tabs": "^7.2.10",
"@react-navigation/native": "^7.1.6",
"@react-navigation/native-stack": "^7.3.10",
"@react-navigation/stack": "^7.2.10",
"@tamagui/config": "^1.126.4",
"@tanstack/query-sync-storage-persister": "^5.74.6",
"@tanstack/react-query": "^5.74.4",
"@tanstack/react-query-persist-client": "^5.74.6",
"@testing-library/react-native": "^13.2.0",
"axios": "^1.8.4",
"bundle": "^2.1.0",
"gem": "^2.4.3",
"invert-color": "^2.0.0",
"lodash": "^4.17.21",
"react": "19.0.0",
"react-freeze": "^1.0.4",
"react-native": "0.79.1",
"react-native-background-actions": "^4.0.1",
"react-native-carplay": "^2.4.1-beta.0",
"react-native-device-info": "^14.0.4",
"react-native-draggable-flatlist": "^4.0.2",
"react-native-fast-image": "^8.6.3",
"react-native-fs": "^2.20.0",
"react-native-gesture-handler": "^2.25.0",
"react-native-haptic-feedback": "^2.3.3",
"react-native-mmkv": "3.2.0",
"react-native-pager-view": "^6.7.1",
"react-native-reanimated": "^3.17.5",
"react-native-safe-area-context": "^5.4.0",
"react-native-screens": "^4.11.0-beta.2",
"react-native-swipeable-item": "^2.0.9",
"react-native-text-ticker": "^1.14.0",
"react-native-toast-message": "^2.3.0",
"react-native-track-player": "git+https://github.com/riteshshukla04/react-native-track-player#APM",
"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.126.4"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@babel/preset-env": "^7.25.3",
"@babel/runtime": "^7.25.0",
"@react-native-community/cli-platform-android": "18.0.0",
"@react-native-community/cli-platform-ios": "18.0.0",
"@react-native/babel-preset": "0.79.1",
"@react-native/eslint-config": "0.79.1",
"@react-native/metro-config": "0.79.1",
"@react-native/typescript-config": "0.79.1",
"@types/jest": "^29.5.13",
"@types/lodash": "^4.17.10",
"@types/react": "^19.1.2",
"@types/react-native-vector-icons": "^6.4.18",
"@types/react-test-renderer": "19.0.0",
"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",
"patch-package": "8.0.0",
"prettier": "^2.8.8",
"react-dom": "^19.1.0",
"react-native-cli-bump-version": "^1.5.1",
"react-test-renderer": "19.0.0",
"typescript": "5.8.3"
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"prettier --write",
"eslint --fix"
]
},
"engines": {
"node": ">=18"
}
}
"name": "jellify",
"version": "0.11.20",
"private": true,
"scripts": {
"init-android": "yarn",
"init-ios": "yarn init-ios:new-arch",
"init-ios:new-arch": "yarn && yarn pod:install:new-arch",
"reinstall": "rm -rf ./node_modules && yarn install",
"android": "react-native run-android",
"ios": "react-native run-ios",
"lint": "eslint .",
"start": "react-native start",
"test": "jest",
"tsc": "tsc",
"clean:ios": "cd ios && pod deintegrate",
"clean:android": "cd android && rm -rf app/ build/",
"pod:install": "echo 'Please run `yarn pod:install:new-arch` to enable the new architecture'",
"pod:install:new-arch": "cd ios && bundle install && RCT_NEW_ARCH_ENABLED=1 bundle exec pod install",
"pod:clean": "cd ios && pod deintegrate",
"fastlane:ios:build": "cd ios && bundle exec fastlane build",
"fastlane:ios:match": "cd ios && bundle exec fastlane match development",
"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",
"format:check": "prettier --check .",
"format": "prettier --write .",
"postinstall": "patch-package"
},
"dependencies": {
"@jellyfin/sdk": "^0.11.0",
"@react-native-community/blur": "^4.4.1",
"@react-native-community/cli": "^18.0.0",
"@react-native-community/netinfo": "^11.4.1",
"@react-native-masked-view/masked-view": "^0.3.2",
"@react-navigation/bottom-tabs": "^7.3.10",
"@react-navigation/material-top-tabs": "^7.2.10",
"@react-navigation/native": "^7.1.6",
"@react-navigation/native-stack": "^7.3.10",
"@react-navigation/stack": "^7.2.10",
"@tamagui/config": "^1.126.4",
"@tanstack/query-sync-storage-persister": "^5.74.6",
"@tanstack/react-query": "^5.74.4",
"@tanstack/react-query-persist-client": "^5.74.6",
"@testing-library/react-native": "^13.2.0",
"axios": "^1.8.4",
"bundle": "^2.1.0",
"gem": "^2.4.3",
"invert-color": "^2.0.0",
"lodash": "^4.17.21",
"react": "19.0.0",
"react-freeze": "^1.0.4",
"react-native": "0.79.1",
"react-native-background-actions": "^4.0.1",
"react-native-carplay": "^2.4.1-beta.0",
"react-native-device-info": "^14.0.4",
"react-native-draggable-flatlist": "^4.0.2",
"react-native-fast-image": "^8.6.3",
"react-native-fs": "^2.20.0",
"react-native-gesture-handler": "^2.25.0",
"react-native-haptic-feedback": "^2.3.3",
"react-native-mmkv": "3.2.0",
"react-native-pager-view": "^6.7.1",
"react-native-reanimated": "^3.17.5",
"react-native-safe-area-context": "^5.4.0",
"react-native-screens": "^4.11.0-beta.2",
"react-native-swipeable-item": "^2.0.9",
"react-native-text-ticker": "^1.14.0",
"react-native-toast-message": "^2.3.0",
"react-native-track-player": "git+https://github.com/riteshshukla04/react-native-track-player#APM",
"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.126.4"
},
"devDependencies": {
"@babel/core": "^7.27.1",
"@babel/preset-env": "^7.27.1",
"@babel/runtime": "^7.27.1",
"@react-native-community/cli-platform-android": "18.0.0",
"@react-native-community/cli-platform-ios": "18.0.0",
"@react-native/babel-preset": "0.79.1",
"@react-native/eslint-config": "0.79.1",
"@react-native/metro-config": "0.79.1",
"@react-native/typescript-config": "0.79.1",
"@types/jest": "^29.5.13",
"@types/lodash": "^4.17.10",
"@types/react": "^19.1.2",
"@types/react-native-vector-icons": "^6.4.18",
"@types/react-test-renderer": "19.0.0",
"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.1",
"patch-package": "8.0.0",
"prettier": "^3.5.3",
"react-dom": "^19.0.0",
"react-native-cli-bump-version": "^1.5.1",
"react-test-renderer": "19.0.0",
"typescript": "5.8.3"
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"prettier --write",
"eslint --fix"
]
},
"engines": {
"node": ">=18"
}
}

View File

@@ -48,12 +48,12 @@ export async function fetchAlbumDiscs(
? isEqual(
discNumber,
(track.ParentIndexNumber ?? 0).toString(),
)
)
: track,
),
}
},
)
)
: [{ title: '1', data: [] }]
resolve(discs)

View File

@@ -41,7 +41,7 @@ export default function Albums({
convertRunTimeTicksToSeconds(album.RunTimeTicks ?? 0) /
60 <=
30)),
)
)
: []
}
numColumns={2} // TODO: Make this adjustable

View File

@@ -30,7 +30,9 @@ export default function RecentlyAdded({
<HorizontalCardList
squared
data={recentlyAdded?.length ?? 0 > 10 ? recentlyAdded!.slice(0, 10) : recentlyAdded}
data={
(recentlyAdded?.length ?? 0 > 10) ? recentlyAdded!.slice(0, 10) : recentlyAdded
}
renderItem={({ item }) => (
<ItemCard
caption={item.Name}

View File

@@ -57,6 +57,6 @@ export function isFavoriteItem(item: BaseItemDto): boolean {
return isUndefined(item.UserData)
? false
: isUndefined(item.UserData.IsFavorite)
? false
: item.UserData.IsFavorite
? false
: item.UserData.IsFavorite
}

View File

@@ -100,7 +100,7 @@ export default function Track({
item: track,
isNested: isNested,
})
}
}
}
paddingVertical={'$2'}
>
@@ -142,10 +142,10 @@ export default function Track({
isPlaying
? getTokens().color.telemagenta
: isOffline
? isDownloaded
? theme.color
: '$purpleGray'
: theme.color
? isDownloaded
? theme.color
: '$purpleGray'
: theme.color
}
lineBreakStrategyIOS='standard'
numberOfLines={1}

View File

@@ -32,7 +32,7 @@ export default function FrequentArtists({
<HorizontalCardList
data={
frequentArtists?.length ?? 0 > 10
(frequentArtists?.length ?? 0 > 10)
? frequentArtists!.slice(0, 10)
: frequentArtists
}

View File

@@ -38,7 +38,7 @@ export default function FrequentlyPlayedTracks({
<HorizontalCardList
data={
frequentlyPlayed?.length ?? 0 > 10
(frequentlyPlayed?.length ?? 0 > 10)
? frequentlyPlayed!.slice(0, 10)
: frequentlyPlayed
}

View File

@@ -30,7 +30,9 @@ export default function RecentArtists({
</XStack>
<HorizontalCardList
data={recentArtists?.length ?? 0 > 10 ? recentArtists!.slice(0, 10) : recentArtists}
data={
(recentArtists?.length ?? 0 > 10) ? recentArtists!.slice(0, 10) : recentArtists
}
renderItem={({ item: recentArtist }) => (
<ItemCard
item={recentArtist}

View File

@@ -40,7 +40,7 @@ export default function RecentlyPlayed({
<HorizontalCardList
squared
data={
recentTracks?.length ?? 0 > 10 ? recentTracks!.slice(0, 10) : recentTracks
(recentTracks?.length ?? 0 > 10) ? recentTracks!.slice(0, 10) : recentTracks
}
renderItem={({ index, item: recentlyPlayedTrack }) => (
<ItemCard

View File

@@ -21,8 +21,8 @@ export default function Login(): React.JSX.Element {
isUndefined(server)
? 'ServerAddress'
: isUndefined(user)
? 'ServerAuthentication'
: 'LibrarySelection'
? 'ServerAuthentication'
: 'LibrarySelection'
}
screenOptions={{ headerShown: false }}
>

View File

@@ -49,7 +49,7 @@ export default function Scrubber(): React.JSX.Element {
value={position}
max={
progress && progress.duration > 0
? progress.duration * ProgressMultiplier
? Math.floor(progress.duration * ProgressMultiplier)
: 1
}
width={getToken('$20') + getToken('$20')}
@@ -72,11 +72,11 @@ export default function Scrubber(): React.JSX.Element {
Math.floor(value / ProgressMultiplier) > -1 &&
Math.floor(value / ProgressMultiplier) < progress.duration
)
setPosition(value)
setPosition(Math.floor(value))
},
onSlideEnd: (event, value) => {
trigger('notificationSuccess')
setPosition(value)
setPosition(Math.floor(value))
useSeekTo.mutate(Math.floor(value / ProgressMultiplier))
setSeeking(false)
},

View File

@@ -75,7 +75,7 @@ export default function PlayerScreen({
{
// If the Queue is a BaseItemDto, display the name of it
typeof queueRef === 'object'
? queueRef.Name ?? 'Untitled'
? (queueRef.Name ?? 'Untitled')
: queueRef
}
</Text>

View File

@@ -29,8 +29,8 @@ export default function TracksScreen({ route, navigation }: TracksProps): React.
route.params.tracks
? route.params.tracks.slice(index, index + 50)
: favoriteTracks
? favoriteTracks.slice(index, index + 50)
: []
? favoriteTracks.slice(index, index + 50)
: []
}
queue={route.params.queue}
/>

View File

@@ -89,7 +89,7 @@ export function mapDtoToTrack(
? getImageApi(Client.api!).getItemImageUrlById(item.AlbumId, ImageType.Primary, {
width: 300,
height: 300,
})
})
: undefined,
rating: isFavorite ? RatingType.Heart : undefined,

960
yarn.lock

File diff suppressed because it is too large Load Diff