mirror of
https://github.com/austinried/subtracks.git
synced 2025-12-27 09:09:29 +01:00
impl scrobble & scrobble setting
works even in background thanks zustand
This commit is contained in:
parent
706e57aa77
commit
efc7e5c799
@ -4,6 +4,7 @@ export interface Server {
|
||||
username: string
|
||||
token: string
|
||||
salt: string
|
||||
scrobble: boolean
|
||||
}
|
||||
|
||||
export interface AppSettings {
|
||||
|
||||
@ -1,8 +1,36 @@
|
||||
import { getCurrentTrack, getPlayerState, trackPlayerCommands } from '@app/state/trackplayer'
|
||||
import TrackPlayer, { Event } from 'react-native-track-player'
|
||||
import { getCurrentTrack, getPlayerState, TrackExt, trackPlayerCommands } from '@app/state/trackplayer'
|
||||
import TrackPlayer, { Event, State } from 'react-native-track-player'
|
||||
import { useStore } from './state/store'
|
||||
import { unstable_batchedUpdates } from 'react-native'
|
||||
|
||||
const reset = () => {
|
||||
unstable_batchedUpdates(() => {
|
||||
useStore.getState().reset()
|
||||
})
|
||||
}
|
||||
|
||||
const setPlayerState = (state: State) => {
|
||||
unstable_batchedUpdates(() => {
|
||||
useStore.getState().setPlayerState(state)
|
||||
})
|
||||
}
|
||||
|
||||
const setCurrentTrackIdx = (idx?: number) => {
|
||||
unstable_batchedUpdates(() => {
|
||||
useStore.getState().setCurrentTrackIdx(idx)
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = async function () {
|
||||
const unsubCurrentTrack = useStore.subscribe(
|
||||
(currentTrack?: TrackExt) => {
|
||||
if (currentTrack) {
|
||||
useStore.getState().scrobbleTrack(currentTrack.id)
|
||||
}
|
||||
},
|
||||
state => state.currentTrack,
|
||||
)
|
||||
|
||||
TrackPlayer.addEventListener(Event.RemotePlay, () => trackPlayerCommands.enqueue(TrackPlayer.play))
|
||||
TrackPlayer.addEventListener(Event.RemotePause, () => trackPlayerCommands.enqueue(TrackPlayer.pause))
|
||||
|
||||
@ -27,30 +55,31 @@ module.exports = async function () {
|
||||
})
|
||||
|
||||
TrackPlayer.addEventListener(Event.RemoteStop, () => {
|
||||
useStore.getState().reset()
|
||||
unsubCurrentTrack()
|
||||
reset()
|
||||
trackPlayerCommands.enqueue(TrackPlayer.destroy)
|
||||
})
|
||||
|
||||
TrackPlayer.addEventListener(Event.PlaybackState, () => {
|
||||
trackPlayerCommands.enqueue(async () => {
|
||||
useStore.getState().setPlayerState(await getPlayerState())
|
||||
setPlayerState(await getPlayerState())
|
||||
})
|
||||
})
|
||||
|
||||
TrackPlayer.addEventListener(Event.PlaybackTrackChanged, () => {
|
||||
useStore.getState().setProgress({ position: 0, duration: 0, buffered: 0 })
|
||||
trackPlayerCommands.enqueue(async () => {
|
||||
useStore.getState().setCurrentTrackIdx(await getCurrentTrack())
|
||||
setCurrentTrackIdx(await getCurrentTrack())
|
||||
})
|
||||
})
|
||||
|
||||
TrackPlayer.addEventListener(Event.PlaybackQueueEnded, () => {
|
||||
trackPlayerCommands.enqueue(async () => {
|
||||
useStore.getState().setCurrentTrackIdx(await getCurrentTrack())
|
||||
setCurrentTrackIdx(await getCurrentTrack())
|
||||
})
|
||||
})
|
||||
|
||||
TrackPlayer.addEventListener(Event.PlaybackMetadataReceived, () => {
|
||||
useStore.getState().setCurrentTrackIdx(useStore.getState().currentTrackIdx)
|
||||
setCurrentTrackIdx(useStore.getState().currentTrackIdx)
|
||||
})
|
||||
}
|
||||
|
||||
@ -32,6 +32,7 @@ const ServerView: React.FC<{
|
||||
const [address, setAddress] = useState(server?.address || '')
|
||||
const [username, setUsername] = useState(server?.username || '')
|
||||
const [password, setPassword] = useState(server?.token ? 'password' : '')
|
||||
const [scrobble, setScrobble] = useState(server?.scrobble || false)
|
||||
|
||||
const validate = useCallback(() => {
|
||||
return !!address && !!username && !!password
|
||||
@ -49,7 +50,7 @@ const ServerView: React.FC<{
|
||||
}
|
||||
}, [navigation])
|
||||
|
||||
const save = () => {
|
||||
const save = useCallback(() => {
|
||||
if (!validate()) {
|
||||
return
|
||||
}
|
||||
@ -68,6 +69,7 @@ const ServerView: React.FC<{
|
||||
username,
|
||||
salt,
|
||||
token,
|
||||
scrobble,
|
||||
}
|
||||
|
||||
if (server) {
|
||||
@ -87,7 +89,20 @@ const ServerView: React.FC<{
|
||||
}
|
||||
|
||||
exit()
|
||||
}
|
||||
}, [
|
||||
activeServer,
|
||||
address,
|
||||
exit,
|
||||
id,
|
||||
password,
|
||||
scrobble,
|
||||
server,
|
||||
servers,
|
||||
setActiveServer,
|
||||
setServers,
|
||||
username,
|
||||
validate,
|
||||
])
|
||||
|
||||
const remove = useCallback(() => {
|
||||
if (!canRemove()) {
|
||||
@ -140,14 +155,17 @@ const ServerView: React.FC<{
|
||||
value={password}
|
||||
onChangeText={setPassword}
|
||||
/>
|
||||
<SettingsItem title="Scrobble" subtitle="Don't scrobble play history">
|
||||
<SettingsItem
|
||||
title="Scrobble plays"
|
||||
subtitle={scrobble ? 'Scrobble play history' : "Don't scrobble play history"}>
|
||||
<Switch
|
||||
trackColor={{
|
||||
false: colors.accentLow,
|
||||
true: colors.accent,
|
||||
}}
|
||||
thumbColor={colors.text.primary}
|
||||
value={false}
|
||||
value={scrobble}
|
||||
onValueChange={setScrobble}
|
||||
/>
|
||||
</SettingsItem>
|
||||
<Button
|
||||
|
||||
@ -9,6 +9,7 @@ export type SettingsSlice = {
|
||||
client?: SubsonicApiClient
|
||||
createClient: (id?: string) => void
|
||||
setActiveServer: (id?: string) => void
|
||||
getActiveServer: () => Server | undefined
|
||||
setServers: (servers: Server[]) => void
|
||||
}
|
||||
|
||||
@ -25,7 +26,7 @@ export const createSettingsSlice = (set: SetState<Store>, get: GetState<Store>):
|
||||
return
|
||||
}
|
||||
|
||||
const server = get().settings.servers.find(s => s.id === id)
|
||||
const server = get().getActiveServer()
|
||||
if (!server) {
|
||||
set({ client: undefined })
|
||||
return
|
||||
@ -52,10 +53,15 @@ export const createSettingsSlice = (set: SetState<Store>, get: GetState<Store>):
|
||||
}),
|
||||
)
|
||||
},
|
||||
getActiveServer: () => get().settings.servers.find(s => s.id === get().settings.activeServer),
|
||||
setServers: servers =>
|
||||
set(
|
||||
produce<SettingsSlice>(state => {
|
||||
state.settings.servers = servers
|
||||
const activeServer = servers.find(s => s.id === state.settings.activeServer)
|
||||
if (activeServer) {
|
||||
state.client = new SubsonicApiClient(activeServer)
|
||||
}
|
||||
}),
|
||||
),
|
||||
})
|
||||
|
||||
@ -35,6 +35,8 @@ export type TrackPlayerSlice = {
|
||||
progress: Progress
|
||||
setProgress: (progress: Progress) => void
|
||||
|
||||
scrobbleTrack: (id: string) => Promise<void>
|
||||
|
||||
reset: () => void
|
||||
}
|
||||
|
||||
@ -59,12 +61,14 @@ export const selectTrackPlayer = {
|
||||
progress: (store: TrackPlayerSlice) => store.progress,
|
||||
setProgress: (store: TrackPlayerSlice) => store.setProgress,
|
||||
|
||||
scrobbleTrack: (store: TrackPlayerSlice) => store.scrobbleTrack,
|
||||
|
||||
reset: (store: TrackPlayerSlice) => store.reset,
|
||||
}
|
||||
|
||||
export const trackPlayerCommands = new PromiseQueue(1)
|
||||
|
||||
export const createTrackPlayerSlice = (set: SetState<Store>, _get: GetState<Store>): TrackPlayerSlice => ({
|
||||
export const createTrackPlayerSlice = (set: SetState<Store>, get: GetState<Store>): TrackPlayerSlice => ({
|
||||
name: undefined,
|
||||
setName: name => set({ name }),
|
||||
|
||||
@ -91,6 +95,21 @@ export const createTrackPlayerSlice = (set: SetState<Store>, _get: GetState<Stor
|
||||
progress: { position: 0, duration: 0, buffered: 0 },
|
||||
setProgress: progress => set({ progress }),
|
||||
|
||||
scrobbleTrack: async id => {
|
||||
const client = get().client
|
||||
if (!client) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!get().getActiveServer()?.scrobble) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await client.scrobble({ id })
|
||||
} catch {}
|
||||
},
|
||||
|
||||
reset: () => {
|
||||
set({
|
||||
name: undefined,
|
||||
|
||||
@ -13,6 +13,7 @@ import {
|
||||
GetPlaylistParams,
|
||||
GetPlaylistsParams,
|
||||
GetTopSongsParams,
|
||||
ScrobbleParams,
|
||||
Search3Params,
|
||||
StreamParams,
|
||||
} from '@app/subsonic/params'
|
||||
@ -223,6 +224,15 @@ export class SubsonicApiClient {
|
||||
return this.buildUrl('stream', params)
|
||||
}
|
||||
|
||||
//
|
||||
// Media annotation
|
||||
//
|
||||
|
||||
async scrobble(params: ScrobbleParams): Promise<SubsonicResponse<undefined>> {
|
||||
const xml = await this.apiGetXml('scrobble', params)
|
||||
return new SubsonicResponse<undefined>(xml, undefined)
|
||||
}
|
||||
|
||||
//
|
||||
// Searching
|
||||
//
|
||||
|
||||
@ -100,6 +100,16 @@ export type StreamParams = {
|
||||
estimateContentLength?: boolean
|
||||
}
|
||||
|
||||
//
|
||||
// Media annotation
|
||||
//
|
||||
|
||||
export type ScrobbleParams = {
|
||||
id: string
|
||||
time?: Date
|
||||
submission?: boolean
|
||||
}
|
||||
|
||||
//
|
||||
// Searching
|
||||
//
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user