mirror of
https://github.com/austinried/subtracks.git
synced 2025-12-27 09:09:29 +01:00
impl songs, whole library refresh
This commit is contained in:
parent
c8ed5bf5cb
commit
50be0a6f85
2
App.tsx
2
App.tsx
@ -3,10 +3,12 @@ import { NavigationContainer } from '@react-navigation/native';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import SplashPage from './src/components/SplashPage';
|
||||
import RootNavigator from './src/components/navigation/RootNavigator';
|
||||
import MusicManager from './src/components/MusicManager';
|
||||
|
||||
const App = () => (
|
||||
<RecoilRoot>
|
||||
<SplashPage>
|
||||
<MusicManager />
|
||||
<NavigationContainer>
|
||||
<RootNavigator />
|
||||
</NavigationContainer>
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
import { Button, FlatList, Text, View } from 'react-native';
|
||||
import { useRecoilValue, useResetRecoilState } from 'recoil';
|
||||
import { artistsState, useUpdateArtists } from '../state/artists';
|
||||
import { useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil';
|
||||
import { Artist } from '../models/music';
|
||||
import { artistsState, isLibraryRefreshingState, libraryRefreshState } from '../state/music';
|
||||
|
||||
const ArtistItem: React.FC<{ item: Artist } > = ({ item }) => (
|
||||
<View>
|
||||
@ -32,7 +32,8 @@ const List = () => {
|
||||
|
||||
const ListPlusControls = () => {
|
||||
const resetArtists = useResetRecoilState(artistsState);
|
||||
const updateArtists = useUpdateArtists();
|
||||
const setLibraryRefresh = useSetRecoilState(libraryRefreshState);
|
||||
const isLibraryRefreshing = useRecoilValue(isLibraryRefreshingState);
|
||||
|
||||
return (
|
||||
<View>
|
||||
@ -41,8 +42,9 @@ const ListPlusControls = () => {
|
||||
onPress={resetArtists}
|
||||
/>
|
||||
<Button
|
||||
title='Update from API'
|
||||
onPress={updateArtists}
|
||||
title='Refresh Library'
|
||||
onPress={() => setLibraryRefresh(true)}
|
||||
disabled={isLibraryRefreshing}
|
||||
/>
|
||||
<List />
|
||||
</View>
|
||||
|
||||
104
src/components/MusicManager.tsx
Normal file
104
src/components/MusicManager.tsx
Normal file
@ -0,0 +1,104 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { ActivityIndicator, useWindowDimensions, View } from 'react-native';
|
||||
import { useSetRecoilState, useRecoilValue, useRecoilState } from 'recoil';
|
||||
import { Album, Artist, Song } from '../models/music';
|
||||
import { albumsState, artistsState, isLibraryRefreshingState, libraryRefreshState, songsState } from '../state/music';
|
||||
import { activeServer } from '../state/settings';
|
||||
import colors from '../styles/colors';
|
||||
import { SubsonicApiClient } from '../subsonic/api';
|
||||
|
||||
const RefreshManager = () => {
|
||||
const setArtists = useSetRecoilState(artistsState);
|
||||
const setAlbums = useSetRecoilState(albumsState);
|
||||
const setSongs = useSetRecoilState(songsState);
|
||||
|
||||
const server = useRecoilValue(activeServer);
|
||||
|
||||
const [libraryRefresh, setLibraryRefresh] = useRecoilState(libraryRefreshState);
|
||||
const [isLibraryRefreshing, setIsLibraryRefreshing] = useRecoilState(isLibraryRefreshingState);
|
||||
|
||||
const updateLibrary = async () => {
|
||||
if (!libraryRefresh) {
|
||||
return;
|
||||
}
|
||||
setLibraryRefresh(false);
|
||||
|
||||
if (isLibraryRefreshing) {
|
||||
return;
|
||||
}
|
||||
setIsLibraryRefreshing(true);
|
||||
|
||||
if (!server) {
|
||||
return;
|
||||
}
|
||||
const client = new SubsonicApiClient(server);
|
||||
|
||||
const artistsResponse = await client.getArtists();
|
||||
const artists: Artist[] = artistsResponse.data.artists.map(x => ({
|
||||
id: x.id,
|
||||
name: x.name,
|
||||
starred: x.starred,
|
||||
coverArt: x.coverArt,
|
||||
}));
|
||||
setArtists(artists);
|
||||
|
||||
const albumsResponse = await client.getAlbumList2({ type: 'alphabeticalByArtist', size: 500 });
|
||||
const albums: Album[] = albumsResponse.data.albums
|
||||
.filter(x => x.artistId !== undefined)
|
||||
.map(x => ({
|
||||
id: x.id,
|
||||
artistId: x.artistId as string,
|
||||
name: x.name,
|
||||
coverArt: x.coverArt,
|
||||
}));
|
||||
setAlbums(albums);
|
||||
|
||||
const songs: Song[] = [];
|
||||
for (const album of albums) {
|
||||
const songsResponse = await client.getAlbum({ id: album.id });
|
||||
const albumSongs: Song[] = songsResponse.data.songs.map(x => ({
|
||||
id: x.id,
|
||||
albumId: album.id,
|
||||
artistId: album.artistId,
|
||||
name: x.title,
|
||||
starred: x.starred,
|
||||
}));
|
||||
songs.push(...albumSongs);
|
||||
}
|
||||
setSongs(songs);
|
||||
|
||||
setIsLibraryRefreshing(false);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
updateLibrary();
|
||||
});
|
||||
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const MusicManager = () => {
|
||||
const isLibraryRefreshing = useRecoilValue(isLibraryRefreshingState);
|
||||
const layout = useWindowDimensions();
|
||||
|
||||
const RefreshIndicator = () => (
|
||||
<ActivityIndicator size={'large'} color={colors.accent} style={{
|
||||
backgroundColor: colors.accentLow,
|
||||
position: 'absolute',
|
||||
left: layout.width / 2 - 18,
|
||||
top: layout.height / 2 - 18,
|
||||
elevation: 999,
|
||||
}}/>
|
||||
);
|
||||
|
||||
return (
|
||||
<View>
|
||||
{isLibraryRefreshing ? <RefreshIndicator /> : <></>}
|
||||
<React.Suspense fallback={<></>}>
|
||||
<RefreshManager />
|
||||
</React.Suspense>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export default MusicManager;
|
||||
@ -3,6 +3,7 @@ import { Text, View } from 'react-native';
|
||||
import RNFS from 'react-native-fs';
|
||||
import TrackPlayer, { Track } from 'react-native-track-player';
|
||||
import { musicDb, settingsDb } from '../clients';
|
||||
import paths from '../paths';
|
||||
|
||||
async function mkdir(path: string): Promise<void> {
|
||||
const exists = await RNFS.exists(path);
|
||||
@ -24,11 +25,9 @@ const SplashPage: React.FC<{}> = ({ children }) => {
|
||||
const minSplashTime = new Promise(resolve => setTimeout(resolve, 1));
|
||||
|
||||
const prepare = async () => {
|
||||
const filesPath = RNFS.DocumentDirectoryPath;
|
||||
|
||||
await mkdir(`${filesPath}/image_cache`);
|
||||
await mkdir(`${filesPath}/song_cache`);
|
||||
await mkdir(`${filesPath}/songs`);
|
||||
await mkdir(paths.imageCache);
|
||||
await mkdir(paths.songCache);
|
||||
await mkdir(paths.songs);
|
||||
|
||||
await musicDb.openDb();
|
||||
await settingsDb.openDb();
|
||||
|
||||
@ -4,7 +4,7 @@ import FastImage from 'react-native-fast-image';
|
||||
import LinearGradient from 'react-native-linear-gradient';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { Album } from '../../models/music';
|
||||
import { albumsState, useCoverArtUri, useUpdateAlbums } from '../../state/albums';
|
||||
import { albumsState, useCoverArtUri } from '../../state/music';
|
||||
import colors from '../../styles/colors';
|
||||
import textStyles from '../../styles/text';
|
||||
import TopTabContainer from '../common/TopTabContainer';
|
||||
@ -91,30 +91,13 @@ const AlbumListRenderItem: React.FC<{ item: Album }> = ({ item }) => (
|
||||
|
||||
const AlbumsList = () => {
|
||||
const albums = useRecoilValue(albumsState);
|
||||
const updateAlbums = useUpdateAlbums();
|
||||
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
|
||||
const refresh = async () => {
|
||||
setRefreshing(true);
|
||||
await updateAlbums();
|
||||
setRefreshing(false);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!refreshing && Object.keys(albums).length === 0) {
|
||||
refresh();
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1 }}>
|
||||
<FlatList
|
||||
data={Object.values(albums)}
|
||||
data={albums}
|
||||
renderItem={AlbumListRenderItem}
|
||||
keyExtractor={item => item.id}
|
||||
onRefresh={refresh}
|
||||
refreshing={refreshing}
|
||||
numColumns={3}
|
||||
removeClippedSubviews={true}
|
||||
/>
|
||||
|
||||
@ -2,9 +2,9 @@ import React from 'react';
|
||||
import { Text, View, Image, FlatList } from 'react-native';
|
||||
import { Artist } from '../../models/music';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { artistsState } from '../../state/artists';
|
||||
import textStyles from '../../styles/text';
|
||||
import TopTabContainer from '../common/TopTabContainer';
|
||||
import { artistsState } from '../../state/music';
|
||||
|
||||
const ArtistItem: React.FC<{ item: Artist } > = ({ item }) => (
|
||||
<View style={{
|
||||
|
||||
@ -7,7 +7,16 @@ export interface Artist {
|
||||
|
||||
export interface Album {
|
||||
id: string;
|
||||
artistId: string;
|
||||
name: string;
|
||||
starred?: Date;
|
||||
coverArt?: string;
|
||||
}
|
||||
|
||||
export interface Song {
|
||||
id: string;
|
||||
albumId: string;
|
||||
artistId: string;
|
||||
name: string;
|
||||
starred?: Date;
|
||||
}
|
||||
|
||||
7
src/paths.ts
Normal file
7
src/paths.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import RNFS from 'react-native-fs';
|
||||
|
||||
export default {
|
||||
imageCache: `${RNFS.DocumentDirectoryPath}/image_cache`,
|
||||
songCache: `${RNFS.DocumentDirectoryPath}/song_cache`,
|
||||
songs: `${RNFS.DocumentDirectoryPath}/songs`,
|
||||
};
|
||||
@ -1,42 +0,0 @@
|
||||
import { atom, DefaultValue, selector, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { SubsonicApiClient } from '../subsonic/api';
|
||||
import { activeServer } from './settings'
|
||||
import { Artist } from '../models/music';
|
||||
import { musicDb } from '../clients';
|
||||
|
||||
export const artistsState = atom<Artist[]>({
|
||||
key: 'artistsState',
|
||||
default: selector({
|
||||
key: 'artistsState/default',
|
||||
get: () => musicDb.getArtists(),
|
||||
}),
|
||||
effects_UNSTABLE: [
|
||||
({ onSet }) => {
|
||||
onSet((newValue) => {
|
||||
if (!(newValue instanceof DefaultValue)) {
|
||||
musicDb.updateArtists(newValue);
|
||||
}
|
||||
});
|
||||
}
|
||||
],
|
||||
});
|
||||
|
||||
export const useUpdateArtists = () => {
|
||||
const setArtists = useSetRecoilState(artistsState);
|
||||
const server = useRecoilValue(activeServer);
|
||||
|
||||
return async () => {
|
||||
if (!server) {
|
||||
return;
|
||||
}
|
||||
|
||||
const client = new SubsonicApiClient(server);
|
||||
const response = await client.getArtists();
|
||||
|
||||
setArtists(response.data.artists.map(x => ({
|
||||
id: x.id,
|
||||
name: x.name,
|
||||
coverArt: x.coverArt,
|
||||
})));
|
||||
};
|
||||
};
|
||||
@ -1,12 +1,30 @@
|
||||
import { atom, DefaultValue, selector, selectorFamily, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { SubsonicApiClient } from '../subsonic/api';
|
||||
import { activeServer } from './settings'
|
||||
import { Album } from '../models/music';
|
||||
import { musicDb } from '../clients';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { atom, DefaultValue, selector, selectorFamily, useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { musicDb } from '../clients';
|
||||
import { Album, Artist, Song } from '../models/music';
|
||||
import paths from '../paths';
|
||||
import { SubsonicApiClient } from '../subsonic/api';
|
||||
import { activeServer } from './settings';
|
||||
import RNFS from 'react-native-fs';
|
||||
|
||||
export const albumsState = atom<{ [id: string]: Album }>({
|
||||
export const artistsState = atom<Artist[]>({
|
||||
key: 'artistsState',
|
||||
default: selector({
|
||||
key: 'artistsState/default',
|
||||
get: () => musicDb.getArtists(),
|
||||
}),
|
||||
effects_UNSTABLE: [
|
||||
({ onSet }) => {
|
||||
onSet((newValue) => {
|
||||
if (!(newValue instanceof DefaultValue)) {
|
||||
musicDb.updateArtists(newValue);
|
||||
}
|
||||
});
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
export const albumsState = atom<Album[]>({
|
||||
key: 'albumsState',
|
||||
default: selector({
|
||||
key: 'albumsState/default',
|
||||
@ -16,44 +34,39 @@ export const albumsState = atom<{ [id: string]: Album }>({
|
||||
({ onSet }) => {
|
||||
onSet((newValue) => {
|
||||
if (!(newValue instanceof DefaultValue)) {
|
||||
musicDb.updateAlbums(Object.values(newValue));
|
||||
musicDb.updateAlbums(newValue);
|
||||
}
|
||||
});
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
export const albumState = selectorFamily<Album, string>({
|
||||
key: 'albumState',
|
||||
get: id => ({ get }) => {
|
||||
return get(albumsState)[id];
|
||||
},
|
||||
export const songsState = atom<Song[]>({
|
||||
key: 'songsState',
|
||||
default: selector({
|
||||
key: 'songsState/default',
|
||||
get: () => musicDb.getSongs(),
|
||||
}),
|
||||
effects_UNSTABLE: [
|
||||
({ onSet }) => {
|
||||
onSet((newValue) => {
|
||||
if (!(newValue instanceof DefaultValue)) {
|
||||
musicDb.updateSongs(newValue);
|
||||
}
|
||||
});
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
export const useUpdateAlbums = () => {
|
||||
const setAlbums = useSetRecoilState(albumsState);
|
||||
const server = useRecoilValue(activeServer);
|
||||
export const libraryRefreshState = atom<boolean>({
|
||||
key: 'libraryRefreshState',
|
||||
default: false,
|
||||
});
|
||||
|
||||
return async () => {
|
||||
if (!server) {
|
||||
return;
|
||||
}
|
||||
|
||||
const client = new SubsonicApiClient(server);
|
||||
const response = await client.getAlbumList2({ type: 'alphabeticalByArtist', size: 50 });
|
||||
|
||||
const albums = response.data.albums.reduce((acc, x) => {
|
||||
acc[x.id] = {
|
||||
id: x.id,
|
||||
name: x.name,
|
||||
coverArt: x.coverArt,
|
||||
};
|
||||
return acc;
|
||||
}, {} as { [id: string]: Album });
|
||||
|
||||
setAlbums(albums);
|
||||
};
|
||||
};
|
||||
export const isLibraryRefreshingState = atom<boolean>({
|
||||
key: 'isLibraryRefreshingState',
|
||||
default: false,
|
||||
});
|
||||
|
||||
export function useCoverArtUri(id?: string): string | undefined {
|
||||
if (!id) {
|
||||
@ -70,7 +83,7 @@ export function useCoverArtUri(id?: string): string | undefined {
|
||||
return;
|
||||
}
|
||||
|
||||
const filePath = `${RNFS.DocumentDirectoryPath}/image_cache/${id}`;
|
||||
const filePath = `${paths.songCache}/${id}`;
|
||||
const fileUri = `file://${filePath}`;
|
||||
|
||||
if (await RNFS.exists(filePath)) {
|
||||
@ -1,4 +1,4 @@
|
||||
import { Album, Artist } from '../models/music';
|
||||
import { Album, Artist, Song } from '../models/music';
|
||||
import { DbStorage } from './db';
|
||||
|
||||
export class MusicDb extends DbStorage {
|
||||
@ -19,11 +19,22 @@ export class MusicDb extends DbStorage {
|
||||
tx.executeSql(`
|
||||
CREATE TABLE albums (
|
||||
id TEXT PRIMARY KEY NOT NULL,
|
||||
artistId TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
starred TEXT,
|
||||
coverArt TEXT
|
||||
);
|
||||
`);
|
||||
tx.executeSql(`
|
||||
CREATE TABLE songs (
|
||||
id TEXT PRIMARY KEY NOT NULL,
|
||||
albumId TEXT NOT NULL,
|
||||
artistId TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
starred TEXT,
|
||||
artist TEXT
|
||||
);
|
||||
`);
|
||||
});
|
||||
}
|
||||
|
||||
@ -62,39 +73,16 @@ export class MusicDb extends DbStorage {
|
||||
});
|
||||
}
|
||||
|
||||
async getAlbum(id: string): Promise<Album> {
|
||||
const results = await this.executeSql(`
|
||||
SELECT * FROM albums
|
||||
WHERE id = ?;
|
||||
`, [id]);
|
||||
|
||||
const rows = results[0].rows.raw();
|
||||
return rows.map(x => ({
|
||||
async getAlbums(): Promise<Album[]> {
|
||||
return (await this.executeSql(`
|
||||
SELECT * FROM albums;
|
||||
`))[0].rows.raw().map(x => ({
|
||||
id: x.id,
|
||||
artistId: x.artistid,
|
||||
name: x.name,
|
||||
starred: x.starred ? new Date(x.starred) : undefined,
|
||||
coverArt: x.coverArt || undefined,
|
||||
}))[0];
|
||||
}
|
||||
|
||||
async getAlbumIds(): Promise<string[]> {
|
||||
return (await this.executeSql(`
|
||||
SELECT id FROM albums;
|
||||
`))[0].rows.raw().map(x => x.id);
|
||||
}
|
||||
|
||||
async getAlbums(): Promise<{[id: string]: Album}> {
|
||||
return (await this.executeSql(`
|
||||
SELECT * FROM albums;
|
||||
`))[0].rows.raw().reduce((acc, x) => {
|
||||
acc[x.id] = {
|
||||
id: x.id,
|
||||
name: x.name,
|
||||
starred: x.starred ? new Date(x.starred) : undefined,
|
||||
coverArt: x.coverArt || undefined,
|
||||
};
|
||||
return acc;
|
||||
}, {});
|
||||
}));
|
||||
}
|
||||
|
||||
async updateAlbums(albums: Album[]): Promise<void> {
|
||||
@ -106,18 +94,58 @@ export class MusicDb extends DbStorage {
|
||||
tx.executeSql(`
|
||||
INSERT INTO albums (
|
||||
id,
|
||||
artistId,
|
||||
name,
|
||||
starred,
|
||||
coverArt
|
||||
)
|
||||
VALUES (?, ?, ?, ?);
|
||||
VALUES (?, ?, ?, ?, ?);
|
||||
`, [
|
||||
a.id,
|
||||
a.name,
|
||||
a.starred ? a.starred.toISOString() : null,
|
||||
a.id,
|
||||
a.artistId,
|
||||
a.name,
|
||||
a.starred ? a.starred.toISOString() : null,
|
||||
a.coverArt || null
|
||||
]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async getSongs(): Promise<Song[]> {
|
||||
return (await this.executeSql(`
|
||||
SELECT * FROM songs;
|
||||
`))[0].rows.raw().map(x => ({
|
||||
id: x.id,
|
||||
artistId: x.artistid,
|
||||
albumId: x.albumId,
|
||||
name: x.name,
|
||||
starred: x.starred ? new Date(x.starred) : undefined,
|
||||
}));
|
||||
}
|
||||
|
||||
async updateSongs(songs: Song[]): Promise<void> {
|
||||
await this.transaction((tx) => {
|
||||
tx.executeSql(`
|
||||
DELETE FROM songs
|
||||
`);
|
||||
for (const x of songs) {
|
||||
tx.executeSql(`
|
||||
INSERT INTO songs (
|
||||
id,
|
||||
artistId,
|
||||
albumId,
|
||||
name,
|
||||
starred
|
||||
)
|
||||
VALUES (?, ?, ?, ?, ?);
|
||||
`, [
|
||||
x.id,
|
||||
x.artistId,
|
||||
x.albumId,
|
||||
x.name,
|
||||
x.starred ? x.starred.toISOString() : null,
|
||||
]);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import { DOMParser } from 'xmldom';
|
||||
import RNFS from 'react-native-fs';
|
||||
import { GetAlbumList2Params, GetAlbumListParams, GetArtistInfo2Params, GetArtistInfoParams, GetCoverArtParams, GetIndexesParams } from './params';
|
||||
import { GetAlbumList2Response, GetAlbumListResponse, GetArtistInfo2Response, GetArtistInfoResponse, GetArtistsResponse, GetIndexesResponse, SubsonicResponse } from './responses';
|
||||
import { GetAlbumList2Params, GetAlbumListParams, GetAlbumParams, GetArtistInfo2Params, GetArtistInfoParams, GetCoverArtParams, GetIndexesParams, GetMusicDirectoryParams } from './params';
|
||||
import { GetAlbumList2Response, GetAlbumListResponse, GetAlbumResponse, GetArtistInfo2Response, GetArtistInfoResponse, GetArtistsResponse, GetIndexesResponse, GetMusicDirectoryResponse, SubsonicResponse } from './responses';
|
||||
import { ServerSettings } from '../models/settings';
|
||||
import paths from '../paths';
|
||||
|
||||
export class SubsonicApiError extends Error {
|
||||
method: string;
|
||||
@ -146,6 +147,16 @@ export class SubsonicApiClient {
|
||||
return new SubsonicResponse<GetIndexesResponse>(xml, new GetIndexesResponse(xml));
|
||||
}
|
||||
|
||||
async getMusicDirectory(params: GetMusicDirectoryParams): Promise<SubsonicResponse<GetMusicDirectoryResponse>> {
|
||||
const xml = await this.apiGetXml('getMusicDirectory', params);
|
||||
return new SubsonicResponse<GetMusicDirectoryResponse>(xml, new GetMusicDirectoryResponse(xml));
|
||||
}
|
||||
|
||||
async getAlbum(params: GetAlbumParams): Promise<SubsonicResponse<GetAlbumResponse>> {
|
||||
const xml = await this.apiGetXml('getAlbum', params);
|
||||
return new SubsonicResponse<GetAlbumResponse>(xml, new GetAlbumResponse(xml));
|
||||
}
|
||||
|
||||
async getArtistInfo(params: GetArtistInfoParams): Promise<SubsonicResponse<GetArtistInfoResponse>> {
|
||||
const xml = await this.apiGetXml('getArtistInfo', params);
|
||||
return new SubsonicResponse<GetArtistInfoResponse>(xml, new GetArtistInfoResponse(xml));
|
||||
@ -175,7 +186,7 @@ export class SubsonicApiClient {
|
||||
//
|
||||
|
||||
async getCoverArt(params: GetCoverArtParams): Promise<string> {
|
||||
const path = `${RNFS.DocumentDirectoryPath}/image_cache/${params.id}`;
|
||||
const path = `${paths.songCache}/${params.id}`;
|
||||
return await this.apiDownload('getCoverArt', path, params);
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,6 +72,25 @@ export class ArtistElement extends BaseArtistElement {
|
||||
}
|
||||
}
|
||||
|
||||
export class DirectoryElement {
|
||||
id: string;
|
||||
parent?: string;
|
||||
name: string;
|
||||
starred?: Date;
|
||||
userRating?: number;
|
||||
averageRating?: number;
|
||||
playCount?: number;
|
||||
|
||||
constructor(e: Element) {
|
||||
this.id = requiredString(e, 'id');
|
||||
this.parent = optionalString(e, 'parent');
|
||||
this.name = requiredString(e, 'name');
|
||||
this.starred = optionalDate(e, 'starred');
|
||||
this.userRating = optionalInt(e, 'userRating');
|
||||
this.averageRating = optionalFloat(e, 'averageRating');
|
||||
}
|
||||
}
|
||||
|
||||
export class ChildElement {
|
||||
id: string;
|
||||
parent?: string;
|
||||
|
||||
@ -15,6 +15,14 @@ export type GetArtistInfoParams = {
|
||||
|
||||
export type GetArtistInfo2Params = GetArtistInfoParams;
|
||||
|
||||
export type GetMusicDirectoryParams = {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export type GetAlbumParams = {
|
||||
id: string;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Album/song lists
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { AlbumID3Element, ArtistElement, ArtistID3Element, BaseArtistElement, ChildElement } from "./elements";
|
||||
import { AlbumID3Element, ArtistElement, ArtistID3Element, BaseArtistElement, ChildElement, DirectoryElement } from "./elements";
|
||||
|
||||
export type ResponseStatus = 'ok' | 'failed';
|
||||
|
||||
@ -98,6 +98,34 @@ export class GetArtistInfo2Response extends BaseGetArtistInfoResponse<ArtistID3E
|
||||
}
|
||||
}
|
||||
|
||||
export class GetMusicDirectoryResponse {
|
||||
directory: DirectoryElement;
|
||||
children: ChildElement[] = [];
|
||||
|
||||
constructor(xml: Document) {
|
||||
this.directory = new DirectoryElement(xml.getElementsByTagName('directory')[0]);
|
||||
|
||||
const childElements = xml.getElementsByTagName('child');
|
||||
for (let i = 0; i < childElements.length; i++) {
|
||||
this.children.push(new ChildElement(childElements[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class GetAlbumResponse {
|
||||
album: AlbumID3Element;
|
||||
songs: ChildElement[] = [];
|
||||
|
||||
constructor(xml: Document) {
|
||||
this.album = new AlbumID3Element(xml.getElementsByTagName('album')[0]);
|
||||
|
||||
const childElements = xml.getElementsByTagName('song');
|
||||
for (let i = 0; i < childElements.length; i++) {
|
||||
this.songs.push(new ChildElement(childElements[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Album/song lists
|
||||
//
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user