This commit is contained in:
ikun 2024-08-06 08:56:40 +08:00
commit 4d200c01f8
24 changed files with 2399 additions and 6289 deletions

View File

@ -1,6 +1,7 @@
module.exports = {
upgrade: true,
reject: [
'react-native-navigation',
'@types/react-native',
'message2call',
'react',
@ -9,11 +10,6 @@ module.exports = {
'react-native-navigation',
],
// target: 'newest',
// filter: [
// 'react-native-navigation',
// ],
// target: 'patch',
// filter: [
// '@types/react-native',

View File

@ -10,5 +10,6 @@
"google-cn",
"google"
],
"i18n-ally.sortKeys": true
}
"i18n-ally.sortKeys": true,
"java.configuration.updateBuildConfiguration": "disabled"
}

View File

@ -21,7 +21,7 @@
- Android
注:由于没有相关开发环境及证书,所以不计划支持IOS
不计划支持IOS
软件变化请查看:[更新日志](https://github.com/lyswhut/lx-music-mobile/blob/master/CHANGELOG.md)<br>
软件下载请转到:[发布页面](https://github.com/lyswhut/lx-music-mobile/releases)<br>

View File

@ -127,21 +127,10 @@ android {
}
signingConfigs {
release {
if (project.hasProperty('MYAPP_UPLOAD_STORE_FILE')) {
storeFile file(MYAPP_UPLOAD_STORE_FILE)
storePassword MYAPP_UPLOAD_STORE_PASSWORD
keyAlias MYAPP_UPLOAD_KEY_ALIAS
keyPassword MYAPP_UPLOAD_KEY_PASSWORD
} else {
def keystorePropertiesFile = rootProject.file("keystore.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
}
storeFile file('debug.keystore')
storePassword 'android'
keyAlias 'androiddebugkey'
keyPassword 'android'
}
debug {
storeFile file('debug.keystore')

View File

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.3-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

8035
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
{
"name": "lx-music-mobile-mod",
"version": "3.3.5",
"versionCode": 67,
"name": "lx-music-mobile",
"version": "8.8.8",
"versionCode": 64,
"private": true,
"scripts": {
"dev": "react-native run-android --active-arch-only",
@ -13,7 +13,6 @@
"rd": "react-devtools",
"menu": "adb shell input keyevent 82",
"bundle-android": "react-native bundle --platform android --dev true --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res",
"build-test": "react-native bundle --platform android --dev true --entry-file index.js --bundle-output index.android.bundle --assets-dest res",
"pack:android:debug": "./gradlew assembleDebug",
"pack": "npm run pack:android",
"pack:android": "cd android && gradlew.bat assembleRelease",
@ -28,65 +27,66 @@
},
"repository": {
"type": "git",
"url": "git+https://github.com/ikunshare/lx-music-mobile-mod.git"
"url": "git+https://github.com/lyswhut/lx-music-mobile.git"
},
"keywords": [
"music-player",
"react-native-app"
],
"author": {
"name": "ikun0014",
"email": "ikun0014@qq.com"
"name": "lyswhut",
"email": "lyswhut@qq.com"
},
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/ikunshare/lx-music-mobile-mod/issues"
"url": "https://github.com/lyswhut/lx-music-mobile/issues"
},
"homepage": "https://github.com/ikunshare/lx-music-mobile-mod#readme",
"homepage": "https://github.com/lyswhut/lx-music-mobile#readme",
"dependencies": {
"@craftzdog/react-native-buffer": "^6.0.5",
"@react-native-async-storage/async-storage": "^1.24.0",
"@react-native-clipboard/clipboard": "^1.14.1",
"@react-native-community/slider": "^4.5.2",
"@react-native-async-storage/async-storage": "^1.22.3",
"@react-native-clipboard/clipboard": "^1.13.2",
"@react-native-community/slider": "^4.5.0",
"iconv-lite": "^0.6.3",
"lrc-file-parser": "^2.4.1",
"message2call": "^0.1.3",
"pako": "^2.1.0",
"react": "18.2.0",
"react-native": "0.73.9",
"react-native": "0.73.6",
"react-native-background-timer": "github:lyswhut/react-native-background-timer#55ecaa80880e9cec1fff81f3ce10e6250ab3c40c",
"react-native-exception-handler": "^2.10.10",
"react-native-fast-image": "^8.6.3",
"react-native-file-system": "github:lyswhut/react-native-file-system#cb3b807ac68c5cdb2c32ca10fdbb5e5209154ece",
"react-native-fs": "^2.20.0",
"react-native-local-media-metadata": "github:lyswhut/react-native-local-media-metadata#5e55e67ecd1d557f29453e1cf34ff087efabbc7b",
"react-native-navigation": "7.39.2",
"react-native-pager-view": "6.3.0",
"react-native-quick-base64": "^2.1.2",
"react-native-local-media-metadata": "github:lyswhut/react-native-local-media-metadata#c8377d82c04aecf5ee79d446276ab9d0c1f167be",
"react-native-navigation": "^7.38.1",
"react-native-pager-view": "^6.2.3",
"react-native-quick-base64": "^2.0.8",
"react-native-quick-md5": "^3.0.6",
"react-native-track-player": "github:lyswhut/react-native-track-player#a567c8b0a319e41430afdb782526723243ea3950",
"react-native-vector-icons": "^10.1.0"
"react-native-track-player": "github:lyswhut/react-native-track-player#ddfb984ec12efb0d6ae84c924e027f872bb1481f",
"react-native-vector-icons": "^10.0.3",
"rn-fetch-blob": "^0.12.0"
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@babel/eslint-parser": "^7.25.1",
"@babel/core": "^7.24.0",
"@babel/eslint-parser": "^7.23.10",
"@babel/plugin-proposal-export-namespace-from": "^7.18.9",
"@babel/preset-env": "^7.25.3",
"@babel/runtime": "^7.25.0",
"@react-native/babel-preset": "^0.74.86",
"@react-native/metro-config": "^0.74.86",
"@react-native/typescript-config": "^0.74.86",
"@tsconfig/react-native": "^3.0.5",
"@types/react": "^18.3.3",
"@babel/preset-env": "^7.24.0",
"@babel/runtime": "^7.24.0",
"@react-native/babel-preset": "^0.74.0",
"@react-native/metro-config": "^0.73.5",
"@react-native/typescript-config": "^0.74.0",
"@tsconfig/react-native": "^3.0.3",
"@types/react": "^18.2.66",
"@types/react-native": "^0.72.8",
"@types/react-native-background-timer": "^2.0.2",
"@types/react-native-vector-icons": "^6.4.18",
"babel-plugin-module-resolver": "^5.0.2",
"babel-plugin-module-resolver": "^5.0.0",
"changelog-parser": "^3.0.1",
"eslint-config-standard": "^17.1.0",
"eslint-config-standard-with-typescript": "^43.0.1",
"eslint-plugin-react": "^7.35.0",
"eslint-plugin-react-hooks": "^4.6.2",
"typescript": "^5.5.4"
"eslint-plugin-react": "^7.34.1",
"eslint-plugin-react-hooks": "^4.6.0",
"typescript": "^5.4.2"
}
}

View File

@ -18,6 +18,7 @@ export interface ListMenuProps {
onCopyName: (selectInfo: SelectInfo) => void
onMusicSourceDetail: (selectInfo: SelectInfo) => void
onDislikeMusic: (selectInfo: SelectInfo) => void
onDownload: (selectInfo: SelectInfo) => void
}
export interface ListMenuType {
show: (selectInfo: SelectInfo, position: Position) => void
@ -52,7 +53,7 @@ export default forwardRef<ListMenuType, ListMenuProps>((props: ListMenuProps, re
return [
{ action: 'play', label: t('play') },
{ action: 'playLater', label: t('play_later') },
// { action: 'download', label: '下载' },
{ action: 'download', label: '下载' },
{ action: 'add', label: t('add_to') },
{ action: 'copyName', label: t('copy_name') },
{ action: 'musicSourceDetail', label: t('music_source_detail') },
@ -82,6 +83,8 @@ export default forwardRef<ListMenuType, ListMenuProps>((props: ListMenuProps, re
case 'dislike':
props.onDislikeMusic(selectInfo)
break
case 'download':
props.onDownload(selectInfo)
default:
break
}

View File

@ -5,9 +5,11 @@ import List, { type ListProps, type ListType, type Status, type RowInfoType } fr
import ListMenu, { type ListMenuType, type Position, type SelectInfo } from './ListMenu'
import ListMusicMultiAdd, { type MusicMultiAddModalType as ListAddMultiType } from '@/components/MusicMultiAddModal'
import ListMusicAdd, { type MusicAddModalType as ListMusicAddType } from '@/components/MusicAddModal'
import MusicDownloadModal, { type MusicDownloadModalType } from '@/screens/Home/Views/Mylist/MusicList/MusicDownloadModal'
import MultipleModeBar, { type MultipleModeBarType, type SelectMode } from './MultipleModeBar'
import { handleDislikeMusic, handlePlay, handlePlayLater, handleShare, handleShowMusicSourceDetail } from './listAction'
import { handleDislikeMusic, handlePlay, handlePlayLater, handleShare } from './listAction'
import { createStyle } from '@/utils/tools'
import { handelDownload } from '@/screens/Home/Views/Mylist/MusicList/listAction'
export interface OnlineListProps {
onRefresh: ListProps['onRefresh']
@ -37,6 +39,7 @@ export default forwardRef<OnlineListType, OnlineListProps>(({
const listMusicAddRef = useRef<ListMusicAddType>(null)
const listMusicMultiAddRef = useRef<ListAddMultiType>(null)
const listMenuRef = useRef<ListMenuType>(null)
const musicDownloadModalRef = useRef<MusicDownloadModalType>(null)
// const loadingMaskRef = useRef<LoadingMaskType>(null)
useImperativeHandle(ref, () => ({
@ -103,13 +106,15 @@ export default forwardRef<OnlineListType, OnlineListProps>(({
</View>
<ListMusicAdd ref={listMusicAddRef} onAdded={() => { hancelExitSelect() }} />
<ListMusicMultiAdd ref={listMusicMultiAddRef} onAdded={() => { hancelExitSelect() }} />
<MusicDownloadModal ref={musicDownloadModalRef}
onDownloadInfo={(info) => { }} />
<ListMenu
ref={listMenuRef}
onPlay={info => { handlePlay(info.musicInfo) }}
onPlayLater={info => { hancelExitSelect(); handlePlayLater(info.musicInfo, info.selectedList, hancelExitSelect) }}
onCopyName={info => { handleShare(info.musicInfo) }}
onAdd={handleAddMusic}
onMusicSourceDetail={info => { void handleShowMusicSourceDetail(info.musicInfo) }}
onDownload={info => musicDownloadModalRef.current?.show(info.musicInfo)}
onDislikeMusic={info => { void handleDislikeMusic(info.musicInfo) }}
/>
{/* <LoadingMask ref={loadingMaskRef} /> */}

View File

@ -19,7 +19,7 @@ import {
} from './local'
export const getMusicUrl = async({
export const getMusicUrl = async ({
musicInfo,
quality,
isRefresh = false,
@ -33,13 +33,14 @@ export const getMusicUrl = async({
if ('progress' in musicInfo) {
return getDownloadMusicUrl({ musicInfo, isRefresh, onToggleSource })
} else if (musicInfo.source == 'local') {
return getLocalMusicUrl({ musicInfo, isRefresh, onToggleSource })
} else {
return getOnlineMusicUrl({ musicInfo, isRefresh, quality, onToggleSource })
}
}
export const getPicPath = async({
export const getPicPath = async ({
musicInfo,
isRefresh = false,
listId,
@ -59,7 +60,7 @@ export const getPicPath = async({
}
}
export const getLyricInfo = async({
export const getLyricInfo = async ({
musicInfo,
isRefresh = false,
onToggleSource,

View File

@ -39,7 +39,7 @@ export const setPic = (datas: {
*/
export const getMusicUrl = async({ musicInfo, quality, isRefresh, allowToggleSource = true, onToggleSource = () => {} }: {
export const getMusicUrl = async ({ musicInfo, quality, isRefresh, allowToggleSource = true, onToggleSource = () => { } }: {
musicInfo: LX.Music.MusicInfoOnline
quality?: LX.Quality
isRefresh: boolean
@ -63,7 +63,7 @@ export const getMusicUrl = async({ musicInfo, quality, isRefresh, allowToggleSou
})
}
export const getPicUrl = async({ musicInfo, listId, isRefresh, allowToggleSource = true, onToggleSource = () => {} }: {
export const getPicUrl = async ({ musicInfo, listId, isRefresh, allowToggleSource = true, onToggleSource = () => { } }: {
musicInfo: LX.Music.MusicInfoOnline
listId?: string | null
isRefresh: boolean
@ -81,7 +81,7 @@ export const getPicUrl = async({ musicInfo, listId, isRefresh, allowToggleSource
return url
})
}
export const getLyricInfo = async({ musicInfo, isRefresh, allowToggleSource = true, onToggleSource = () => {} }: {
export const getLyricInfo = async ({ musicInfo, isRefresh, allowToggleSource = true, onToggleSource = () => { } }: {
musicInfo: LX.Music.MusicInfoOnline
isRefresh: boolean
allowToggleSource?: boolean
@ -93,7 +93,7 @@ export const getLyricInfo = async({ musicInfo, isRefresh, allowToggleSource = tr
}
// lrcRequest = music[musicInfo.source].getLyric(musicInfo)
return handleGetOnlineLyricInfo({ musicInfo, onToggleSource, isRefresh, allowToggleSource }).then(async({ lyricInfo, musicInfo: targetMusicInfo, isFromCache }) => {
return handleGetOnlineLyricInfo({ musicInfo, onToggleSource, isRefresh, allowToggleSource }).then(async ({ lyricInfo, musicInfo: targetMusicInfo, isFromCache }) => {
// lrcRequest = null
if (isFromCache) return buildLyricInfo(lyricInfo)
if (targetMusicInfo.id == musicInfo.id) void saveLyric(musicInfo, lyricInfo)

View File

@ -16,7 +16,7 @@ import { apis } from '@/utils/musicSdk/api-source'
const getOtherSourcePromises = new Map()
export const existTimeExp = /\[\d{1,2}:.*\d{1,4}\]/
export const getOtherSource = async(musicInfo: LX.Music.MusicInfo | LX.Download.ListItem, isRefresh = false): Promise<LX.Music.MusicInfoOnline[]> => {
export const getOtherSource = async (musicInfo: LX.Music.MusicInfo | LX.Download.ListItem, isRefresh = false): Promise<LX.Music.MusicInfoOnline[]> => {
if (!isRefresh) {
const cachedInfo = await getOtherSourceFromStore(musicInfo.id)
if (cachedInfo.length) return cachedInfo
@ -71,7 +71,7 @@ export const getOtherSource = async(musicInfo: LX.Music.MusicInfo | LX.Download.
}
export const buildLyricInfo = async(lyricInfo: MakeOptional<LX.Player.LyricInfo, 'rawlrcInfo'>): Promise<LX.Player.LyricInfo> => {
export const buildLyricInfo = async (lyricInfo: MakeOptional<LX.Player.LyricInfo, 'rawlrcInfo'>): Promise<LX.Player.LyricInfo> => {
if (!settingState.setting['player.isS2t']) {
// @ts-expect-error
if (lyricInfo.rawlrcInfo) return lyricInfo
@ -117,7 +117,7 @@ export const buildLyricInfo = async(lyricInfo: MakeOptional<LX.Player.LyricInfo,
return lyricInfo.rawlrcInfo ? lyricInfo : { ...lyricInfo, rawlrcInfo: { ...lyricInfo } }
}
export const getCachedLyricInfo = async(musicInfo: LX.Music.MusicInfo): Promise<LX.Player.LyricInfo | null> => {
export const getCachedLyricInfo = async (musicInfo: LX.Music.MusicInfo): Promise<LX.Player.LyricInfo | null> => {
let lrcInfo = await getStoreLyric(musicInfo)
// lrcInfo = {}
if (existTimeExp.test(lrcInfo.lyric) && lrcInfo.tlyric != null) {
@ -146,7 +146,7 @@ export const getCachedLyricInfo = async(musicInfo: LX.Music.MusicInfo): Promise<
return null
}
export const getOnlineOtherSourceMusicUrlByLocal = async(musicInfo: LX.Music.MusicInfoLocal, isRefresh: boolean): Promise<{
export const getOnlineOtherSourceMusicUrlByLocal = async (musicInfo: LX.Music.MusicInfoLocal, isRefresh: boolean): Promise<{
url: string
quality: LX.Quality
isFromCache: boolean
@ -170,7 +170,7 @@ export const getOnlineOtherSourceMusicUrlByLocal = async(musicInfo: LX.Music.Mus
})
}
export const getOnlineOtherSourceLyricByLocal = async(musicInfo: LX.Music.MusicInfoLocal, isRefresh: boolean): Promise<{
export const getOnlineOtherSourceLyricByLocal = async (musicInfo: LX.Music.MusicInfoLocal, isRefresh: boolean): Promise<{
lyricInfo: LX.Music.LyricInfo
isFromCache: boolean
}> => {
@ -191,7 +191,7 @@ export const getOnlineOtherSourceLyricByLocal = async(musicInfo: LX.Music.MusicI
})
}
export const getOnlineOtherSourcePicByLocal = async(musicInfo: LX.Music.MusicInfoLocal): Promise<{
export const getOnlineOtherSourcePicByLocal = async (musicInfo: LX.Music.MusicInfoLocal): Promise<{
url: string
}> => {
if (!await global.lx.apiInitPromise[0]) throw new Error('source init failed')
@ -208,23 +208,21 @@ export const getOnlineOtherSourcePicByLocal = async(musicInfo: LX.Music.MusicInf
})
}
export const TRY_QUALITYS_LIST = ['flac24bit', 'flac', '320k'] as const
type TryQualityType = typeof TRY_QUALITYS_LIST[number]
export const getPlayQuality = (highQuality: LX.Quality, musicInfo: LX.Music.MusicInfoOnline): LX.Quality => {
// export const getPlayQuality = (highQuality: boolean, musicInfo: LX.Music.MusicInfoOnline): LX.Quality => {
// let type: LX.Quality = '128k'
// let list = global.lx.qualityList[musicInfo.source]
// if (highQuality && musicInfo.meta._qualitys['flac'] && list && list.includes('flac')) type = 'flac'
// return type
// }
export const getPlayQuality = (playQuality: LX.Quality, musicInfo: LX.Music.MusicInfoOnline): LX.Quality => {
let type: LX.Quality = '128k'
if (TRY_QUALITYS_LIST.includes(highQuality as TryQualityType)) {
let list = global.lx.qualityList[musicInfo.source]
let t = TRY_QUALITYS_LIST
.slice(TRY_QUALITYS_LIST.indexOf(highQuality as TryQualityType))
.find(q => musicInfo.meta._qualitys[q] && list?.includes(q))
if (t) type = t
}
let list = global.lx.qualityList[musicInfo.source]
if (musicInfo.meta._qualitys[playQuality] && list && list.includes(playQuality)) type = playQuality
return type
}
export const getOnlineOtherSourceMusicUrl = async({ musicInfos, quality, onToggleSource, isRefresh, retryedSource = [] }: {
export const getOnlineOtherSourceMusicUrl = async ({ musicInfos, quality, onToggleSource, isRefresh, retryedSource = [] }: {
musicInfos: LX.Music.MusicInfoOnline[]
quality?: LX.Quality
onToggleSource: (musicInfo?: LX.Music.MusicInfoOnline) => void
@ -245,6 +243,7 @@ export const getOnlineOtherSourceMusicUrl = async({ musicInfos, quality, onToggl
if (retryedSource.includes(musicInfo.source)) continue
retryedSource.push(musicInfo.source)
if (!assertApiSupport(musicInfo.source)) continue
// itemQuality = quality ?? getPlayQuality(settingState.setting['player.isPlayHighQuality'], musicInfo)
itemQuality = quality ?? getPlayQuality(settingState.setting['player.playQuality'], musicInfo)
if (!musicInfo.meta._qualitys[itemQuality]) continue
@ -278,7 +277,7 @@ export const getOnlineOtherSourceMusicUrl = async({ musicInfos, quality, onToggl
/**
* 线URL
*/
export const handleGetOnlineMusicUrl = async({ musicInfo, quality, onToggleSource, isRefresh, allowToggleSource }: {
export const handleGetOnlineMusicUrl = async ({ musicInfo, quality, onToggleSource, isRefresh, allowToggleSource }: {
musicInfo: LX.Music.MusicInfoOnline
quality?: LX.Quality
isRefresh: boolean
@ -302,7 +301,7 @@ export const handleGetOnlineMusicUrl = async({ musicInfo, quality, onToggleSourc
}
return reqPromise.then(({ url, type }: { url: string, type: LX.Quality }) => {
return { musicInfo, url, quality: type, isFromCache: false }
}).catch(async(err: any) => {
}).catch(async (err: any) => {
console.log(err)
if (!allowToggleSource || err.message == requestMsg.tooManyRequests) throw err
onToggleSource()
@ -324,7 +323,7 @@ export const handleGetOnlineMusicUrl = async({ musicInfo, quality, onToggleSourc
}
export const getOnlineOtherSourcePicUrl = async({ musicInfos, onToggleSource, isRefresh, retryedSource = [] }: {
export const getOnlineOtherSourcePicUrl = async ({ musicInfos, onToggleSource, isRefresh, retryedSource = [] }: {
musicInfos: LX.Music.MusicInfoOnline[]
onToggleSource: (musicInfo?: LX.Music.MusicInfoOnline) => void
isRefresh: boolean
@ -367,7 +366,7 @@ export const getOnlineOtherSourcePicUrl = async({ musicInfos, onToggleSource, is
/**
* 线
*/
export const handleGetOnlinePicUrl = async({ musicInfo, isRefresh, onToggleSource, allowToggleSource }: {
export const handleGetOnlinePicUrl = async ({ musicInfo, isRefresh, onToggleSource, allowToggleSource }: {
musicInfo: LX.Music.MusicInfoOnline
onToggleSource: (musicInfo?: LX.Music.MusicInfoOnline) => void
isRefresh: boolean
@ -386,7 +385,7 @@ export const handleGetOnlinePicUrl = async({ musicInfo, isRefresh, onToggleSourc
}
return reqPromise.then((url: string) => {
return { musicInfo, url, isFromCache: false }
}).catch(async(err: any) => {
}).catch(async (err: any) => {
console.log(err)
if (!allowToggleSource) throw err
onToggleSource()
@ -407,7 +406,7 @@ export const handleGetOnlinePicUrl = async({ musicInfo, isRefresh, onToggleSourc
}
export const getOnlineOtherSourceLyricInfo = async({ musicInfos, onToggleSource, isRefresh, retryedSource = [] }: {
export const getOnlineOtherSourceLyricInfo = async ({ musicInfos, onToggleSource, isRefresh, retryedSource = [] }: {
musicInfos: LX.Music.MusicInfoOnline[]
onToggleSource: (musicInfo?: LX.Music.MusicInfoOnline) => void
isRefresh: boolean
@ -442,7 +441,7 @@ export const getOnlineOtherSourceLyricInfo = async({ musicInfos, onToggleSource,
reqPromise = Promise.reject(err)
}
// retryedSource.includes(musicInfo.source)
return reqPromise.then(async(lyricInfo: LX.Music.LyricInfo) => {
return reqPromise.then(async (lyricInfo: LX.Music.LyricInfo) => {
return existTimeExp.test(lyricInfo.lyric) ? {
lyricInfo,
musicInfo,
@ -458,7 +457,7 @@ export const getOnlineOtherSourceLyricInfo = async({ musicInfos, onToggleSource,
/**
* 线
*/
export const handleGetOnlineLyricInfo = async({ musicInfo, onToggleSource, isRefresh, allowToggleSource }: {
export const handleGetOnlineLyricInfo = async ({ musicInfo, onToggleSource, isRefresh, allowToggleSource }: {
musicInfo: LX.Music.MusicInfoOnline
onToggleSource: (musicInfo?: LX.Music.MusicInfoOnline) => void
isRefresh: boolean
@ -476,13 +475,13 @@ export const handleGetOnlineLyricInfo = async({ musicInfo, onToggleSource, isRef
} catch (err) {
reqPromise = Promise.reject(err)
}
return reqPromise.then(async(lyricInfo: LX.Music.LyricInfo) => {
return reqPromise.then(async (lyricInfo: LX.Music.LyricInfo) => {
return existTimeExp.test(lyricInfo.lyric) ? {
musicInfo,
lyricInfo,
isFromCache: false,
} : Promise.reject(new Error('failed'))
}).catch(async(err: any) => {
}).catch(async (err: any) => {
console.log(err)
if (!allowToggleSource) throw err

View File

@ -11,6 +11,7 @@
"change_position_list_title": "Change the position of the list",
"change_position_music_multi_title": "Adjust the position of the selected {num} song to",
"change_position_music_title": "Adjust the position of {name} to",
"download_music_title": "Adjust the download of {name}",
"change_position_tip": "Please enter a new position",
"close": "Close",
"collect": "Collect",
@ -337,6 +338,7 @@
"setting_play_auto_clean_played_list": "Automatically clear the played list",
"setting_play_auto_clean_played_list_tip": "In random play mode, when switching songs by clicking the same list as the playlist, if automatic clearing of the already played list is enabled, the played songs will re-participate in random play.",
"setting_play_cache_size": "Maximum cache size (MB)",
"setting_play_select": "Select sound quality listening",
"setting_play_cache_size_no_cache": "Disabled cache",
"setting_play_cache_size_save_tip": "The cache setting is completed, it will take effect after restarting the application",
"setting_play_handle_audio_focus": "When other apps play sound, automatically pause the playback",
@ -503,4 +505,4 @@
"version_title_new": "🌟 New version found 🌟",
"version_title_unknown": "❓ Failed to get the latest version information ❓",
"version_title_update": "🚀 Program update 🚀"
}
}

View File

@ -11,6 +11,7 @@
"change_position_list_title": "调整列表位置",
"change_position_music_multi_title": "将已选的 {num} 首歌曲的位置调整到",
"change_position_music_title": "将 {name} 的位置调整到",
"download_music_title": "下载 {name}",
"change_position_tip": "请输入新的位置",
"close": "关闭",
"collect": "收藏",
@ -336,6 +337,7 @@
"setting_play_auto_clean_played_list": "自动清空已播放列表",
"setting_play_auto_clean_played_list_tip": "随机播放模式下,通过 「点击」 与 「播放列表相同的列表内的歌曲」 切歌时,若启用 「自动清空已播放列表」,则已播放的歌曲将重新参与随机播放。",
"setting_play_cache_size": "最大缓存大小MB",
"setting_play_select": "播放音质选择",
"setting_play_cache_size_no_cache": "禁用缓存",
"setting_play_cache_size_save_tip": "缓存设置完毕,重启应用后生效",
"setting_play_handle_audio_focus": "其他应用播放声音时,自动暂停播放",
@ -502,4 +504,4 @@
"version_title_new": "🌟 发现新版本 🌟",
"version_title_unknown": "❓ 获取最新版本信息失败 ❓",
"version_title_update": "🚀 程序更新 🚀"
}
}

View File

@ -68,7 +68,7 @@ const Footer = ({ componentId }: { componentId: string }) => {
const theme = useTheme()
const isAgreePact = useSettingValue('common.isAgreePact')
// const checkUpdate = useDispatch('common', 'checkUpdate')
const [time, setTime] = useState(20)
const [time, setTime] = useState(1)
const handleRejct = () => {
exitApp()
@ -123,7 +123,7 @@ const Footer = ({ componentId }: { componentId: string }) => {
return () => {
timeoutTools.clear()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
return (
@ -132,18 +132,18 @@ const Footer = ({ componentId }: { componentId: string }) => {
isAgreePact
? null
: (
<Text selectable style={styles.tip} size={13}>使退</Text>
)
<Text selectable style={styles.tip} size={13}>使退</Text>
)
}
<View style={styles.btns}>
{
isAgreePact
? null
: (
<Button style={{ ...styles.btn, backgroundColor: theme['c-button-background'] }} onPress={handleRejct}>
<Text color={theme['c-button-font']}></Text>
</Button>
)
<Button style={{ ...styles.btn, backgroundColor: theme['c-button-background'] }} onPress={handleRejct}>
<Text color={theme['c-button-font']}></Text>
</Button>
)
}
<Button disabled={confirmBtn.disabled} style={{ ...styles.btn, backgroundColor: theme['c-button-background'] }} onPress={handleConfirm}>
<Text color={theme['c-button-font']}>{confirmBtn.text}</Text>

View File

@ -21,9 +21,9 @@ export interface ListMenuProps {
onEditMetadata: (selectInfo: SelectInfo) => void
onCopyName: (selectInfo: SelectInfo) => void
onChangePosition: (selectInfo: SelectInfo) => void
onMusicSourceDetail: (selectInfo: SelectInfo) => void
onDislikeMusic: (selectInfo: SelectInfo) => void
onRemove: (selectInfo: SelectInfo) => void
onDownload: (selectInfo: SelectInfo) => void
}
export interface ListMenuType {
show: (selectInfo: SelectInfo, position: Position) => void
@ -33,7 +33,7 @@ export type {
Position,
}
const hasEditMetadata = async(musicInfo: LX.Music.MusicInfo) => {
const hasEditMetadata = async (musicInfo: LX.Music.MusicInfo) => {
if (musicInfo.source != 'local') return false
return existsFile(musicInfo.meta.filePath)
}
@ -63,29 +63,38 @@ export default forwardRef<ListMenuType, ListMenuProps>((props, ref) => {
const menu = [
{ action: 'play', label: t('play') },
{ action: 'playLater', label: t('play_later') },
// { action: 'download', label: '下载' },
{ action: 'download', label: '下载' },
{ action: 'add', label: t('add_to') },
{ action: 'move', label: t('move_to') },
{ action: 'changePosition', label: t('change_position') },
{ action: 'copyName', label: t('copy_name') },
{ action: 'musicSourceDetail', disabled: musicInfo.source == 'local', label: t('music_source_detail') },
// { action: 'musicSearch', label: t('music_search') },
{ action: 'changePosition', label: t('change_position') },
{ action: 'dislike', disabled: hasDislike(musicInfo), label: t('dislike') },
{ action: 'remove', label: t('delete') },
]
if (musicInfo.source == 'local') menu.splice(5, 0, { action: 'editMetadata', disabled: !edit_metadata, label: t('edit_metadata') })
if (musicInfo.source == 'local') menu.splice(4, 0, { action: 'editMetadata', disabled: !edit_metadata, label: t('edit_metadata') })
setMenus(menu)
void Promise.all([hasEditMetadata(musicInfo)]).then(([_edit_metadata]) => {
// console.log(_edit_metadata)
let isUpdated = false
let isUpdated = true
if (edit_metadata != _edit_metadata) {
edit_metadata = _edit_metadata
isUpdated ||= true
}
if (isUpdated) {
menu[menu.findIndex(m => m.action == 'editMetadata')].disabled = !edit_metadata
setMenus([...menu])
const menu = [
{ action: 'play', label: t('play') },
{ action: 'playLater', label: t('play_later') },
{ action: 'download', label: '下载' },
{ action: 'add', label: t('add_to') },
{ action: 'move', label: t('move_to') },
{ action: 'copyName', label: t('copy_name') },
{ action: 'changePosition', label: t('change_position') },
{ action: 'dislike', disabled: hasDislike(musicInfo), label: t('dislike') },
{ action: 'remove', label: t('delete') },
]
if (musicInfo.source == 'local') menu.splice(4, 0, { action: 'editMetadata', disabled: !edit_metadata, label: t('edit_metadata') })
setMenus(menu)
}
})
}
@ -124,16 +133,15 @@ export default forwardRef<ListMenuType, ListMenuProps>((props, ref) => {
props.onChangePosition(selectInfo)
// setVIsibleMusicPosition(true)
break
case 'musicSourceDetail':
props.onMusicSourceDetail(selectInfo)
// setVIsibleMusicPosition(true)
break
case 'dislike':
props.onDislikeMusic(selectInfo)
break
case 'remove':
props.onRemove(selectInfo)
break
case 'download':
props.onDownload(selectInfo)
break
default:
break
}

View File

@ -0,0 +1,210 @@
import { useState, useRef, useImperativeHandle, forwardRef, useMemo, useEffect } from 'react'
import { View } from 'react-native'
import ConfirmAlert, { type ConfirmAlertType } from '@/components/common/ConfirmAlert'
import Text from '@/components/common/Text'
import { createStyle } from '@/utils/tools'
import CheckBox from '@/components/common/CheckBox'
import { handelDownload } from './listAction'
import { getOtherSource } from '@/core/music/utils'
interface TitleType {
updateTitle: (musicInfo: LX.Music.MusicInfo) => void
}
const Title = forwardRef<TitleType, {}>((props, ref) => {
const [title, setTitle] = useState('')
useImperativeHandle(ref, () => ({
updateTitle(musicInfo) {
setTitle(global.i18n.t('download_music_title', { name: musicInfo.name }))
},
}))
return (
<Text style={{ marginBottom: 5 }}>{title}</Text>
)
})
interface PositionInputType {
getText: () => string
setText: (text: string) => void
focus: () => void
}
export interface SelectInfo {
musicInfo: LX.Music.MusicInfo
selectedList: LX.Music.MusicInfo[]
index: number
listId: string
single: boolean
}
const initSelectInfo = {}
interface MusicDownloadModalProps {
onDownloadInfo: (info: LX.Music.MusicInfo) => void
}
export interface MusicDownloadModalType {
show: (info: LX.Music.MusicInfo) => void
}
export default forwardRef<MusicDownloadModalType, MusicDownloadModalProps>(({ onDownloadInfo }, ref) => {
const alertRef = useRef<ConfirmAlertType>(null)
const titleRef = useRef<TitleType>(null)
const inputRef = useRef<PositionInputType>(null)
const selectedInfo = useRef<LX.Music.MusicInfo>(initSelectInfo as LX.Music.MusicInfo)
const [selectedQuality, setSelectedQuality] = useState<LX.Quality>("128k");
const [playQualityList, setPlayQualityList] = useState<MusicOption[]>([]);
const [visible, setVisible] = useState(false)
interface QualityMap {
[key: string]: MusicOption;
}
const calcQualitys = () => {
console.log("calcQualitys");
console.log("calcQualitys" + selectedInfo.current.name);
setPlayQualityList([])
const map = new Map();
map.set("128k", "标准音质");
map.set("320k", "高品音质");
map.set("flac", "无损音质");
map.set("flac24bit", "Hi-Res音质");
const qualitys = selectedInfo.current.meta.qualitys;
let qualityMap: QualityMap = {};
for (let index = 0; index < qualitys.length; index++) {
const element = qualitys[index];
const temp: MusicOption = {
id: element.type,
name: map.has(element.type) ? map.get(element.type) : "未知",
size: element.size,
key: element.type,
}
qualityMap[element.type] = temp;
}
setPlayQualityList(Object.values(qualityMap));
// if (Object.values(qualityMap).length == map.size) {
// return;
// }
// getOtherSource(selectedInfo.current, true).then(res => {
// if (res.length == 0) {
// setPlayQualityList(Object.values(qualityMap));
// return;
// }
// for (let index = 0; index < res.length; index++) {
// const element = res[index];
// let qualitys = element.meta.qualitys
// for (let index = 0; index < qualitys.length; index++) {
// const element = qualitys[index];
// if (element.type in qualityMap) {
// continue;
// }
// const tem: MusicOption = {
// id: element.type,
// name: map.has(element.type) ? map.get(element.type) : "未知",
// size: element.size,
// key: element.type,
// }
// qualityMap[element.type] = tem;
// if (Object.values(qualityMap).length == map.size) {
// setPlayQualityList(Object.values(qualityMap));
// return;
// }
// }
// }
// }).catch(err => {
// })
}
const handleShow = () => {
console.log("handleShow");
alertRef.current?.setVisible(true)
requestAnimationFrame(() => {
titleRef.current?.updateTitle(selectedInfo.current)
setTimeout(() => {
inputRef.current?.focus()
}, 300)
})
}
useImperativeHandle(ref, () => ({
show(info) {
selectedInfo.current = info
calcQualitys();
if (visible) {
handleShow()
} else {
setVisible(true)
requestAnimationFrame(() => {
handleShow()
})
}
},
}))
const handleDownloadMusic = () => {
setSelectedQuality("128k");
alertRef.current?.setVisible(false)
handelDownload(selectedInfo.current, selectedQuality);
}
interface MusicOption {
id: LX.Quality;
name: string;
size?: string | null;
key?: string
}
const useActive = (id: LX.Quality) => {
const isActive = useMemo(() => selectedQuality == id, [selectedQuality, id])
return isActive
}
const Item = ({ id, name }: {
id: LX.Quality
name: string
}) => {
const isActive = useActive(id)
return <CheckBox marginRight={8} check={isActive} label={name} onChange={() => { setSelectedQuality(id) }} need />
}
return (
visible
? <ConfirmAlert
ref={alertRef}
onConfirm={handleDownloadMusic}
onHide={() => inputRef.current?.setText('')}
>
<View style={styles.content}>
<Title ref={titleRef} />
<View style={styles.list}>
{
playQualityList.map((item) => <Item name={item.name + "" + "(" + item.size + ")"} id={item.id} key={item.key} />)
}
</View>
</View>
</ConfirmAlert>
: null
)
})
const styles = createStyle({
content: {
flexGrow: 1,
flexShrink: 1,
flexDirection: 'column',
},
input: {
flexGrow: 1,
flexShrink: 1,
minWidth: 260,
borderRadius: 4,
},
list: {
flexDirection: 'column',
flexWrap: 'nowrap',
},
})

View File

@ -2,7 +2,7 @@ import { useCallback, useRef } from 'react'
import listState from '@/store/list/state'
import ListMenu, { type ListMenuType, type Position, type SelectInfo } from './ListMenu'
import { handleDislikeMusic, handlePlay, handlePlayLater, handleRemove, handleShare, handleShowMusicSourceDetail, handleUpdateMusicInfo, handleUpdateMusicPosition } from './listAction'
import { handelDownload, handleDislikeMusic, handlePlay, handlePlayLater, handleRemove, handleShare, handleUpdateMusicInfo, handleUpdateMusicPosition } from './listAction'
import List, { type ListType } from './List'
import ListMusicAdd, { type MusicAddModalType as ListMusicAddType } from '@/components/MusicAddModal'
import ListMusicMultiAdd, { type MusicMultiAddModalType as ListAddMultiType } from '@/components/MusicMultiAddModal'
@ -13,6 +13,7 @@ import MultipleModeBar, { type SelectMode, type MultipleModeBarType } from './Mu
import ListSearchBar, { type ListSearchBarType } from './ListSearchBar'
import ListMusicSearch, { type ListMusicSearchType } from './ListMusicSearch'
import MusicPositionModal, { type MusicPositionModalType } from './MusicPositionModal'
import MusicDownloadModal, { type MusicDownloadModalType } from './MusicDownloadModal'
import MetadataEditModal, { type MetadataEditType, type MetadataEditProps } from '@/components/MetadataEditModal'
@ -26,6 +27,7 @@ export default () => {
const listMusicAddRef = useRef<ListMusicAddType>(null)
const listMusicMultiAddRef = useRef<ListAddMultiType>(null)
const musicPositionModalRef = useRef<MusicPositionModalType>(null)
const musicDownloadModalRef = useRef<MusicDownloadModalType>(null)
const metadataEditTypeRef = useRef<MetadataEditType>(null)
const listMenuRef = useRef<ListMenuType>(null)
const layoutHeightRef = useRef<number>(0)
@ -87,7 +89,7 @@ export default () => {
const handleScrollToInfo = useCallback((info: LX.Music.MusicInfo) => {
listRef.current?.scrollToInfo(info)
handleExitSearch()
}, [handleExitSearch])
}, [])
const onLayout = useCallback((e: LayoutChangeEvent) => {
layoutHeightRef.current = e.nativeEvent.layout.height
}, [])
@ -145,10 +147,12 @@ export default () => {
onScrollToInfo={handleScrollToInfo}
/>
</View>
<ListMusicAdd ref={listMusicAddRef} onAdded={hancelExitSelect} />
<ListMusicMultiAdd ref={listMusicMultiAddRef} onAdded={hancelExitSelect} />
<ListMusicAdd ref={listMusicAddRef} onAdded={() => { hancelExitSelect() }} />
<ListMusicMultiAdd ref={listMusicMultiAddRef} onAdded={() => { hancelExitSelect() }} />
<MusicPositionModal ref={musicPositionModalRef}
onUpdatePosition={(info, postion) => { handleUpdateMusicPosition(postion, info.listId, info.musicInfo, info.selectedList, hancelExitSelect) }} />
<MusicDownloadModal ref={musicDownloadModalRef}
onDownloadInfo={(info) => { }} />
<ListMenu
ref={listMenuRef}
onPlay={info => { handlePlay(info.listId, info.index) }}
@ -156,7 +160,7 @@ export default () => {
onRemove={info => { hancelExitSelect(); handleRemove(info.listId, info.musicInfo, info.selectedList, hancelExitSelect) }}
onDislikeMusic={info => { void handleDislikeMusic(info.musicInfo) }}
onCopyName={info => { handleShare(info.musicInfo) }}
onMusicSourceDetail={info => { void handleShowMusicSourceDetail(info.musicInfo) }}
onDownload={info => musicDownloadModalRef.current?.show(info.musicInfo)}
onAdd={handleAddMusic}
onMove={handleMoveMusic}
onEditMetadata={handleEditMetadata}

View File

@ -2,14 +2,17 @@ import { removeListMusics, updateListMusicPosition, updateListMusics } from '@/c
import { playList, playNext } from '@/core/player/player'
import { addTempPlayList } from '@/core/player/tempPlayList'
import settingState from '@/store/setting/state'
import { similar, sortInsert, toOldMusicInfo } from '@/utils'
import { confirmDialog, openUrl, shareMusic, toast } from '@/utils/tools'
import { similar, sortInsert } from '@/utils'
import { confirmDialog, requestStoragePermission, shareMusic, toast } from '@/utils/tools'
import { addDislikeInfo, hasDislike } from '@/core/dislikeList'
import playerState from '@/store/player/state'
import RNFetchBlob from 'rn-fetch-blob'
import type { SelectInfo } from './ListMenu'
import { type Metadata } from '@/components/MetadataEditModal'
import musicSdk from '@/utils/musicSdk'
import { getMusicUrl } from '@/core/music'
import log from '@/plugins/sync/log'
import { setStatusText } from '@/core/player/playStatus'
export const handlePlay = (listId: SelectInfo['listId'], index: SelectInfo['index']) => {
void playList(listId, index)
@ -23,6 +26,69 @@ export const handlePlayLater = (listId: SelectInfo['listId'], musicInfo: SelectI
}
}
export function getFileExtension(url: string) {
// 使用正则表达式匹配URL中的文件扩展名
const match = url.match(/\.([0-9a-z]+)(?=[?#]|$)/i);
// 如果匹配到扩展名,则返回该扩展名,否则返回默认值'mp3'
return match ? match[1] : 'mp3';
}
/**
*
*/
const diffCurrentMusicInfo = (curMusicInfo: LX.Music.MusicInfo | LX.Download.ListItem): boolean => {
// return curMusicInfo !== playerState.playMusicInfo.musicInfo || playerState.isPlay
return curMusicInfo.id != global.lx.gettingUrlId || curMusicInfo.id != playerState.playMusicInfo.musicInfo?.id || playerState.isPlay
}
// export const handelDownload = (musicInfo: LX.Music.MusicInfoOnline) => {
export const handelDownload = (musicInfo: any, quality: LX.Quality) => {
return requestStoragePermission().then(async () => {
console.log(quality);
try {
getMusicUrl({
musicInfo, quality, isRefresh: true, onToggleSource(mInfo) {
if (diffCurrentMusicInfo(musicInfo)) return
setStatusText(global.i18n.t('toggle_source_try'))
},
}).then(url => {
console.log(url);
const extension = getFileExtension(url);
const fileName = musicInfo.name;
const downloadDir = RNFetchBlob.fs.dirs.DownloadDir + "/lx.music";
const path = `${downloadDir}/${fileName}.${extension}`
const config = {
fileCache: true,
addAndroidDownloads: {
useDownloadManager: true,
notification: true,
path: path,
description: '正在下载文件...',
},
};
RNFetchBlob.config(config)
.fetch('GET', url)
.then((res) => {
console.log('文件下载成功!路径:', res.path());
toast("文件下载成功!", 'long')
})
.catch((error) => {
console.log('文件下载失败:', error);
});
}).catch(e => {
console.log('getMusicUrl e', e);
});
} catch (e) {
console.log('文件下载e', e);
}
}).catch((e) => {
return Promise.reject(e ?? "权限获取失败")
})
}
export const handleRemove = (listId: SelectInfo['listId'], musicInfo: SelectInfo['musicInfo'], selectedList: SelectInfo['selectedList'], onCancelSelect: () => void) => {
if (selectedList.length) {
void confirmDialog({
@ -90,16 +156,9 @@ export const searchListMusic = (list: LX.Music.MusicInfo[], text: string) => {
return sortedList.map(item => item.data).reverse()
}
export const handleShowMusicSourceDetail = async(minfo: SelectInfo['musicInfo']) => {
const url = musicSdk[minfo.source as LX.OnlineSource]?.getMusicDetailPageUrl(toOldMusicInfo(minfo))
if (!url) return
void openUrl(url)
}
export const handleDislikeMusic = async(musicInfo: SelectInfo['musicInfo']) => {
export const handleDislikeMusic = async (musicInfo: SelectInfo['musicInfo']) => {
const confirm = await confirmDialog({
message: musicInfo.singer ? global.i18n.t('lists_dislike_music_singer_tip', { name: musicInfo.name, singer: musicInfo.singer }) : global.i18n.t('lists_dislike_music_tip', { name: musicInfo.name }),
message: global.i18n.t('lists_dislike_music_tip', { name: musicInfo.name }),
cancelButtonText: global.i18n.t('cancel_button_text_2'),
confirmButtonText: global.i18n.t('confirm_button_text'),
bgClose: false,

View File

@ -46,7 +46,8 @@ export default memo(() => {
const styles = createStyle({
content: {
marginTop: 10,
marginTop: 0,
marginBottom: 15,
},
})

View File

@ -0,0 +1,90 @@
import { memo, useEffect, useMemo, useState } from 'react'
import { View } from 'react-native'
import InputItem, { type InputItemProps } from '../../components/InputItem'
import { createStyle, toast } from '@/utils/tools'
import { useSettingValue } from '@/store/setting/hook'
import { useI18n } from '@/lang'
import { updateSetting } from '@/core/common'
import CheckBox from '@/components/common/CheckBox'
import SubTitle from '../../components/SubTitle'
import settingState from '@/store/setting/state'
const MAX_SIZE = 1024 * 1024 * 1024
export default memo(() => {
const t = useI18n()
const [playQualityList, setPlayQualityList] = useState<MusicOption[]>([]);
useEffect(() => {
setPlayQualityList([
{
id: "128k",
key: "128k",
name: "标准音质"
},
{
id: "320k",
key: "320k",
name: "高品音质"
},
{
id: "flac",
key: "flac",
name: "无损音质"
},
{
id: "flac24bit",
key: "flac24bit",
name: "Hi-Res音质"
}
]);
}, [])
const [selectedQuality, setSelectedQuality] = useState<LX.Quality>(useSettingValue('player.playQuality'));
const setPlayQuality = (playQuality: LX.Quality) => {
updateSetting({ 'player.playQuality': playQuality })
setSelectedQuality(playQuality);
}
interface MusicOption {
id: LX.Quality;
name: string;
size?: string | null;
key?: string
}
const useActive = (id: LX.Quality) => {
const isActive = useMemo(() => selectedQuality == id, [selectedQuality, id])
return isActive
}
const Item = ({ id, name }: {
id: LX.Quality
name: string
}) => {
const isActive = useActive(id)
return <CheckBox marginRight={8} check={isActive} label={name} onChange={() => { setPlayQuality(id) }} need />
}
return (
<View style={styles.content} >
<SubTitle title={t('setting_play_select')}>
<View style={styles.list}>
{
playQualityList.map((item) => <Item name={item.name} id={item.id} key={item.key} />)
}
</View>
</SubTitle>
</View>
)
})
const styles = createStyle({
content: {
marginTop: 10,
},
list: {
flexDirection: 'column',
flexWrap: 'nowrap',
},
})

View File

@ -2,13 +2,14 @@ import { memo } from 'react'
import Section from '../../components/Section'
import IsSavePlayTime from './IsSavePlayTime'
import PlayHighQuality from './PlayHighQuality'
import IsPlayHighQuality from './IsPlayHighQuality'
import IsHandleAudioFocus from './IsHandleAudioFocus'
import IsEnableAudioOffload from './IsEnableAudioOffload'
import IsAutoCleanPlayedList from './IsAutoCleanPlayedList'
import IsShowNotificationImage from './IsShowNotificationImage'
import IsShowLyricTranslation from './IsShowLyricTranslation'
import IsShowLyricRoma from './IsShowLyricRoma'
import SelectPlayQuality from './SelectPlayQuality'
import IsS2T from './IsS2T'
import MaxCache from './MaxCache'
import { useI18n } from '@/lang'
@ -21,14 +22,15 @@ export default memo(() => {
<Section title={t('setting_player')}>
<IsSavePlayTime />
<IsAutoCleanPlayedList />
{/* <IsPlayHighQuality /> */}
<IsHandleAudioFocus />
<IsEnableAudioOffload />
<IsShowNotificationImage />
<IsShowLyricTranslation />
<IsShowLyricRoma />
<IsS2T />
<SelectPlayQuality />
<MaxCache />
<PlayHighQuality />
</Section>
)
})

View File

@ -21,7 +21,7 @@ export default memo(() => {
const progress = useVersionDownloadProgressUpdated()
const handleOpenVersionModal = () => {
// setVersionInfo({ showModal: true })
showModal()
// showModal()
}
useEffect(() => {

View File

@ -4,6 +4,7 @@ declare namespace LX {
type OnlineSource = 'kw' | 'kg' | 'tx' | 'wy' | 'mg'
type Source = OnlineSource | 'local'
type Quality = '128k' | '320k' | 'flac' | 'flac24bit' | '192k' | 'ape' | 'wav'
type
type QualityList = Partial<Record<LX.Source, LX.Quality[]>>
type ShareType = 'system' | 'clipboard'