mirror of
https://github.com/austinried/subtracks.git
synced 2026-02-10 06:52:43 +01:00
refactor star
This commit is contained in:
@@ -1,8 +1,6 @@
|
|||||||
import PressableOpacity from '@app/components/PressableOpacity'
|
import PressableOpacity from '@app/components/PressableOpacity'
|
||||||
import { useStarred } from '@app/hooks/music'
|
import { useStar } from '@app/hooks/music'
|
||||||
import { AlbumListItem, Artist, Song, StarrableItemType } from '@app/models/music'
|
import { AlbumListItem, Artist, Song, StarrableItemType } from '@app/models/music'
|
||||||
import { selectMusic } from '@app/state/music'
|
|
||||||
import { useStore } from '@app/state/store'
|
|
||||||
import colors from '@app/styles/colors'
|
import colors from '@app/styles/colors'
|
||||||
import font from '@app/styles/font'
|
import font from '@app/styles/font'
|
||||||
import { NavigationProp, useNavigation } from '@react-navigation/native'
|
import { NavigationProp, useNavigation } from '@react-navigation/native'
|
||||||
@@ -12,9 +10,8 @@ import { ScrollView, StyleProp, StyleSheet, Text, View, ViewStyle } from 'react-
|
|||||||
import { Menu, MenuOption, MenuOptions, MenuTrigger, renderers } from 'react-native-popup-menu'
|
import { Menu, MenuOption, MenuOptions, MenuTrigger, renderers } from 'react-native-popup-menu'
|
||||||
import IconFA from 'react-native-vector-icons/FontAwesome'
|
import IconFA from 'react-native-vector-icons/FontAwesome'
|
||||||
import IconFA5 from 'react-native-vector-icons/FontAwesome5'
|
import IconFA5 from 'react-native-vector-icons/FontAwesome5'
|
||||||
// import IconMat from 'react-native-vector-icons/MaterialIcons'
|
|
||||||
import CoverArt from './CoverArt'
|
import CoverArt from './CoverArt'
|
||||||
import Star from './Star'
|
import { Star } from './Star'
|
||||||
|
|
||||||
const { SlideInMenu } = renderers
|
const { SlideInMenu } = renderers
|
||||||
|
|
||||||
@@ -144,14 +141,13 @@ const OptionStar = React.memo<{
|
|||||||
type: StarrableItemType
|
type: StarrableItemType
|
||||||
additionalText?: string
|
additionalText?: string
|
||||||
}>(({ id, type, additionalText: text }) => {
|
}>(({ id, type, additionalText: text }) => {
|
||||||
const starred = useStarred(id, type)
|
const { starred, toggleStar } = useStar(id, type)
|
||||||
const setStarred = useStore(selectMusic.starItem)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ContextMenuIconTextOption
|
<ContextMenuIconTextOption
|
||||||
IconComponentRaw={<Star starred={starred} size={26} />}
|
IconComponentRaw={<Star starred={starred} size={26} />}
|
||||||
text={(starred ? 'Unstar' : 'Star') + (text ? ` ${text}` : '')}
|
text={(starred ? 'Unstar' : 'Star') + (text ? ` ${text}` : '')}
|
||||||
onSelect={() => setStarred(id, type, starred)}
|
onSelect={toggleStar}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
import { useStarred } from '@app/hooks/music'
|
|
||||||
import { useIsPlaying } from '@app/hooks/trackplayer'
|
import { useIsPlaying } from '@app/hooks/trackplayer'
|
||||||
import { AlbumListItem, Artist, ListableItem, Song } from '@app/models/music'
|
import { AlbumListItem, Artist, ListableItem, Song } from '@app/models/music'
|
||||||
import { selectMusic } from '@app/state/music'
|
|
||||||
import { useStore } from '@app/state/store'
|
|
||||||
import colors from '@app/styles/colors'
|
import colors from '@app/styles/colors'
|
||||||
import font from '@app/styles/font'
|
import font from '@app/styles/font'
|
||||||
import { useNavigation } from '@react-navigation/native'
|
import { useNavigation } from '@react-navigation/native'
|
||||||
@@ -13,7 +10,7 @@ import IconMat from 'react-native-vector-icons/MaterialIcons'
|
|||||||
import { AlbumContextPressable, ArtistContextPressable, SongContextPressable } from './ContextMenu'
|
import { AlbumContextPressable, ArtistContextPressable, SongContextPressable } from './ContextMenu'
|
||||||
import CoverArt from './CoverArt'
|
import CoverArt from './CoverArt'
|
||||||
import PressableOpacity from './PressableOpacity'
|
import PressableOpacity from './PressableOpacity'
|
||||||
import Star from './Star'
|
import { PressableStar } from './Star'
|
||||||
|
|
||||||
const TitleTextSong = React.memo<{
|
const TitleTextSong = React.memo<{
|
||||||
contextId?: string
|
contextId?: string
|
||||||
@@ -58,7 +55,6 @@ const ListItem: React.FC<{
|
|||||||
style?: StyleProp<ViewStyle>
|
style?: StyleProp<ViewStyle>
|
||||||
}> = ({ item, contextId, queueId, onPress, showArt, showStar, subtitle, listStyle, style }) => {
|
}> = ({ item, contextId, queueId, onPress, showArt, showStar, subtitle, listStyle, style }) => {
|
||||||
const navigation = useNavigation()
|
const navigation = useNavigation()
|
||||||
const starred = useStarred(item.id, item.itemType)
|
|
||||||
|
|
||||||
showStar = showStar === undefined ? true : showStar
|
showStar = showStar === undefined ? true : showStar
|
||||||
listStyle = listStyle || 'small'
|
listStyle = listStyle || 'small'
|
||||||
@@ -133,13 +129,6 @@ const ListItem: React.FC<{
|
|||||||
PressableComponent = artistPressable
|
PressableComponent = artistPressable
|
||||||
}
|
}
|
||||||
|
|
||||||
const starItem = useStore(selectMusic.starItem)
|
|
||||||
const toggleStarred = useCallback(() => {
|
|
||||||
if (item.itemType !== 'playlist') {
|
|
||||||
starItem(item.id, item.itemType, starred)
|
|
||||||
}
|
|
||||||
}, [item.id, item.itemType, starItem, starred])
|
|
||||||
|
|
||||||
let title = <></>
|
let title = <></>
|
||||||
if (item.itemType === 'song' && queueId !== undefined) {
|
if (item.itemType === 'song' && queueId !== undefined) {
|
||||||
title = <TitleTextSong contextId={contextId} queueId={queueId} title={item.title} />
|
title = <TitleTextSong contextId={contextId} queueId={queueId} title={item.title} />
|
||||||
@@ -178,10 +167,8 @@ const ListItem: React.FC<{
|
|||||||
</View>
|
</View>
|
||||||
</PressableComponent>
|
</PressableComponent>
|
||||||
<View style={styles.controls}>
|
<View style={styles.controls}>
|
||||||
{showStar && (
|
{showStar && item.itemType !== 'playlist' && (
|
||||||
<PressableOpacity onPress={toggleStarred} style={styles.controlItem}>
|
<PressableStar id={item.id} type={item.itemType} size={26} style={styles.controlItem} />
|
||||||
<Star size={26} starred={starred} />
|
|
||||||
</PressableOpacity>
|
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
|
import { useStar } from '@app/hooks/music'
|
||||||
import colors from '@app/styles/colors'
|
import colors from '@app/styles/colors'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import { PressableStateCallbackType, StyleProp, ViewStyle } from 'react-native'
|
||||||
import IconFA from 'react-native-vector-icons/FontAwesome'
|
import IconFA from 'react-native-vector-icons/FontAwesome'
|
||||||
|
import PressableOpacity from './PressableOpacity'
|
||||||
|
|
||||||
const Star = React.memo<{
|
export const Star = React.memo<{
|
||||||
starred: boolean
|
starred: boolean
|
||||||
size: number
|
size: number
|
||||||
}>(({ starred, size }) => {
|
}>(({ starred, size }) => {
|
||||||
@@ -11,4 +14,17 @@ const Star = React.memo<{
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
export default Star
|
export const PressableStar = React.memo<{
|
||||||
|
id: string
|
||||||
|
type: 'album' | 'artist' | 'song'
|
||||||
|
size: number
|
||||||
|
style?: StyleProp<ViewStyle> | ((state: PressableStateCallbackType) => StyleProp<ViewStyle>) | undefined
|
||||||
|
}>(({ id, type, size, style }) => {
|
||||||
|
const { starred, toggleStar } = useStar(id, type)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PressableOpacity onPress={toggleStar} style={style}>
|
||||||
|
<Star size={size} starred={starred} />
|
||||||
|
</PressableOpacity>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|||||||
@@ -1,35 +1,63 @@
|
|||||||
import { Store, useStore, useStoreDeep } from '@app/state/store'
|
import { useStore } from '@app/state/store'
|
||||||
|
import { StarParams } from '@app/subsonic/params'
|
||||||
import { useCallback, useEffect } from 'react'
|
import { useCallback, useEffect } from 'react'
|
||||||
|
|
||||||
export const useArtistInfo = (id: string) => {
|
type StarrableItem = 'album' | 'artist' | 'song'
|
||||||
const artistInfo = useStoreDeep(useCallback(store => store.entities.artistInfo[id], [id]))
|
|
||||||
const fetchArtistInfo = useStore(store => store.fetchLibraryArtistInfo)
|
|
||||||
|
|
||||||
useEffect(() => {
|
function starParams(id: string, type: StarrableItem): StarParams {
|
||||||
if (!artistInfo) {
|
const params: StarParams = {}
|
||||||
fetchArtistInfo(id)
|
if (type === 'album') {
|
||||||
}
|
params.albumId = id
|
||||||
}, [artistInfo, fetchArtistInfo, id])
|
} else if (type === 'artist') {
|
||||||
|
params.artistId = id
|
||||||
|
} else {
|
||||||
|
params.id = id
|
||||||
|
}
|
||||||
|
|
||||||
return artistInfo
|
return params
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useStarred = (id: string, type: string) => {
|
export const useStar = (id: string, type: StarrableItem) => {
|
||||||
return useStore(
|
const fetchAlbum = useStore(store => store.fetchLibraryAlbum)
|
||||||
|
const fetchArtist = useStore(store => store.fetchLibraryArtist)
|
||||||
|
const fetchSong = useStore(store => store.fetchLibrarySong)
|
||||||
|
|
||||||
|
const _starred = useStore(
|
||||||
useCallback(
|
useCallback(
|
||||||
(state: Store) => {
|
store => {
|
||||||
switch (type) {
|
if (type === 'album') {
|
||||||
case 'song':
|
return store.entities.albums[id] ? !!store.entities.albums[id].starred : null
|
||||||
return state.starredSongs[id]
|
} else if (type === 'artist') {
|
||||||
case 'album':
|
return store.entities.artists[id] ? !!store.entities.artists[id].starred : null
|
||||||
return state.starredAlbums[id]
|
} else {
|
||||||
case 'artist':
|
return store.entities.songs[id] ? !!store.entities.songs[id].starred : null
|
||||||
return state.starredArtists[id]
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[type, id],
|
[id, type],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (_starred === null) {
|
||||||
|
if (type === 'album') {
|
||||||
|
fetchAlbum(id)
|
||||||
|
} else if (type === 'artist') {
|
||||||
|
fetchArtist(id)
|
||||||
|
} else {
|
||||||
|
fetchSong(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [fetchAlbum, fetchArtist, fetchSong, id, _starred, type])
|
||||||
|
|
||||||
|
const starred = !!_starred
|
||||||
|
|
||||||
|
const _star = useStore(store => store.star)
|
||||||
|
const _unstar = useStore(store => store.unstar)
|
||||||
|
|
||||||
|
const star = useCallback(() => _star(starParams(id, type)), [_star, id, type])
|
||||||
|
const unstar = useCallback(() => _unstar(starParams(id, type)), [_unstar, id, type])
|
||||||
|
|
||||||
|
const toggleStar = useCallback(() => (starred ? unstar() : star()), [star, starred, unstar])
|
||||||
|
|
||||||
|
return { star, unstar, toggleStar, starred }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,8 @@ import CoverArt from '@app/components/CoverArt'
|
|||||||
import HeaderBar from '@app/components/HeaderBar'
|
import HeaderBar from '@app/components/HeaderBar'
|
||||||
import ImageGradientBackground from '@app/components/ImageGradientBackground'
|
import ImageGradientBackground from '@app/components/ImageGradientBackground'
|
||||||
import PressableOpacity from '@app/components/PressableOpacity'
|
import PressableOpacity from '@app/components/PressableOpacity'
|
||||||
import Star from '@app/components/Star'
|
import { PressableStar } from '@app/components/Star'
|
||||||
import { useStarred } from '@app/hooks/music'
|
|
||||||
import { useNext, usePause, usePlay, usePrevious, useSeekTo } from '@app/hooks/trackplayer'
|
import { useNext, usePause, usePlay, usePrevious, useSeekTo } from '@app/hooks/trackplayer'
|
||||||
import { selectMusic } from '@app/state/music'
|
|
||||||
import { useStore } from '@app/state/store'
|
import { useStore } from '@app/state/store'
|
||||||
import { QueueContextType, selectTrackPlayer, TrackExt } from '@app/state/trackplayer'
|
import { QueueContextType, selectTrackPlayer, TrackExt } from '@app/state/trackplayer'
|
||||||
import { selectTrackPlayerMap } from '@app/state/trackplayermap'
|
import { selectTrackPlayerMap } from '@app/state/trackplayermap'
|
||||||
@@ -118,10 +116,6 @@ const coverArtStyles = StyleSheet.create({
|
|||||||
|
|
||||||
const SongInfo = () => {
|
const SongInfo = () => {
|
||||||
const track = useStore(selectTrackPlayer.currentTrack)
|
const track = useStore(selectTrackPlayer.currentTrack)
|
||||||
const id = track?.id || '-1'
|
|
||||||
const type = 'song'
|
|
||||||
const starred = useStarred(id, type)
|
|
||||||
const setStarred = useStore(selectMusic.starItem)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={infoStyles.container}>
|
<View style={infoStyles.container}>
|
||||||
@@ -134,9 +128,7 @@ const SongInfo = () => {
|
|||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<View style={infoStyles.controls}>
|
<View style={infoStyles.controls}>
|
||||||
<PressableOpacity onPress={() => setStarred(id, type, starred)}>
|
<PressableStar id={track?.id || '-1'} type={'song'} size={32} />
|
||||||
<Star size={32} starred={starred} />
|
|
||||||
</PressableOpacity>
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
GetArtistsResponse,
|
GetArtistsResponse,
|
||||||
GetPlaylistResponse,
|
GetPlaylistResponse,
|
||||||
GetPlaylistsResponse,
|
GetPlaylistsResponse,
|
||||||
|
GetSongResponse,
|
||||||
GetTopSongsResponse,
|
GetTopSongsResponse,
|
||||||
Search3Response,
|
Search3Response,
|
||||||
SubsonicResponse,
|
SubsonicResponse,
|
||||||
@@ -184,6 +185,8 @@ export type LibrarySlice = {
|
|||||||
fetchLibraryPlaylists: () => Promise<void>
|
fetchLibraryPlaylists: () => Promise<void>
|
||||||
fetchLibraryPlaylist: (id: string) => Promise<void>
|
fetchLibraryPlaylist: (id: string) => Promise<void>
|
||||||
|
|
||||||
|
fetchLibrarySong: (id: string) => Promise<void>
|
||||||
|
|
||||||
fetchLibraryAlbumList: (params: GetAlbumList2Params) => Promise<string[]>
|
fetchLibraryAlbumList: (params: GetAlbumList2Params) => Promise<string[]>
|
||||||
fetchLibrarySearchResults: (params: Search3Params) => Promise<SearchResults>
|
fetchLibrarySearchResults: (params: Search3Params) => Promise<SearchResults>
|
||||||
star: (params: StarParams) => Promise<void>
|
star: (params: StarParams) => Promise<void>
|
||||||
@@ -405,6 +408,28 @@ export const createLibrarySlice = (set: SetState<Store>, get: GetState<Store>):
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
fetchLibrarySong: async id => {
|
||||||
|
const client = get().client
|
||||||
|
if (!client) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let response: SubsonicResponse<GetSongResponse>
|
||||||
|
try {
|
||||||
|
response = await client.getSong({ id })
|
||||||
|
} catch {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const song = mapSong(response.data.song)
|
||||||
|
|
||||||
|
set(
|
||||||
|
produce<LibrarySlice>(state => {
|
||||||
|
state.entities.songs[id] = song
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
|
||||||
fetchLibraryAlbumList: async params => {
|
fetchLibraryAlbumList: async params => {
|
||||||
const client = get().client
|
const client = get().client
|
||||||
if (!client) {
|
if (!client) {
|
||||||
|
|||||||
@@ -1,102 +1,15 @@
|
|||||||
import { StarrableItemType } from '@app/models/music'
|
|
||||||
import { Store } from '@app/state/store'
|
import { Store } from '@app/state/store'
|
||||||
import { StarParams } from '@app/subsonic/params'
|
|
||||||
import produce from 'immer'
|
import produce from 'immer'
|
||||||
import { GetState, SetState } from 'zustand'
|
import { GetState, SetState } from 'zustand'
|
||||||
|
|
||||||
export type MusicSlice = {
|
export type MusicSlice = {
|
||||||
//
|
|
||||||
// actions, etc.
|
|
||||||
//
|
|
||||||
starredSongs: { [id: string]: boolean }
|
|
||||||
starredAlbums: { [id: string]: boolean }
|
|
||||||
starredArtists: { [id: string]: boolean }
|
|
||||||
starItem: (id: string, type: StarrableItemType, unstar?: boolean) => Promise<void>
|
|
||||||
|
|
||||||
albumIdCoverArt: { [id: string]: string | undefined }
|
albumIdCoverArt: { [id: string]: string | undefined }
|
||||||
albumIdCoverArtRequests: { [id: string]: Promise<void> }
|
albumIdCoverArtRequests: { [id: string]: Promise<void> }
|
||||||
fetchAlbumCoverArt: (id: string) => Promise<void>
|
fetchAlbumCoverArt: (id: string) => Promise<void>
|
||||||
getAlbumCoverArt: (id: string | undefined) => Promise<string | undefined>
|
getAlbumCoverArt: (id: string | undefined) => Promise<string | undefined>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const selectMusic = {
|
|
||||||
starItem: (store: MusicSlice) => store.starItem,
|
|
||||||
}
|
|
||||||
|
|
||||||
function reduceStarred(
|
|
||||||
starredType: { [id: string]: boolean },
|
|
||||||
items: { id: string; starred?: Date | boolean }[],
|
|
||||||
): { [id: string]: boolean } {
|
|
||||||
return {
|
|
||||||
...starredType,
|
|
||||||
...items.reduce((acc, val) => {
|
|
||||||
acc[val.id] = !!val.starred
|
|
||||||
return acc
|
|
||||||
}, {} as { [id: string]: boolean }),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const createMusicSlice = (set: SetState<Store>, get: GetState<Store>): MusicSlice => ({
|
export const createMusicSlice = (set: SetState<Store>, get: GetState<Store>): MusicSlice => ({
|
||||||
starredSongs: {},
|
|
||||||
starredAlbums: {},
|
|
||||||
starredArtists: {},
|
|
||||||
|
|
||||||
starItem: async (id, type, unstar = false) => {
|
|
||||||
const client = get().client
|
|
||||||
if (!client) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let params: StarParams
|
|
||||||
let setStarred: (starred: boolean) => void
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case 'song':
|
|
||||||
params = { id }
|
|
||||||
setStarred = starred => {
|
|
||||||
set(
|
|
||||||
produce<MusicSlice>(state => {
|
|
||||||
state.starredSongs = reduceStarred(state.starredSongs, [{ id, starred }])
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case 'album':
|
|
||||||
params = { albumId: id }
|
|
||||||
setStarred = starred => {
|
|
||||||
set(
|
|
||||||
produce<MusicSlice>(state => {
|
|
||||||
state.starredAlbums = reduceStarred(state.starredAlbums, [{ id, starred }])
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case 'artist':
|
|
||||||
params = { artistId: id }
|
|
||||||
setStarred = starred => {
|
|
||||||
set(
|
|
||||||
produce<MusicSlice>(state => {
|
|
||||||
state.starredArtists = reduceStarred(state.starredArtists, [{ id, starred }])
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
setStarred(!unstar)
|
|
||||||
if (unstar) {
|
|
||||||
await client.unstar(params)
|
|
||||||
} else {
|
|
||||||
await client.star(params)
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
setStarred(unstar)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
albumIdCoverArt: {},
|
albumIdCoverArt: {},
|
||||||
albumIdCoverArtRequests: {},
|
albumIdCoverArtRequests: {},
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
GetMusicDirectoryParams,
|
GetMusicDirectoryParams,
|
||||||
GetPlaylistParams,
|
GetPlaylistParams,
|
||||||
GetPlaylistsParams,
|
GetPlaylistsParams,
|
||||||
|
GetSongParams,
|
||||||
GetTopSongsParams,
|
GetTopSongsParams,
|
||||||
ScrobbleParams,
|
ScrobbleParams,
|
||||||
Search3Params,
|
Search3Params,
|
||||||
@@ -29,6 +30,7 @@ import {
|
|||||||
GetMusicDirectoryResponse,
|
GetMusicDirectoryResponse,
|
||||||
GetPlaylistResponse,
|
GetPlaylistResponse,
|
||||||
GetPlaylistsResponse,
|
GetPlaylistsResponse,
|
||||||
|
GetSongResponse,
|
||||||
GetTopSongsResponse,
|
GetTopSongsResponse,
|
||||||
Search3Response,
|
Search3Response,
|
||||||
SubsonicResponse,
|
SubsonicResponse,
|
||||||
@@ -180,6 +182,11 @@ export class SubsonicApiClient {
|
|||||||
return new SubsonicResponse<GetTopSongsResponse>(xml, new GetTopSongsResponse(xml))
|
return new SubsonicResponse<GetTopSongsResponse>(xml, new GetTopSongsResponse(xml))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getSong(params: GetSongParams): Promise<SubsonicResponse<GetSongResponse>> {
|
||||||
|
const xml = await this.apiGetXml('getSong', params)
|
||||||
|
return new SubsonicResponse<GetSongResponse>(xml, new GetSongResponse(xml))
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Album/song lists
|
// Album/song lists
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ export type GetArtistParams = {
|
|||||||
id: string
|
id: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type GetSongParams = {
|
||||||
|
id: string
|
||||||
|
}
|
||||||
|
|
||||||
export type GetTopSongsParams = {
|
export type GetTopSongsParams = {
|
||||||
artist: string
|
artist: string
|
||||||
count?: number
|
count?: number
|
||||||
|
|||||||
@@ -129,6 +129,14 @@ export class GetTopSongsResponse {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class GetSongResponse {
|
||||||
|
song: ChildElement
|
||||||
|
|
||||||
|
constructor(xml: Document) {
|
||||||
|
this.song = new ChildElement(xml.getElementsByTagName('song')[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Album/song lists
|
// Album/song lists
|
||||||
//
|
//
|
||||||
|
|||||||
Reference in New Issue
Block a user