mirror of
https://github.com/austinried/subtracks.git
synced 2025-12-29 09:29:29 +01:00
build out artist view
clean up mapping methods a bit
This commit is contained in:
parent
e7f9b1db86
commit
62a721ba4d
@ -164,7 +164,13 @@ const ArtistArt = React.memo<ArtistArtProps>(({ id, height, width }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container, { borderRadius: height / 2 }]}>
|
<View style={[styles.container, { borderRadius: height / 2 }]}>
|
||||||
<CoverArt PlaceholderComponent={Placeholder} height={height} width={width} coverArtUri={artistArt?.uri} />
|
<CoverArt
|
||||||
|
PlaceholderComponent={Placeholder}
|
||||||
|
height={height}
|
||||||
|
width={width}
|
||||||
|
coverArtUri={artistArt?.uri}
|
||||||
|
resizeMode={FastImage.resizeMode.cover}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -11,7 +11,8 @@ const CoverArt: React.FC<{
|
|||||||
height?: string | number
|
height?: string | number
|
||||||
width?: string | number
|
width?: string | number
|
||||||
coverArtUri?: string
|
coverArtUri?: string
|
||||||
}> = ({ PlaceholderComponent, placeholderIcon, height, width, coverArtUri }) => {
|
resizeMode?: keyof typeof FastImage.resizeMode
|
||||||
|
}> = ({ PlaceholderComponent, placeholderIcon, height, width, coverArtUri, resizeMode }) => {
|
||||||
const [placeholderVisible, setPlaceholderVisible] = useState(false)
|
const [placeholderVisible, setPlaceholderVisible] = useState(false)
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
const [layout, setLayout] = useState({ x: 0, y: 0, width: 0, height: 0 })
|
const [layout, setLayout] = useState({ x: 0, y: 0, width: 0, height: 0 })
|
||||||
@ -26,7 +27,7 @@ const CoverArt: React.FC<{
|
|||||||
<FastImage
|
<FastImage
|
||||||
source={{ uri: coverArtUri, priority: 'high' }}
|
source={{ uri: coverArtUri, priority: 'high' }}
|
||||||
style={{ ...styles.image, opacity: placeholderVisible ? 0 : 1 }}
|
style={{ ...styles.image, opacity: placeholderVisible ? 0 : 1 }}
|
||||||
resizeMode={FastImage.resizeMode.contain}
|
resizeMode={resizeMode || FastImage.resizeMode.contain}
|
||||||
onError={() => {
|
onError={() => {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
setPlaceholderVisible(true)
|
setPlaceholderVisible(true)
|
||||||
|
|||||||
@ -17,12 +17,14 @@ const GradientBackground: React.FC<{
|
|||||||
<LinearGradient
|
<LinearGradient
|
||||||
colors={colors || [colorStyles.gradient.high, colorStyles.gradient.low]}
|
colors={colors || [colorStyles.gradient.high, colorStyles.gradient.low]}
|
||||||
locations={locations || [0.01, 0.7]}
|
locations={locations || [0.01, 0.7]}
|
||||||
style={{
|
style={[
|
||||||
...style,
|
style,
|
||||||
|
{
|
||||||
width: width || '100%',
|
width: width || '100%',
|
||||||
height: height || layout.height,
|
height: height || layout.height,
|
||||||
position: position || 'absolute',
|
position: position || 'absolute',
|
||||||
}}>
|
},
|
||||||
|
]}>
|
||||||
{children}
|
{children}
|
||||||
</LinearGradient>
|
</LinearGradient>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -4,7 +4,11 @@ import dimensions from '@app/styles/dimensions'
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { ScrollView, ScrollViewProps, useWindowDimensions } from 'react-native'
|
import { ScrollView, ScrollViewProps, useWindowDimensions } from 'react-native'
|
||||||
|
|
||||||
const GradientScrollView: React.FC<ScrollViewProps> = props => {
|
const GradientScrollView: React.FC<
|
||||||
|
ScrollViewProps & {
|
||||||
|
offset?: number
|
||||||
|
}
|
||||||
|
> = props => {
|
||||||
const layout = useWindowDimensions()
|
const layout = useWindowDimensions()
|
||||||
|
|
||||||
const minHeight = layout.height - (dimensions.top() + dimensions.bottom())
|
const minHeight = layout.height - (dimensions.top() + dimensions.bottom())
|
||||||
@ -15,7 +19,7 @@ const GradientScrollView: React.FC<ScrollViewProps> = props => {
|
|||||||
{...props}
|
{...props}
|
||||||
style={[props.style, { backgroundColor: colors.gradient.low }]}
|
style={[props.style, { backgroundColor: colors.gradient.low }]}
|
||||||
contentContainerStyle={[props.contentContainerStyle, { minHeight }]}>
|
contentContainerStyle={[props.contentContainerStyle, { minHeight }]}>
|
||||||
<GradientBackground />
|
<GradientBackground style={{ top: props.offset }} />
|
||||||
{props.children}
|
{props.children}
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -17,18 +17,6 @@ export interface ArtistArt {
|
|||||||
coverArtUris: string[]
|
coverArtUris: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Album {
|
|
||||||
id: string
|
|
||||||
artistId?: string
|
|
||||||
artist?: string
|
|
||||||
name: string
|
|
||||||
starred?: Date
|
|
||||||
coverArt?: string
|
|
||||||
coverArtUri?: string
|
|
||||||
coverArtThumbUri?: string
|
|
||||||
year?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AlbumListItem {
|
export interface AlbumListItem {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
@ -37,6 +25,11 @@ export interface AlbumListItem {
|
|||||||
coverArtThumbUri?: string
|
coverArtThumbUri?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Album extends AlbumListItem {
|
||||||
|
coverArtUri?: string
|
||||||
|
year?: number
|
||||||
|
}
|
||||||
|
|
||||||
export interface AlbumWithSongs extends Album {
|
export interface AlbumWithSongs extends Album {
|
||||||
songs: Song[]
|
songs: Song[]
|
||||||
}
|
}
|
||||||
@ -47,19 +40,7 @@ export interface Song {
|
|||||||
artist?: string
|
artist?: string
|
||||||
title: string
|
title: string
|
||||||
track?: number
|
track?: number
|
||||||
year?: number
|
|
||||||
genre?: string
|
|
||||||
coverArt?: string
|
|
||||||
size?: number
|
|
||||||
contentType?: string
|
|
||||||
suffix?: string
|
|
||||||
duration?: number
|
duration?: number
|
||||||
bitRate?: number
|
|
||||||
userRating?: number
|
|
||||||
averageRating?: number
|
|
||||||
playCount?: number
|
|
||||||
discNumber?: number
|
|
||||||
created?: Date
|
|
||||||
starred?: Date
|
starred?: Date
|
||||||
|
|
||||||
streamUri: string
|
streamUri: string
|
||||||
|
|||||||
@ -19,13 +19,6 @@ type TabStackParamList = {
|
|||||||
ArtistView: { id: string; title: string }
|
ArtistView: { id: string; title: string }
|
||||||
}
|
}
|
||||||
|
|
||||||
type TabMainScreenNavigationProp = NativeStackNavigationProp<TabStackParamList, 'TabMain'>
|
|
||||||
type TabMainScreenRouteProp = RouteProp<TabStackParamList, 'TabMain'>
|
|
||||||
type TabMainScreenProps = {
|
|
||||||
route: TabMainScreenRouteProp
|
|
||||||
navigation: TabMainScreenNavigationProp
|
|
||||||
}
|
|
||||||
|
|
||||||
type AlbumScreenNavigationProp = NativeStackNavigationProp<TabStackParamList, 'AlbumView'>
|
type AlbumScreenNavigationProp = NativeStackNavigationProp<TabStackParamList, 'AlbumView'>
|
||||||
type AlbumScreenRouteProp = RouteProp<TabStackParamList, 'AlbumView'>
|
type AlbumScreenRouteProp = RouteProp<TabStackParamList, 'AlbumView'>
|
||||||
type AlbumScreenProps = {
|
type AlbumScreenProps = {
|
||||||
|
|||||||
@ -1,24 +1,64 @@
|
|||||||
|
import CoverArt from '@app/components/CoverArt'
|
||||||
|
import GradientScrollView from '@app/components/GradientScrollView'
|
||||||
|
import PressableOpacity from '@app/components/PressableOpacity'
|
||||||
|
import { Album } from '@app/models/music'
|
||||||
|
import { artistInfoAtomFamily } from '@app/state/music'
|
||||||
|
import colors from '@app/styles/colors'
|
||||||
|
import font from '@app/styles/font'
|
||||||
|
import { useLayout } from '@react-native-community/hooks'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
import { useAtomValue } from 'jotai/utils'
|
import { useAtomValue } from 'jotai/utils'
|
||||||
import React, { useEffect } from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import { StyleSheet, Text } from 'react-native'
|
import { StyleSheet, Text, View } from 'react-native'
|
||||||
import { artistInfoAtomFamily } from '@app/state/music'
|
import FastImage from 'react-native-fast-image'
|
||||||
import ArtistArt from '@app/components/ArtistArt'
|
|
||||||
import GradientScrollView from '@app/components/GradientScrollView'
|
const AlbumItem = React.memo<{
|
||||||
import font from '@app/styles/font'
|
album: Album
|
||||||
import colors from '@app/styles/colors'
|
height: number
|
||||||
|
width: number
|
||||||
|
}>(({ album, height, width }) => {
|
||||||
|
const navigation = useNavigation()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PressableOpacity
|
||||||
|
onPress={() => navigation.navigate('AlbumView', { id: album.id, title: album.name })}
|
||||||
|
key={album.id}
|
||||||
|
style={[styles.albumItem, { width }]}>
|
||||||
|
<CoverArt coverArtUri={album.coverArtThumbUri} height={height} width={width} />
|
||||||
|
<Text style={styles.albumTitle}>{album.name}</Text>
|
||||||
|
<Text style={styles.albumYear}> {album.year ? album.year : ''}</Text>
|
||||||
|
</PressableOpacity>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
const ArtistDetails: React.FC<{ id: string }> = ({ id }) => {
|
const ArtistDetails: React.FC<{ id: string }> = ({ id }) => {
|
||||||
const artist = useAtomValue(artistInfoAtomFamily(id))
|
const artist = useAtomValue(artistInfoAtomFamily(id))
|
||||||
|
const layout = useLayout()
|
||||||
|
|
||||||
|
const size = layout.width / 2 - styles.container.paddingHorizontal / 2
|
||||||
|
|
||||||
if (!artist) {
|
if (!artist) {
|
||||||
return <></>
|
return <></>
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GradientScrollView style={styles.scroll} contentContainerStyle={styles.scrollContent}>
|
<GradientScrollView offset={artistImageHeight} style={styles.scroll} contentContainerStyle={styles.scrollContent}>
|
||||||
|
<FastImage
|
||||||
|
style={[styles.artistImage]}
|
||||||
|
source={{ uri: artist.largeImageUrl }}
|
||||||
|
resizeMode={FastImage.resizeMode.cover}
|
||||||
|
/>
|
||||||
|
<View style={styles.titleContainer}>
|
||||||
<Text style={styles.title}>{artist.name}</Text>
|
<Text style={styles.title}>{artist.name}</Text>
|
||||||
<ArtistArt id={artist.id} height={200} width={200} />
|
</View>
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Text style={styles.header}>Albums</Text>
|
||||||
|
<View style={styles.albums} onLayout={layout.onLayout}>
|
||||||
|
{artist.albums.map(a => (
|
||||||
|
<AlbumItem key={a.id} album={a} height={size} width={size} />
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
</GradientScrollView>
|
</GradientScrollView>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -40,6 +80,8 @@ const ArtistView: React.FC<{
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const artistImageHeight = 280
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
scroll: {
|
scroll: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
@ -47,10 +89,58 @@ const styles = StyleSheet.create({
|
|||||||
scrollContent: {
|
scrollContent: {
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
|
container: {
|
||||||
|
width: '100%',
|
||||||
|
paddingHorizontal: 20,
|
||||||
|
},
|
||||||
|
titleContainer: {
|
||||||
|
width: '100%',
|
||||||
|
height: artistImageHeight,
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
},
|
||||||
title: {
|
title: {
|
||||||
fontFamily: font.regular,
|
fontFamily: font.bold,
|
||||||
fontSize: 16,
|
fontSize: 44,
|
||||||
color: colors.text.primary,
|
color: colors.text.primary,
|
||||||
|
textAlign: 'left',
|
||||||
|
textShadowColor: 'black',
|
||||||
|
textShadowOffset: { width: 0, height: 0 },
|
||||||
|
textShadowRadius: 16,
|
||||||
|
paddingHorizontal: 10,
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
fontFamily: font.bold,
|
||||||
|
fontSize: 24,
|
||||||
|
color: colors.text.primary,
|
||||||
|
marginTop: 14,
|
||||||
|
},
|
||||||
|
artistImage: {
|
||||||
|
position: 'absolute',
|
||||||
|
width: '100%',
|
||||||
|
height: artistImageHeight,
|
||||||
|
},
|
||||||
|
albums: {
|
||||||
|
marginTop: 14,
|
||||||
|
width: '100%',
|
||||||
|
flexDirection: 'row',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
alignItems: 'flex-start',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
},
|
||||||
|
albumItem: {
|
||||||
|
marginBottom: 20,
|
||||||
|
},
|
||||||
|
albumTitle: {
|
||||||
|
fontFamily: font.semiBold,
|
||||||
|
fontSize: 14,
|
||||||
|
color: colors.text.primary,
|
||||||
|
marginTop: 4,
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
albumYear: {
|
||||||
|
color: colors.text.secondary,
|
||||||
|
fontFamily: font.regular,
|
||||||
|
textAlign: 'center',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -38,7 +38,7 @@ const Category = React.memo<{
|
|||||||
}>(({ name, data }) => {
|
}>(({ name, data }) => {
|
||||||
return (
|
return (
|
||||||
<View style={styles.category}>
|
<View style={styles.category}>
|
||||||
<Text style={styles.header}>{name}</Text>
|
<Text style={styles.categoryHeader}>{name}</Text>
|
||||||
<ScrollView
|
<ScrollView
|
||||||
horizontal={true}
|
horizontal={true}
|
||||||
showsHorizontalScrollIndicator={false}
|
showsHorizontalScrollIndicator={false}
|
||||||
@ -95,7 +95,7 @@ const styles = StyleSheet.create({
|
|||||||
category: {
|
category: {
|
||||||
marginTop: 12,
|
marginTop: 12,
|
||||||
},
|
},
|
||||||
header: {
|
categoryHeader: {
|
||||||
fontFamily: font.bold,
|
fontFamily: font.bold,
|
||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
color: colors.text.primary,
|
color: colors.text.primary,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { Album, AlbumListItem, AlbumWithSongs, Artist, ArtistArt, ArtistInfo, Song } from '@app/models/music'
|
import { Album, AlbumListItem, AlbumWithSongs, Artist, ArtistArt, ArtistInfo, Song } from '@app/models/music'
|
||||||
import { activeServerAtom, homeListTypesAtom } from '@app/state/settings'
|
import { activeServerAtom, homeListTypesAtom } from '@app/state/settings'
|
||||||
import { SubsonicApiClient } from '@app/subsonic/api'
|
import { SubsonicApiClient } from '@app/subsonic/api'
|
||||||
import { AlbumID3Element, ArtistInfo2Element, ChildElement } from '@app/subsonic/elements'
|
import { AlbumID3Element, ArtistID3Element, ArtistInfo2Element, ChildElement } from '@app/subsonic/elements'
|
||||||
import { GetAlbumList2Type } from '@app/subsonic/params'
|
import { GetAlbumList2Type } from '@app/subsonic/params'
|
||||||
import { GetArtistResponse } from '@app/subsonic/responses'
|
import { GetArtistResponse } from '@app/subsonic/responses'
|
||||||
import { atom, useAtom } from 'jotai'
|
import { atom, useAtom } from 'jotai'
|
||||||
@ -28,13 +28,7 @@ export const useUpdateArtists = () => {
|
|||||||
const client = new SubsonicApiClient(server)
|
const client = new SubsonicApiClient(server)
|
||||||
const response = await client.getArtists()
|
const response = await client.getArtists()
|
||||||
|
|
||||||
setArtists(
|
setArtists(response.data.artists.map(mapArtistID3toArtist))
|
||||||
response.data.artists.map(x => ({
|
|
||||||
id: x.id,
|
|
||||||
name: x.name,
|
|
||||||
starred: x.starred,
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
setUpdating(false)
|
setUpdating(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -121,7 +115,7 @@ export const albumAtomFamily = atomFamily((id: string) =>
|
|||||||
|
|
||||||
const client = new SubsonicApiClient(server)
|
const client = new SubsonicApiClient(server)
|
||||||
const response = await client.getAlbum({ id })
|
const response = await client.getAlbum({ id })
|
||||||
return mapAlbumID3WithSongs(response.data.album, response.data.songs, client)
|
return mapAlbumID3WithSongstoAlbunWithSongs(response.data.album, response.data.songs, client)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -161,45 +155,56 @@ export const artistArtAtomFamily = atomFamily((id: string) =>
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
coverArtUris,
|
coverArtUris,
|
||||||
uri: artistInfo.mediumImageUrl,
|
uri: artistInfo.largeImageUrl,
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
function mapArtistID3toArtist(artist: ArtistID3Element): Artist {
|
||||||
|
return {
|
||||||
|
id: artist.id,
|
||||||
|
name: artist.name,
|
||||||
|
starred: artist.starred,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function mapArtistInfo(
|
function mapArtistInfo(
|
||||||
artistResponse: GetArtistResponse,
|
artistResponse: GetArtistResponse,
|
||||||
artistInfo: ArtistInfo2Element,
|
info: ArtistInfo2Element,
|
||||||
client: SubsonicApiClient,
|
client: SubsonicApiClient,
|
||||||
): ArtistInfo {
|
): ArtistInfo {
|
||||||
const info = { ...artistInfo } as any
|
|
||||||
delete info.similarArtists
|
|
||||||
|
|
||||||
const { artist, albums } = artistResponse
|
const { artist, albums } = artistResponse
|
||||||
|
|
||||||
const mappedAlbums = albums.map(a => mapAlbumID3(a, client))
|
const mappedAlbums = albums.map(a => mapAlbumID3toAlbum(a, client))
|
||||||
const coverArtUris = mappedAlbums
|
const coverArtUris = mappedAlbums
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
if (a.year && b.year) {
|
if (a.year && b.year) {
|
||||||
return a.year - b.year
|
return b.year - a.year
|
||||||
} else {
|
} else {
|
||||||
return a.name.localeCompare(b.name) - 9000
|
return a.name.localeCompare(b.name) - 9000
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.map(a => a.coverArtThumbUri)
|
.map(a => a.coverArtThumbUri)
|
||||||
|
.filter(a => a !== undefined) as string[]
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...artist,
|
...mapArtistID3toArtist(artist),
|
||||||
...info,
|
|
||||||
albums: mappedAlbums,
|
albums: mappedAlbums,
|
||||||
coverArtUris,
|
coverArtUris,
|
||||||
|
mediumImageUrl: info.mediumImageUrl,
|
||||||
|
largeImageUrl: info.largeImageUrl,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapAlbumID3(album: AlbumID3Element, client: SubsonicApiClient): Album {
|
function mapCoverArtUri(item: { coverArt?: string }, client: SubsonicApiClient) {
|
||||||
return {
|
return {
|
||||||
...album,
|
coverArtUri: item.coverArt ? client.getCoverArtUri({ id: item.coverArt }) : undefined,
|
||||||
coverArtUri: album.coverArt ? client.getCoverArtUri({ id: album.coverArt }) : undefined,
|
}
|
||||||
coverArtThumbUri: album.coverArt ? client.getCoverArtUri({ id: album.coverArt, size: '256' }) : undefined,
|
}
|
||||||
|
|
||||||
|
function mapCoverArtThumbUri(item: { coverArt?: string }, client: SubsonicApiClient) {
|
||||||
|
return {
|
||||||
|
coverArtThumbUri: item.coverArt ? client.getCoverArtUri({ id: item.coverArt, size: '256' }) : undefined,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,26 +214,41 @@ function mapAlbumID3toAlbumListItem(album: AlbumID3Element, client: SubsonicApiC
|
|||||||
name: album.name,
|
name: album.name,
|
||||||
artist: album.artist,
|
artist: album.artist,
|
||||||
starred: album.starred,
|
starred: album.starred,
|
||||||
coverArtThumbUri: album.coverArt ? client.getCoverArtUri({ id: album.coverArt, size: '256' }) : undefined,
|
...mapCoverArtThumbUri(album, client),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapAlbumID3toAlbum(album: AlbumID3Element, client: SubsonicApiClient): Album {
|
||||||
|
return {
|
||||||
|
...mapAlbumID3toAlbumListItem(album, client),
|
||||||
|
...mapCoverArtUri(album, client),
|
||||||
|
...mapCoverArtThumbUri(album, client),
|
||||||
|
year: album.year,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapChildToSong(child: ChildElement, client: SubsonicApiClient): Song {
|
function mapChildToSong(child: ChildElement, client: SubsonicApiClient): Song {
|
||||||
return {
|
return {
|
||||||
...child,
|
id: child.id,
|
||||||
|
album: child.album,
|
||||||
|
artist: child.artist,
|
||||||
|
title: child.title,
|
||||||
|
track: child.track,
|
||||||
|
duration: child.duration,
|
||||||
|
starred: child.starred,
|
||||||
streamUri: client.streamUri({ id: child.id }),
|
streamUri: client.streamUri({ id: child.id }),
|
||||||
coverArtUri: child.coverArt ? client.getCoverArtUri({ id: child.coverArt }) : undefined,
|
...mapCoverArtUri(child, client),
|
||||||
coverArtThumbUri: child.coverArt ? client.getCoverArtUri({ id: child.coverArt, size: '256' }) : undefined,
|
...mapCoverArtThumbUri(child, client),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapAlbumID3WithSongs(
|
function mapAlbumID3WithSongstoAlbunWithSongs(
|
||||||
album: AlbumID3Element,
|
album: AlbumID3Element,
|
||||||
songs: ChildElement[],
|
songs: ChildElement[],
|
||||||
client: SubsonicApiClient,
|
client: SubsonicApiClient,
|
||||||
): AlbumWithSongs {
|
): AlbumWithSongs {
|
||||||
return {
|
return {
|
||||||
...mapAlbumID3(album, client),
|
...mapAlbumID3toAlbum(album, client),
|
||||||
songs: songs.map(s => mapChildToSong(s, client)),
|
songs: songs.map(s => mapChildToSong(s, client)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user