diff --git a/src/assets/icons/waveform.svg b/src/assets/icons/waveform.svg
new file mode 100644
index 0000000..e39cc74
--- /dev/null
+++ b/src/assets/icons/waveform.svg
@@ -0,0 +1,8 @@
+
diff --git a/src/screens/modals/Player/components/MediaControls.tsx b/src/screens/modals/Player/components/MediaControls.tsx
index 3a71753..bc366ce 100644
--- a/src/screens/modals/Player/components/MediaControls.tsx
+++ b/src/screens/modals/Player/components/MediaControls.tsx
@@ -19,6 +19,7 @@ const previous = () => TrackPlayer.skipToPrevious();
const Container = styled.View`
align-items: center;
margin-top: 40px;
+ margin-bottom: 52px;
`;
const Buttons = styled.View`
diff --git a/src/screens/modals/Player/components/MediaInformation.tsx b/src/screens/modals/Player/components/MediaInformation.tsx
new file mode 100644
index 0000000..3d759df
--- /dev/null
+++ b/src/screens/modals/Player/components/MediaInformation.tsx
@@ -0,0 +1,72 @@
+import { Text } from '@/components/Typography';
+import { useTypedSelector } from '@/store';
+import useCurrentTrack from '@/utility/useCurrentTrack';
+import React from 'react-native';
+import WaveformIcon from '@/assets/icons/waveform.svg';
+import useDefaultStyles from '@/components/Colors';
+import styled, { css } from 'styled-components/native';
+
+const Container = styled.View`
+ flex-direction: row;
+ gap: 8px;
+ margin-top: 8px;
+ margin-bottom: 16px;
+`;
+
+const Info = styled.View`
+ flex-direction: row;
+ justify-content: space-between;
+ gap: 8px;
+ flex-grow: 1;
+ flex-shrink: 1;
+`;
+
+const Label = styled(Text)<{ overflow?: boolean }>`
+ opacity: 0.5;
+ font-size: 13px;
+
+ ${(props) => props?.overflow && css`
+ flex: 0 1 auto;
+ `}
+`;
+
+export default function MediaInformation() {
+ const styles = useDefaultStyles();
+ const { track } = useCurrentTrack();
+ const { entities } = useTypedSelector((state) => state.music.tracks);
+
+ if (!track) {
+ return null;
+ }
+
+ const albumTrack = entities[track.backendId];
+ const mediaStream = albumTrack.MediaStreams?.find((d) => d.Type === 'Audio');
+
+ console.log(mediaStream);
+
+ return (
+
+
+
+
+
+ {mediaStream && (
+ <>
+
+
+ >
+ )}
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/screens/modals/Player/components/Timer.tsx b/src/screens/modals/Player/components/Timer.tsx
index 1b1afb5..cdbab18 100644
--- a/src/screens/modals/Player/components/Timer.tsx
+++ b/src/screens/modals/Player/components/Timer.tsx
@@ -13,7 +13,6 @@ import { t } from '@/localisation';
const Container = styled.View`
align-self: flex-start;
align-items: flex-start;
- margin-top: 52px;
padding: 8px;
margin-left: -8px;
flex: 0 1 auto;
diff --git a/src/screens/modals/Player/index.tsx b/src/screens/modals/Player/index.tsx
index aeea5a8..2c1cf35 100644
--- a/src/screens/modals/Player/index.tsx
+++ b/src/screens/modals/Player/index.tsx
@@ -12,6 +12,7 @@ import Timer from './components/Timer';
import styled from 'styled-components/native';
import { ColoredBlurView } from '@/components/Colors.tsx';
import LyricsPreview from './components/LyricsPreview.tsx';
+import MediaInformation from './components/MediaInformation';
const Group = styled.View`
flex-direction: row;
@@ -28,6 +29,7 @@ export default function Player() {
+
diff --git a/src/store/music/types.ts b/src/store/music/types.ts
index 99a683c..109d0dd 100644
--- a/src/store/music/types.ts
+++ b/src/store/music/types.ts
@@ -8,6 +8,29 @@ export interface UserData {
Key: string;
}
+export interface MediaStream {
+ Codec: string
+ TimeBase: string
+ VideoRange: string
+ VideoRangeType: string
+ AudioSpatialFormat: string
+ DisplayTitle: string
+ IsInterlaced: boolean
+ ChannelLayout: string
+ BitRate: number
+ Channels: number
+ SampleRate: number
+ IsDefault: boolean
+ IsForced: boolean
+ IsHearingImpaired: boolean
+ Type: string
+ Index: number
+ IsExternal: boolean
+ IsTextSubtitleStream: boolean
+ SupportsExternalStream: boolean
+ Level: number
+}
+
export interface ArtistItem {
Name: string;
Id: string;
@@ -71,6 +94,7 @@ export interface AlbumTrack {
MediaType: string;
HasLyrics: boolean;
Lyrics: Lyrics | null;
+ MediaStreams: MediaStream[];
}
export interface State {
diff --git a/src/utility/JellyfinApi/album.ts b/src/utility/JellyfinApi/album.ts
index 11e9754..97004b0 100644
--- a/src/utility/JellyfinApi/album.ts
+++ b/src/utility/JellyfinApi/album.ts
@@ -61,6 +61,7 @@ export async function retrieveAlbumTracks(ItemId: string) {
const singleAlbumOptions = {
ParentId: ItemId,
SortBy: 'ParentIndexNumber,IndexNumber,SortName',
+ Fields: 'MediaStreams',
};
const singleAlbumParams = new URLSearchParams(singleAlbumOptions).toString();
diff --git a/src/utility/JellyfinApi/lib.ts b/src/utility/JellyfinApi/lib.ts
index 8e30ac2..19e1453 100644
--- a/src/utility/JellyfinApi/lib.ts
+++ b/src/utility/JellyfinApi/lib.ts
@@ -15,6 +15,9 @@ function generateConfig(credentials: Credentials): RequestInit {
};
}
+/**
+ * Retrieve a copy of the store without getting caught in import cycles.
+ */
export function asyncFetchStore() {
return require('@/store').default as Store;
}
@@ -74,7 +77,7 @@ export async function fetchApi(
const data = await response.json();
throw data;
} catch {
- throw response;
+ throw new Error('FailedRequest');
}
}
diff --git a/src/utility/JellyfinApi/track.ts b/src/utility/JellyfinApi/track.ts
index 73f05bb..8fa218f 100644
--- a/src/utility/JellyfinApi/track.ts
+++ b/src/utility/JellyfinApi/track.ts
@@ -22,8 +22,9 @@ const baseTrackOptions: Record = {
TranscodingContainer: 'aac',
AudioCodec: 'aac',
Container: 'mp3,aac',
+ audioBitRate: '320000',
...trackOptionsOsOverrides[Platform.OS],
-};
+} as const;
/**
* Generate the track streaming url from the trackId
@@ -47,10 +48,12 @@ export function generateTrackUrl(trackId: string) {
* Generate a track object from a Jellyfin ItemId so that
* react-native-track-player can easily consume it.
*/
-export function generateTrack(track: AlbumTrack): Track {
+export async function generateTrack(track: AlbumTrack): Promise