mirror of
https://github.com/austinried/subtracks.git
synced 2026-02-11 07:12:44 +01:00
switching to async storage
also switching to not storing music data from api unless downloaded
This commit is contained in:
@@ -1,6 +1,4 @@
|
||||
import { MusicDb } from "./storage/music";
|
||||
import { SettingsDb } from "./storage/settings";
|
||||
import { SubsonicApiClient } from "./subsonic/api";
|
||||
|
||||
export const musicDb = new MusicDb();
|
||||
export const settingsDb = new SettingsDb();
|
||||
|
||||
@@ -3,8 +3,8 @@ import { Button, TextInput, View, Text } from 'react-native';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import md5 from 'md5';
|
||||
import { musicDb, settingsDb } from '../clients';
|
||||
import { appSettingsState, serversState } from '../state/settings';
|
||||
import { musicDb } from '../clients';
|
||||
import { appSettingsState } from '../state/settings';
|
||||
import { DbStorage } from '../storage/db';
|
||||
import { StackScreenProps } from '@react-navigation/stack';
|
||||
import { useNavigation } from '@react-navigation/core';
|
||||
@@ -36,7 +36,6 @@ const DbControls = () => {
|
||||
return (
|
||||
<View>
|
||||
<RecreateDbButton db={musicDb} title='Music' />
|
||||
<RecreateDbButton db={settingsDb} title='Settings' />
|
||||
<Button
|
||||
title='Now Playing'
|
||||
onPress={() => navigation.navigate('Now Playing')}
|
||||
@@ -46,11 +45,10 @@ const DbControls = () => {
|
||||
}
|
||||
|
||||
const ServerSettingsView = () => {
|
||||
const [servers, setServers] = useRecoilState(serversState);
|
||||
const [appSettings, setAppSettings] = useRecoilState(appSettingsState);
|
||||
|
||||
const bootstrapServer = () => {
|
||||
if (servers.length !== 0) {
|
||||
if (appSettings.servers.length !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -58,14 +56,17 @@ const ServerSettingsView = () => {
|
||||
const salt = uuidv4();
|
||||
const address = 'http://demo.subsonic.org';
|
||||
|
||||
setServers([{
|
||||
id, salt, address,
|
||||
username: 'guest',
|
||||
token: md5('guest' + salt),
|
||||
}]);
|
||||
|
||||
setAppSettings({
|
||||
server: id,
|
||||
...appSettings,
|
||||
servers: [
|
||||
...appSettings.servers,
|
||||
{
|
||||
id, salt, address,
|
||||
username: 'guest',
|
||||
token: md5('guest' + salt),
|
||||
},
|
||||
],
|
||||
activeServer: id,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -75,7 +76,7 @@ const ServerSettingsView = () => {
|
||||
title='Add default server'
|
||||
onPress={bootstrapServer}
|
||||
/>
|
||||
{servers.map(s => (
|
||||
{appSettings.servers.map(s => (
|
||||
<View key={s.id}>
|
||||
<Text>{s.address}</Text>
|
||||
<Text>{s.username}</Text>
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
|
||||
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 { musicDb } from '../clients';
|
||||
import paths from '../paths';
|
||||
|
||||
async function mkdir(path: string): Promise<void> {
|
||||
@@ -30,14 +30,10 @@ const SplashPage: React.FC<{}> = ({ children }) => {
|
||||
await mkdir(paths.songs);
|
||||
|
||||
await musicDb.openDb();
|
||||
await settingsDb.openDb();
|
||||
|
||||
if (!(await musicDb.dbExists())) {
|
||||
await musicDb.createDb();
|
||||
}
|
||||
if (!(await settingsDb.dbExists())) {
|
||||
await settingsDb.createDb();
|
||||
}
|
||||
|
||||
await TrackPlayer.setupPlayer();
|
||||
TrackPlayer.updateOptions({
|
||||
|
||||
@@ -4,14 +4,16 @@ 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 } from '../../state/music';
|
||||
import { albumsState, albumsUpdatingState, useCoverArtUri, useUpdateAlbums } from '../../state/music';
|
||||
import colors from '../../styles/colors';
|
||||
import textStyles from '../../styles/text';
|
||||
import TopTabContainer from '../common/TopTabContainer';
|
||||
|
||||
const AlbumArt: React.FC<{ height: number, width: number, id?: string }> = ({ height, width, id }) => {
|
||||
const coverArtUri = useCoverArtUri(id);
|
||||
|
||||
const AlbumArt: React.FC<{
|
||||
height: number,
|
||||
width: number,
|
||||
coverArtUri?: string
|
||||
}> = ({ height, width, coverArtUri }) => {
|
||||
const Placeholder = (
|
||||
<LinearGradient
|
||||
colors={[colors.accent, colors.accentLow]}
|
||||
@@ -39,27 +41,27 @@ const AlbumArt: React.FC<{ height: number, width: number, id?: string }> = ({ he
|
||||
}
|
||||
const MemoAlbumArt = React.memo(AlbumArt);
|
||||
|
||||
const AlbumItem: React.FC<{ name: string, coverArt?: string } > = ({ name, coverArt }) => {
|
||||
const AlbumItem: React.FC<{
|
||||
name: string,
|
||||
artist?: string,
|
||||
coverArtUri?: string
|
||||
} > = ({ name, artist, coverArtUri }) => {
|
||||
const size = 125;
|
||||
|
||||
return (
|
||||
<View style={{
|
||||
// flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginVertical: 8,
|
||||
// marginLeft: 6,
|
||||
// width: size,
|
||||
flex: 1/3,
|
||||
}}>
|
||||
<MemoAlbumArt
|
||||
width={size}
|
||||
height={size}
|
||||
id={coverArt}
|
||||
coverArtUri={coverArtUri}
|
||||
/>
|
||||
<View style={{
|
||||
flex: 1,
|
||||
width: size,
|
||||
// alignItems: 'baseline',
|
||||
}}>
|
||||
<Text
|
||||
style={{
|
||||
@@ -71,13 +73,10 @@ const AlbumItem: React.FC<{ name: string, coverArt?: string } > = ({ name, cover
|
||||
{name}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
...textStyles.itemSubtitle,
|
||||
// marginTop: 2,
|
||||
}}
|
||||
style={{ ...textStyles.itemSubtitle }}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{name}
|
||||
{artist}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
@@ -86,11 +85,19 @@ const AlbumItem: React.FC<{ name: string, coverArt?: string } > = ({ name, cover
|
||||
const MemoAlbumItem = React.memo(AlbumItem);
|
||||
|
||||
const AlbumListRenderItem: React.FC<{ item: Album }> = ({ item }) => (
|
||||
<MemoAlbumItem name={item.name} coverArt={item.coverArt} />
|
||||
<MemoAlbumItem name={item.name} artist={item.artist} coverArtUri={item.coverArtThumbUri} />
|
||||
);
|
||||
|
||||
const AlbumsList = () => {
|
||||
const albums = useRecoilValue(albumsState);
|
||||
const updating = useRecoilValue(albumsUpdatingState);
|
||||
const updateAlbums = useUpdateAlbums();
|
||||
|
||||
useEffect(() => {
|
||||
if (albums.length === 0) {
|
||||
updateAlbums();
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1 }}>
|
||||
@@ -100,6 +107,8 @@ const AlbumsList = () => {
|
||||
keyExtractor={item => item.id}
|
||||
numColumns={3}
|
||||
removeClippedSubviews={true}
|
||||
refreshing={updating}
|
||||
onRefresh={updateAlbums}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Text, View, Image, FlatList } from 'react-native';
|
||||
import { Artist } from '../../models/music';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useRecoilValue, useResetRecoilState } from 'recoil';
|
||||
import textStyles from '../../styles/text';
|
||||
import TopTabContainer from '../common/TopTabContainer';
|
||||
import { artistsState } from '../../state/music';
|
||||
import { artistsState, artistsUpdatingState, useUpdateArtists } from '../../state/music';
|
||||
|
||||
const ArtistItem: React.FC<{ item: Artist } > = ({ item }) => (
|
||||
<View style={{
|
||||
@@ -29,6 +29,14 @@ const ArtistItem: React.FC<{ item: Artist } > = ({ item }) => (
|
||||
|
||||
const ArtistsList = () => {
|
||||
const artists = useRecoilValue(artistsState);
|
||||
const updating = useRecoilValue(artistsUpdatingState);
|
||||
const updateArtists = useUpdateArtists();
|
||||
|
||||
useEffect(() => {
|
||||
if (artists.length === 0) {
|
||||
updateArtists();
|
||||
}
|
||||
});
|
||||
|
||||
const renderItem: React.FC<{ item: Artist }> = ({ item }) => (
|
||||
<ArtistItem item={item} />
|
||||
@@ -39,6 +47,8 @@ const ArtistsList = () => {
|
||||
data={artists}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={item => item.id}
|
||||
onRefresh={updateArtists}
|
||||
refreshing={updating}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,14 +3,18 @@ export interface Artist {
|
||||
name: string;
|
||||
starred?: Date;
|
||||
coverArt?: string;
|
||||
coverArtUri?: string,
|
||||
}
|
||||
|
||||
export interface Album {
|
||||
id: string;
|
||||
artistId: string;
|
||||
artistId?: string;
|
||||
artist?: string;
|
||||
name: string;
|
||||
starred?: Date;
|
||||
coverArt?: string;
|
||||
coverArtUri?: string,
|
||||
coverArtThumbUri?: string,
|
||||
}
|
||||
|
||||
export interface Song {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export interface ServerSettings {
|
||||
export interface Server {
|
||||
id: string;
|
||||
address: string;
|
||||
username: string;
|
||||
@@ -7,5 +7,6 @@ export interface ServerSettings {
|
||||
}
|
||||
|
||||
export interface AppSettings {
|
||||
server?: string;
|
||||
servers: Server[],
|
||||
activeServer?: string;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { atom, DefaultValue, selector, selectorFamily, useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { atom, DefaultValue, selector, useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { musicDb } from '../clients';
|
||||
import { Album, Artist, Song } from '../models/music';
|
||||
import paths from '../paths';
|
||||
@@ -9,38 +9,85 @@ import RNFS from 'react-native-fs';
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
||||
},
|
||||
],
|
||||
default: [],
|
||||
});
|
||||
|
||||
export const artistsUpdatingState = atom<boolean>({
|
||||
key: 'artistsUpdatingState',
|
||||
default: false,
|
||||
})
|
||||
|
||||
export const useUpdateArtists = () => {
|
||||
const server = useRecoilValue(activeServer);
|
||||
if (!server) {
|
||||
return () => Promise.resolve();
|
||||
}
|
||||
|
||||
const [updating, setUpdating] = useRecoilState(artistsUpdatingState);
|
||||
const setArtists = useSetRecoilState(artistsState);
|
||||
|
||||
return async () => {
|
||||
if (updating) {
|
||||
return;
|
||||
}
|
||||
setUpdating(true);
|
||||
|
||||
const client = new SubsonicApiClient(server);
|
||||
const response = await client.getArtists();
|
||||
|
||||
setArtists(response.data.artists.map(x => ({
|
||||
id: x.id,
|
||||
name: x.name,
|
||||
starred: x.starred,
|
||||
coverArt: x.coverArt,
|
||||
coverArtUri: x.coverArt ? client.getCoverArtUri({ id: x.coverArt }) : undefined,
|
||||
})));
|
||||
setUpdating(false);
|
||||
}
|
||||
}
|
||||
|
||||
export const albumsState = atom<Album[]>({
|
||||
key: 'albumsState',
|
||||
default: selector({
|
||||
key: 'albumsState/default',
|
||||
get: () => musicDb.getAlbums(),
|
||||
}),
|
||||
effects_UNSTABLE: [
|
||||
({ onSet }) => {
|
||||
onSet((newValue) => {
|
||||
if (!(newValue instanceof DefaultValue)) {
|
||||
musicDb.updateAlbums(newValue);
|
||||
}
|
||||
});
|
||||
},
|
||||
],
|
||||
default: [],
|
||||
});
|
||||
|
||||
export const albumsUpdatingState = atom<boolean>({
|
||||
key: 'albumsUpdatingState',
|
||||
default: false,
|
||||
})
|
||||
|
||||
export const useUpdateAlbums = () => {
|
||||
const server = useRecoilValue(activeServer);
|
||||
if (!server) {
|
||||
return () => Promise.resolve();
|
||||
}
|
||||
|
||||
const [updating, setUpdating] = useRecoilState(albumsUpdatingState);
|
||||
const setAlbums = useSetRecoilState(albumsState);
|
||||
|
||||
return async () => {
|
||||
if (updating) {
|
||||
return;
|
||||
}
|
||||
setUpdating(true);
|
||||
|
||||
const client = new SubsonicApiClient(server);
|
||||
const response = await client.getAlbumList2({ type: 'alphabeticalByArtist', size: 500 });
|
||||
|
||||
setAlbums(response.data.albums.map(x => ({
|
||||
id: x.id,
|
||||
artistId: x.artistId,
|
||||
artist: x.artist,
|
||||
name: x.name,
|
||||
starred: x.starred,
|
||||
coverArt: x.coverArt,
|
||||
coverArtUri: x.coverArt ? client.getCoverArtUri({ id: x.coverArt }) : undefined,
|
||||
coverArtThumbUri: x.coverArt ? client.getCoverArtUri({ id: x.coverArt, size: '128' }) : undefined,
|
||||
})));
|
||||
setUpdating(false);
|
||||
}
|
||||
}
|
||||
|
||||
export const songsState = atom<Song[]>({
|
||||
key: 'songsState',
|
||||
default: selector({
|
||||
|
||||
@@ -1,47 +1,28 @@
|
||||
import { atom, DefaultValue, selector } from 'recoil';
|
||||
import { settingsDb } from '../clients';
|
||||
import { AppSettings, ServerSettings } from '../models/settings';
|
||||
|
||||
export const serversState = atom<ServerSettings[]>({
|
||||
key: 'serverState',
|
||||
default: selector({
|
||||
key: 'serversState/default',
|
||||
get: () => settingsDb.getServers(),
|
||||
}),
|
||||
effects_UNSTABLE: [
|
||||
({ onSet }) => {
|
||||
onSet((newValue) => {
|
||||
if (!(newValue instanceof DefaultValue)) {
|
||||
settingsDb.updateServers(newValue);
|
||||
}
|
||||
});
|
||||
}
|
||||
],
|
||||
});
|
||||
import { AppSettings, Server } from '../models/settings';
|
||||
import { getAppSettings, setAppSettings } from '../storage/settings';
|
||||
|
||||
export const appSettingsState = atom<AppSettings>({
|
||||
key: 'appSettingsState',
|
||||
default: selector({
|
||||
key: 'appSettingsState/default',
|
||||
get: () => settingsDb.getApp(),
|
||||
get: () => getAppSettings(),
|
||||
}),
|
||||
effects_UNSTABLE: [
|
||||
({ onSet }) => {
|
||||
onSet((newValue, oldValue) => {
|
||||
if (!(newValue instanceof DefaultValue)) {
|
||||
settingsDb.updateApp(newValue);
|
||||
setAppSettings(newValue);
|
||||
}
|
||||
});
|
||||
}
|
||||
],
|
||||
});
|
||||
|
||||
export const activeServer = selector<ServerSettings | undefined>({
|
||||
export const activeServer = selector<Server | undefined>({
|
||||
key: 'activeServer',
|
||||
get: ({get}) => {
|
||||
const appSettings = get(appSettingsState);
|
||||
const servers = get(serversState);
|
||||
|
||||
return servers.find(x => x.id == appSettings.server);
|
||||
return appSettings.servers.find(x => x.id == appSettings.activeServer);
|
||||
}
|
||||
});
|
||||
|
||||
98
src/storage/asyncstorage.ts
Normal file
98
src/storage/asyncstorage.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
|
||||
const key = {
|
||||
downloadedSongKeys: '@downloadedSongKeys',
|
||||
downloadedAlbumKeys: '@downloadedAlbumKeys',
|
||||
downloadedArtistKeys: '@downloadedArtistKeys',
|
||||
downloadedPlaylistKeys: '@downloadedPlaylistKeys',
|
||||
};
|
||||
|
||||
export async function getItem(key: string): Promise<string | null> {
|
||||
try {
|
||||
return await AsyncStorage.getItem(key);
|
||||
} catch (e) {
|
||||
console.error(`getItem error (key: ${key})`, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function multiGet(keys: string[]): Promise<[string, string | null][]> {
|
||||
try {
|
||||
return await AsyncStorage.multiGet(keys);
|
||||
} catch (e) {
|
||||
console.error(`multiGet error`, e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export async function setItem(key: string, item: string): Promise<void> {
|
||||
try {
|
||||
await AsyncStorage.setItem(key, item);
|
||||
} catch (e) {
|
||||
console.error(`setItem error (key: ${key})`, e);
|
||||
}
|
||||
}
|
||||
|
||||
export async function multiSet(items: string[][]): Promise<void> {
|
||||
try {
|
||||
await AsyncStorage.multiSet(items);
|
||||
} catch (e) {
|
||||
console.error(`multiSet error`, e);
|
||||
}
|
||||
}
|
||||
|
||||
type DownloadedSong = {
|
||||
id: string;
|
||||
type: 'song';
|
||||
name: string;
|
||||
album: string;
|
||||
artist: string;
|
||||
};
|
||||
|
||||
type DownloadedAlbum = {
|
||||
id: string;
|
||||
type: 'album';
|
||||
songs: string[];
|
||||
name: string;
|
||||
artist: string;
|
||||
};
|
||||
|
||||
type DownloadedArtist = {
|
||||
id: string;
|
||||
type: 'artist';
|
||||
songs: string[];
|
||||
name: string;
|
||||
};
|
||||
|
||||
type DownloadedPlaylist = {
|
||||
id: string;
|
||||
type: 'playlist';
|
||||
songs: string[];
|
||||
name: string;
|
||||
};
|
||||
|
||||
export async function getDownloadedSongs(): Promise<DownloadedSong[]> {
|
||||
const keysItem = await getItem(key.downloadedSongKeys);
|
||||
const keys: string[] = keysItem ? JSON.parse(keysItem) : [];
|
||||
|
||||
const items = await multiGet(keys);
|
||||
return items.map(x => {
|
||||
const parsed = JSON.parse(x[1] as string);
|
||||
return {
|
||||
id: x[0],
|
||||
type: 'song',
|
||||
...parsed,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export async function setDownloadedSongs(items: DownloadedSong[]): Promise<void> {
|
||||
await multiSet([
|
||||
[key.downloadedSongKeys, JSON.stringify(items.map(x => x.id))],
|
||||
...items.map(x => [x.id, JSON.stringify({
|
||||
name: x.name,
|
||||
album: x.album,
|
||||
artist: x.artist,
|
||||
})]),
|
||||
]);
|
||||
}
|
||||
@@ -1,92 +1,15 @@
|
||||
import { AppSettings, ServerSettings } from '../models/settings';
|
||||
import { DbStorage } from './db';
|
||||
import { AppSettings } from '../models/settings';
|
||||
import { getItem, setItem } from './asyncstorage';
|
||||
|
||||
export class SettingsDb extends DbStorage {
|
||||
constructor() {
|
||||
super({ name: 'settings.db', location: 'Library' });
|
||||
}
|
||||
const appSettingsKey = '@appSettings';
|
||||
|
||||
async createDb(): Promise<void> {
|
||||
await this.initDb(tx => {
|
||||
tx.executeSql(`
|
||||
CREATE TABLE servers (
|
||||
id TEXT PRIMARY KEY NOT NULL,
|
||||
address TEXT NOT NULL,
|
||||
username TEXT NOT NULL,
|
||||
token TEXT NOT NULL,
|
||||
salt TEXT NOT NULL
|
||||
);
|
||||
`);
|
||||
tx.executeSql(`
|
||||
CREATE TABLE app (
|
||||
server TEXT
|
||||
);
|
||||
`);
|
||||
tx.executeSql(`
|
||||
INSERT INTO app (server)
|
||||
VALUES (NULL);
|
||||
`);
|
||||
});
|
||||
}
|
||||
|
||||
async getServers(): Promise<ServerSettings[]> {
|
||||
return (await this.executeSql(`
|
||||
SELECT * FROM servers;
|
||||
`))[0].rows.raw().map(x => ({
|
||||
id: x.id,
|
||||
address: x.address,
|
||||
username: x.username,
|
||||
token: x.token,
|
||||
salt: x.salt,
|
||||
}));
|
||||
}
|
||||
|
||||
async getServer(id: string): Promise<ServerSettings | undefined> {
|
||||
return (await this.getServers()).find(x => x.id === id);
|
||||
}
|
||||
|
||||
// async addServer(server: ServerSettings): Promise<void> {
|
||||
// await this.executeSql(`
|
||||
// INSERT INTO servers (id, address, username, token, salt)
|
||||
// VALUES (?, ?, ?, ?, ?);
|
||||
// `, [server.id, server.address, server.username, server.token, server.salt]);
|
||||
// }
|
||||
|
||||
// async removeServer(id: string): Promise<void> {
|
||||
// await this.executeSql(`
|
||||
// DELETE FROM servers
|
||||
// WHERE id = ?;
|
||||
// `, [id]);
|
||||
// }
|
||||
|
||||
async updateServers(servers: ServerSettings[]): Promise<void> {
|
||||
await this.transaction((tx) => {
|
||||
tx.executeSql(`
|
||||
DELETE FROM servers
|
||||
`);
|
||||
for (const s of servers) {
|
||||
tx.executeSql(`
|
||||
INSERT INTO servers (id, address, username, token, salt)
|
||||
VALUES (?, ?, ?, ?, ?);
|
||||
`, [s.id, s.address, s.username, s.token, s.salt]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async getApp(): Promise<AppSettings> {
|
||||
return (await this.executeSql(`
|
||||
SELECT * FROM app;
|
||||
`))[0].rows.raw().map(x => ({
|
||||
server: x.server || undefined,
|
||||
}))[0];
|
||||
}
|
||||
|
||||
async updateApp(app: AppSettings): Promise<void> {
|
||||
await this.executeSql(`
|
||||
UPDATE app SET
|
||||
server = ?;
|
||||
`, [
|
||||
app.server || null,
|
||||
]);
|
||||
}
|
||||
export async function getAppSettings(): Promise<AppSettings> {
|
||||
const item = await getItem(appSettingsKey);
|
||||
return item ? JSON.parse(item) : {
|
||||
servers: [],
|
||||
};
|
||||
}
|
||||
|
||||
export async function setAppSettings(appSettings: AppSettings): Promise<void> {
|
||||
await setItem(appSettingsKey, JSON.stringify(appSettings));
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { DOMParser } from 'xmldom';
|
||||
import RNFS from 'react-native-fs';
|
||||
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 { Server } from '../models/settings';
|
||||
import paths from '../paths';
|
||||
|
||||
export class SubsonicApiError extends Error {
|
||||
@@ -58,7 +58,7 @@ export class SubsonicApiClient {
|
||||
|
||||
private params: URLSearchParams
|
||||
|
||||
constructor(server: ServerSettings) {
|
||||
constructor(server: Server) {
|
||||
this.address = server.address;
|
||||
this.username = server.username;
|
||||
|
||||
@@ -80,7 +80,7 @@ export class SubsonicApiClient {
|
||||
}
|
||||
|
||||
const url = `${this.address}/rest/${method}?${query}`;
|
||||
console.log(url);
|
||||
// console.log(url);
|
||||
return url;
|
||||
}
|
||||
|
||||
@@ -189,4 +189,8 @@ export class SubsonicApiClient {
|
||||
const path = `${paths.songCache}/${params.id}`;
|
||||
return await this.apiDownload('getCoverArt', path, params);
|
||||
}
|
||||
|
||||
getCoverArtUri(params: GetCoverArtParams): string {
|
||||
return this.buildUrl('getCoverArt', params);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user