-
Notifications
You must be signed in to change notification settings - Fork 7
RG-T117 Fixing Expo 56 AV issue #261
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,148 +1,61 @@ | ||
| # Audio Stream Store Refactoring | ||
| # Audio Stream Store | ||
|
|
||
| ## Overview | ||
|
|
||
| The audio stream store has been refactored to use `expo-av` instead of `expo-audio` to resolve issues with playing remote MP3 streams over the internet in the new Expo architecture. | ||
| The audio stream store uses `expo-audio`, the audio package supported by the current Expo SDK. Remote streams are created with `createAudioPlayer()` and their loading, buffering, playback, completion, and error states are observed through `playbackStatusUpdate` events. | ||
|
|
||
| ## Key Changes | ||
| ## Player lifecycle | ||
|
|
||
| ### 1. Replaced expo-audio with expo-av | ||
|
|
||
| **Before:** | ||
| ```typescript | ||
| import { type AudioPlayer, createAudioPlayer } from 'expo-audio'; | ||
| ``` | ||
|
|
||
| **After:** | ||
| ```typescript | ||
| import { Audio, type AVPlaybackSource, type AVPlaybackStatus } from 'expo-av'; | ||
| ``` | ||
|
|
||
| ### 2. Updated Audio Player Management | ||
|
|
||
| **Before:** | ||
| - Used `createAudioPlayer()` function | ||
| - Audio player instance stored as `AudioPlayer` | ||
|
|
||
| **After:** | ||
| - Uses `Audio.Sound.createAsync()` method | ||
| - Audio player instance stored as `Audio.Sound` | ||
| import { createAudioPlayer, setAudioModeAsync, type AudioPlayer } from 'expo-audio'; | ||
|
|
||
| await setAudioModeAsync({ | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OS-level external call Kody rule violation: Add try-catch blocks for external calls Prompt for LLMTalk to Kody by mentioning @kody Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction. |
||
| allowsRecording: false, | ||
| shouldPlayInBackground: true, | ||
| playsInSilentMode: true, | ||
| interruptionMode: 'duckOthers', | ||
| shouldRouteThroughEarpiece: false, | ||
| }); | ||
|
|
||
| ### 3. Enhanced Audio Configuration | ||
| const streamUrl = stream?.Url?.trim(); | ||
| if (!streamUrl) { | ||
| throw new Error('Audio stream URL is required'); | ||
| } | ||
|
|
||
| Added proper audio mode configuration for streaming: | ||
| const player: AudioPlayer = createAudioPlayer(streamUrl, { | ||
| updateInterval: 1000, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Magic number identified where the numeric literal Kody rule violation: Replace magic numbers with named constants Prompt for LLMTalk to Kody by mentioning @kody Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction. |
||
| keepAudioSessionActive: true, | ||
| preferredForwardBufferDuration: 5, | ||
| }); | ||
|
|
||
| ```typescript | ||
| await Audio.setAudioModeAsync({ | ||
| allowsRecordingIOS: false, | ||
| staysActiveInBackground: true, | ||
| playsInSilentModeIOS: true, | ||
| shouldDuckAndroid: true, | ||
| playThroughEarpieceAndroid: false, | ||
| player.addListener('playbackStatusUpdate', (status) => { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing error handler and indeterminate cleanup path identified for the Kody rule violation: Provide error handlers to subscription/listener APIs Prompt for LLMTalk to Kody by mentioning @kody Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Memory leak risk identified where the event listener registered via Kody rule violation: Proper memory management in event listeners Prompt for LLMTalk to Kody by mentioning @kody Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction. |
||
| // Synchronize playback and buffering state and handle status.error. | ||
| }); | ||
| player.play(); | ||
| ``` | ||
|
|
||
| ### 4. Improved State Management | ||
| Call `player.pause()` followed by `player.remove()` when replacing or stopping a stream. `remove()` releases the native player and its listeners. | ||
|
|
||
| Added new state properties for better stream status tracking: | ||
| ## State | ||
|
|
||
| ```typescript | ||
| interface AudioStreamState { | ||
| // ... existing properties | ||
| isLoading: boolean; // Track loading state | ||
| isBuffering: boolean; // Track buffering state | ||
| soundObject: Audio.Sound | null; // Sound instance | ||
| } | ||
| ``` | ||
|
|
||
| ### 5. Better Error Handling | ||
| `useAudioStreamStore` exposes the available streams, the current stream and player, loading and buffering flags, and the `playStream`, `stopStream`, and `cleanup` operations. The public store API remains stable for UI consumers. | ||
|
|
||
| Enhanced error handling with proper cleanup and status updates. | ||
| ## Dependencies | ||
|
|
||
| ## Installation | ||
|
|
||
| Make sure you have `expo-av` installed: | ||
| Use Expo's SDK-aware installer when changing audio dependencies: | ||
|
|
||
| ```bash | ||
| yarn add expo-av | ||
| ``` | ||
|
|
||
| ## Usage Example | ||
|
|
||
| ```typescript | ||
| import { useAudioStreamStore } from '@/stores/app/audio-stream-store'; | ||
|
|
||
| const MyComponent = () => { | ||
| const { | ||
| availableStreams, | ||
| isLoadingStreams, | ||
| currentStream, | ||
| isPlaying, | ||
| isLoading, | ||
| isBuffering, | ||
| fetchAvailableStreams, | ||
| playStream, | ||
| stopStream, | ||
| } = useAudioStreamStore(); | ||
|
|
||
| useEffect(() => { | ||
| fetchAvailableStreams(); | ||
| }, []); | ||
|
|
||
| const handlePlay = async (stream) => { | ||
| try { | ||
| await playStream(stream); | ||
| } catch (error) { | ||
| console.error('Failed to play stream:', error); | ||
| } | ||
| }; | ||
|
|
||
| // ... render logic | ||
| }; | ||
| yarn expo install expo-audio | ||
| ``` | ||
|
|
||
| ## Benefits | ||
|
|
||
| 1. **Better Remote Streaming Support**: `expo-av` provides more robust support for remote MP3 streams | ||
| 2. **Improved Audio Configuration**: Proper audio mode settings for background playback and silent mode | ||
| 3. **Enhanced Error Handling**: Better error recovery and cleanup | ||
| 4. **Loading States**: More granular loading and buffering states for better UX | ||
| 5. **Memory Management**: Proper cleanup of audio resources | ||
| Run `yarn expo install --check` after dependency changes to ensure every native package matches the installed Expo SDK. | ||
|
|
||
| ## Migration Notes | ||
| ## Configuration | ||
|
|
||
| If you were using the previous audio stream store: | ||
|
|
||
| 1. Replace any direct `audioPlayer` references with `soundObject` | ||
| 2. Update any custom audio handling code to use `expo-av` APIs | ||
| 3. The store API remains largely the same, so most usage code should work without changes | ||
| The app config enables background audio and declares microphone permissions for PTT and LiveKit calls. Runtime microphone permissions are checked without activating a competing audio session so permission handling does not race LiveKit or CallKeep. | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| ### Common Issues | ||
|
|
||
| 1. **Audio not playing on iOS in silent mode**: Make sure `playsInSilentModeIOS: true` is set | ||
| 2. **Buffering issues**: The store now properly tracks buffering state - use `isBuffering` to show loading indicators | ||
| 3. **Background playback**: Ensure your app has proper background audio permissions configured | ||
|
|
||
| ### Audio Permissions | ||
|
|
||
| Make sure your app's configuration includes proper audio permissions: | ||
|
|
||
| **app.json/app.config.js:** | ||
| ```json | ||
| { | ||
| "expo": { | ||
| "ios": { | ||
| "infoPlist": { | ||
| "UIBackgroundModes": ["audio"] | ||
| } | ||
| }, | ||
| "android": { | ||
| "permissions": [ | ||
| "android.permission.RECORD_AUDIO" | ||
| ] | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| 1. For silent-mode playback, verify `playsInSilentMode: true`. | ||
| 2. For remote stream stalls, inspect the `isBuffering` and `error` fields delivered by `playbackStatusUpdate`. | ||
| 3. For background playback, verify the platform background-audio configuration and keep the player audio session active. | ||
|
Comment on lines
+53
to
+61
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== file context =="
if [ -f docs/audio-stream-refactoring.md ]; then
nl -ba docs/audio-stream-refactoring.md | sed -n '1,120p'
else
echo "docs/audio-stream-refactoring.md not found"
fi
echo
echo "== mentions of audio APIs and background config =="
rg -n "shouldPlayInBackground|keepAudioSessionActive|setActiveForLockScreen|interruptionMode|doNotMix|playsInSilentMode|background audio|playbackStatusUpdate|isBuffering" -S .Repository: Resgrid/Unit Length of output: 209 🌐 Web query:
💡 Result: The issue where audio playback stops after approximately three minutes on Android when the app is in the background is a known Android OS limitation that is addressed by enabling lock screen controls in Expo [1][2]. To achieve sustained background audio playback in Expo SDK 56 and later, you must explicitly call the setActiveForLockScreen method [1][3]. According to the official Expo documentation, if this is not configured, the Android operating system will terminate the audio playback after roughly three minutes to optimize battery usage [1][4]. Required configuration steps: 1. Enable Lock Screen Controls: You must use the setActiveForLockScreen method provided by the expo-audio library to enable media controls on the lock screen [1][2]. This signals to the Android OS that the app is actively performing media playback, preventing the system from killing the background task [1]. 2. Configure the Config Plugin: Ensure that the Expo audio config plugin is correctly set up in your app configuration (e.g., app.json or app.config.js) [1][4]. This plugin is responsible for adding the necessary permissions, such as FOREGROUND_SERVICE and FOREGROUND_SERVICE_MEDIA_PLAYBACK, and declaring the media playback foreground service (AudioControlsService) in your AndroidManifest.xml [2]. 3. Background Playback Settings: Ensure that shouldPlayInBackground is set to true in your audio mode configuration [1][2]. While setting shouldPlayInBackground and using the correct interruptionMode (such as mixWithOthers) are important for overall audio behavior, they are insufficient on their own to prevent the three-minute timeout on Android [1][5]. The foreground service enabled via the config plugin, combined with the activation of lock screen controls, is mandatory for indefinite background playback [1][2]. Citations:
🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== docs file lines =="
if [ -f docs/audio-stream-refactoring.md ]; then
awk '{printf "%6d %s\n", NR, $0}' docs/audio-stream-refactoring.md | sed -n '1,120p'
else
echo "docs/audio-stream-refactoring.md not found"
fi
echo
echo "== audio API mentions =="
rg -n "shouldPlayInBackground|keepAudioSessionActive|setActiveForLockScreen|interruptionMode|doNotMix|playsInSilentMode|background audio|playbackStatusUpdate|isBuffering" -S . || trueRepository: Resgrid/Unit Length of output: 10133 🌐 Web query:
💡 Result: When using setActiveForLockScreen in Expo SDK 56 (and other versions using expo-audio), you must configure your audio mode to use interruptionMode: 'doNotMix' via setAudioModeAsync [1][2][3]. The 'doNotMix' mode is a strict requirement for lock screen controls to function correctly because the operating system requires an exclusive audio focus to associate lock screen metadata and controls with your player [1][4][5]. If this mode is not set, the OS may fail to display your metadata or controls [1][2][4]. Key implementation details: 1. Configuration: Always set the audio mode before or when initializing your player [1][6]. setAudioModeAsync({ playsInSilentMode: true, shouldPlayInBackground: true, interruptionMode: 'doNotMix', }); 2. Activation: Use setActiveForLockScreen(true, metadata, options) to enable the controls [1][7]. 3. Android Background Playback: Enabling lock screen controls is essential for sustained background playback on Android; otherwise, the OS may terminate playback after approximately 3 minutes due to background limitations [1][8][7]. For more information, refer to the official Expo Audio documentation [1][2]. Citations:
Document the Android lock-screen requirement.
🧰 Tools🪛 LanguageTool[style] ~56-~56: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym. (ENGLISH_WORD_REPEAT_BEGINNING_RULE) 🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -239,6 +239,16 @@ jest.mock('nativewind', () => ({ | |
| __esModule: true, | ||
| })); | ||
|
|
||
| // Avoid loading expo-video's native module in component tests. | ||
| jest.mock('expo-video', () => ({ | ||
| VideoView: 'VideoView', | ||
| useVideoPlayer: jest.fn(() => ({ | ||
| play: jest.fn(), | ||
| pause: jest.fn(), | ||
| addListener: jest.fn(() => ({ remove: jest.fn() })), | ||
| })), | ||
|
Comment on lines
+243
to
+249
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
curl -fsSL 'https://docs.expo.dev/versions/v56.0.0/sdk/video/' \
| rg -n 'useVideoPlayer\(source, setup\)|setup.*created'
rg -n -U -C 4 'useVideoPlayer\(' src --glob '*.{ts,tsx}'Repository: Resgrid/Unit Length of output: 50369 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
echo "== jest-setup useVideoPlayer mock =="
sed -n '230,255p' jest-setup.ts
echo
echo "== useVideoPlayer calls =="
rg -n -U -C 3 'useVideoPlayer\(' src --glob '*.{ts,tsx}' || true
echo
echo "== NativeVideoPlayer references =="
rg -n -U -C 3 'NativeVideoPlayer|play\(\)' src --glob '*.{ts,tsx}' || true
echo
echo "== package expo-video version =="
if [ -f package.json ]; then
node - <<'JS'
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
console.log(JSON.stringify({ dependencies: pkg.dependencies?.['expo-video'], devDependencies: pkg.devDependencies?.['expo-video'] }, null, 2));
JS
fi
echo
echo "== focused setup callback text in curated docs source if available =="
curl -fsSL 'https://docs.expo.dev/versions/v56.0.0/sdk/video/' \
| rg -n 'useVideoPlayer\(source, setup\)|callback after|created player|automatically play|play()' \
| head -n 80Repository: Resgrid/Unit Length of output: 50369 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
echo "== repo files around setup =="
wc -l jest-setup.ts
sed -n '238,252p' jest-setup.ts
echo
echo "== useVideoPlayer calls in tracked src files only =="
git ls-files 'src/*' 'src/**/*' \
| xargs rg -n -U -C 3 'useVideoPlayer\(' --glob '*.{ts,tsx}' || true
echo
echo "== focused setup callback/signature in docs source only =="
curl -fsSL 'https://docs.expo.dev/versions/v56.0.0/sdk/video/' \
| python3 - <<'PY'
import sys, re
data=sys.stdin.read()
for pat in [
r'useVideoPlayer\s*\([^)]*\)\s*:',
r'\\bsetup\\b.*callback',
r'called.*after.*create',
r'play\\(\\)',
]:
hits=re.findall(pat, data, re.I)
if hits:
print(f"== pattern {pat!r} ==")
print("\n".join(set(hits))[:200])
PY
echo
echo "== package expo-video metadata =="
python3 - <<'JS' || true
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
console.log(JSON.stringify({ dependencies: pkg.dependencies?.['expo-video'], devDependencies: pkg.devDependencies?.['expo-video'] }, null, 2));
JSRepository: Resgrid/Unit Length of output: 1357 🌐 Web query:
💡 Result: In Expo SDK 56, the Citations:
Invoke the
🤖 Prompt for AI AgentsSource: Coding guidelines |
||
| })); | ||
|
|
||
| // Mock zod globally to avoid validation schema issues in tests | ||
| jest.mock('zod', () => ({ | ||
| z: { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| import { ResizeMode, Video } from 'expo-av'; | ||
| import { useVideoPlayer, VideoView } from 'expo-video'; | ||
| import { CopyIcon, XIcon } from 'lucide-react-native'; | ||
| import React, { useCallback } from 'react'; | ||
| import { useTranslation } from 'react-i18next'; | ||
|
|
@@ -21,6 +21,19 @@ interface VideoPlayerModalProps { | |
| onCopyUrl: (feed: CallVideoFeedResultData) => void; | ||
| } | ||
|
|
||
| interface NativeVideoPlayerProps { | ||
| uri: string; | ||
| contentType: 'hls' | 'dash'; | ||
| } | ||
|
|
||
| const NativeVideoPlayer: React.FC<NativeVideoPlayerProps> = ({ uri, contentType }) => { | ||
| const player = useVideoPlayer({ uri, contentType }, (videoPlayer) => { | ||
| videoPlayer.play(); | ||
| }); | ||
|
Comment on lines
+30
to
+32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The const source = contentType === 'hls' ? { hls: uri } : { dash: uri };
const player = useVideoPlayer(source, (videoPlayer) => {
videoPlayer.play();
});Prompt for LLMTalk to Kody by mentioning @kody Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction. |
||
|
|
||
| return <VideoView player={player} style={styles.video} nativeControls contentFit="contain" />; | ||
|
Comment on lines
+29
to
+34
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
curl -fsSL 'https://docs.expo.dev/versions/v56.0.0/sdk/video/' \
| rg -n 'statusChange|error|useEventListener'
rg -n -C 4 \
'useVideoPlayer|statusChange|useEvent|useEventListener|useToastStore|logger' \
src/components/call-video-feeds --glob '*.{ts,tsx}'Repository: Resgrid/Unit Length of output: 50369 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
printf '--- target file outline ---\n'
ast-grep outline src/components/call-video-feeds/video-player-modal.tsx --view expanded || true
printf '\n--- target file relevant content ---\n'
cat -n src/components/call-video-feeds/video-player-modal.tsx | sed -n '1,220p'
printf '\n--- focused repository search ---\n'
rg -n -C 3 'useVideoPlayer|statusChange|useEvent|useEventListener|useToastStore|logger|NativeVideoPlayer|VideoView' src/components/call-video-feeds --glob '*.{ts,tsx}' || trueRepository: Resgrid/Unit Length of output: 241 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- target file outline ---'
ast-grep outline src/components/call-video-feeds/video-player-modal.tsx --view expanded || true
printf '%s\n' ''
printf '%s\n' '--- target file relevant content ---'
cat -n src/components/call-video-feeds/video-player-modal.tsx | sed -n '1,220p'
printf '%s\n' ''
printf '%s\n' '--- focused repository search ---'
rg -n -C 3 'useVideoPlayer|statusChange|useEvent|useEventListener|useToastStore|logger|NativeVideoPlayer|VideoView' src/components/call-video-feeds --glob '*.{ts,tsx}' || trueRepository: Resgrid/Unit Length of output: 13177 🌐 Web query:
💡 Result: In Expo SDK 56, the expo-video library uses an event-based system for the VideoPlayer, as changes to player properties do not automatically trigger React state updates [1][2]. To handle status changes and potential errors, you can use the statusChange event emitted by the VideoPlayer. The payload for this event, StatusChangeEventPayload, includes the following properties [2]: - status: The new VideoPlayerStatus. - error: An optional PlayerError object, which contains information if an error occurred during the status change [2]. - oldStatus: The previous VideoPlayerStatus [2]. You can listen for this event using the useEventListener hook from the expo package, which is built around the player's addListener and removeListener methods and handles cleanup automatically [1][3]. Example usage: import { useEventListener } from 'expo'; // Inside your component useEventListener(player, 'statusChange', ({ status, error }) => { if (error) { console.error('Player error:', error); } console.log('Player status changed to:', status); }); Alternatively, you can use the useEvent hook if you need a stateful value that updates automatically, or use player.addListener directly with a useEffect hook for more manual control [1][3]. Citations:
Handle native playback errors in
🤖 Prompt for AI AgentsSource: Coding guidelines |
||
| }; | ||
|
|
||
| export const VideoPlayerModal: React.FC<VideoPlayerModalProps> = ({ isOpen, onClose, feed, onCopyUrl }) => { | ||
| const { t } = useTranslation(); | ||
|
|
||
|
|
@@ -40,7 +53,7 @@ export const VideoPlayerModal: React.FC<VideoPlayerModalProps> = ({ isOpen, onCl | |
| switch (feed.FeedFormat) { | ||
| case FeedFormat.HLS: | ||
| case FeedFormat.DASH: | ||
| return <Video source={{ uri: feed.Url }} style={styles.video} useNativeControls resizeMode={ResizeMode.CONTAIN} shouldPlay />; | ||
| return <NativeVideoPlayer key={`${feed.FeedFormat}:${feed.Url}`} uri={feed.Url} contentType={feed.FeedFormat === FeedFormat.HLS ? 'hls' : 'dash'} />; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
curl -fsSL 'https://docs.expo.dev/versions/v56.0.0/sdk/video/' \
| rg -n '`dash`|Android-only'
rg -n -C 5 \
'FeedFormat\.DASH|contentType.*dash|VideoAssetTransportProvider|contentTypeHint|isAndroid' \
. --glob '*.{ts,tsx,js}'Repository: Resgrid/Unit Length of output: 50369 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== candidate files =="
fd -a 'video-player-modal|platform|FeedFormat|feedResult|call' . | sed 's#^\./##' | head -200
echo
echo "== exact DASH/transport/platform references in tracked files =="
rg -n -C 3 \
'FeedFormat\.DASH|contentType:\s*["'\'']dash["'\'']|VideoAssetTransportProvider|contentTypeHint|isAndroid|FeedFormat' \
--glob '*.{ts,tsx,js}' . || true
echo
echo "== video player modal lines =="
file="$(fd 'video-player-modal.tsx' . | head -1)"
if [ -n "$file" ]; then
echo -- "$file"
wc -l "$file"
sed -n '1,140p' "$file" | cat -n
fi
echo
echo "== platform module =="
file="$(fd 'platform.ts' src/lib | head -1)"
if [ -n "$file" ]; then
echo -- "$file"
sed -n '1,160p' "$file" | cat -n
fi
echo
echo "== FeedFormat definitions/usages =="
rg -n -C 3 'enum FeedFormat|FeedFormat;' --glob '*.{ts,tsx,js}' . || true
echo
echo "== deterministic content-type mapping probe from source =="
python3 - <<'PY'
from pathlib import Path
p = next(Path('.').glob('src/components/call-video-feeds/video-player-modal.tsx'), None)
if p:
src = p.read_text()
print("modal_path", str(p))
print("has_hls_branch", "FeedFormat.HLS ? 'hls'" in src)
print("has_dash_branch", "FeedFormat.DASH ? 'dash'" in src)
print("has_isAndroid_in_modal", "isAndroid" in src)
else:
print("modal_path", None)
PYRepository: Resgrid/Unit Length of output: 37570 Guard
🤖 Prompt for AI AgentsSource: Coding guidelines There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Code duplication identified where shared string literals Kody rule violation: Centralize string constants Prompt for LLMTalk to Kody by mentioning @kody Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction. |
||
|
|
||
| case FeedFormat.YouTubeLive: | ||
| case FeedFormat.Embed: | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unhandled promise rejection risk identified where the awaited
setAudioModeAsynccall lacks a try/catch guard. Wrap the operation in a try/catch block to comply with Rule [1] and degrade gracefully.Kody rule violation: Handle async operations with proper error handling
Prompt for LLM
Talk to Kody by mentioning @kody
Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.