mirror of
https://github.com/ikun0014/lx-music-mobile.git
synced 2025-05-23 22:37:41 +08:00
Merge branch 'beta' of https://github.com/xiaocge/lx-music-mobile into dev
This commit is contained in:
commit
4d200c01f8
@ -1,6 +1,7 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
upgrade: true,
|
upgrade: true,
|
||||||
reject: [
|
reject: [
|
||||||
|
'react-native-navigation',
|
||||||
'@types/react-native',
|
'@types/react-native',
|
||||||
'message2call',
|
'message2call',
|
||||||
'react',
|
'react',
|
||||||
@ -9,11 +10,6 @@ module.exports = {
|
|||||||
'react-native-navigation',
|
'react-native-navigation',
|
||||||
],
|
],
|
||||||
|
|
||||||
// target: 'newest',
|
|
||||||
// filter: [
|
|
||||||
// 'react-native-navigation',
|
|
||||||
// ],
|
|
||||||
|
|
||||||
// target: 'patch',
|
// target: 'patch',
|
||||||
// filter: [
|
// filter: [
|
||||||
// '@types/react-native',
|
// '@types/react-native',
|
||||||
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -10,5 +10,6 @@
|
|||||||
"google-cn",
|
"google-cn",
|
||||||
"google"
|
"google"
|
||||||
],
|
],
|
||||||
"i18n-ally.sortKeys": true
|
"i18n-ally.sortKeys": true,
|
||||||
|
"java.configuration.updateBuildConfiguration": "disabled"
|
||||||
}
|
}
|
@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
- Android
|
- Android
|
||||||
|
|
||||||
注:由于没有相关开发环境及证书,所以不计划支持IOS
|
注:不计划支持IOS
|
||||||
|
|
||||||
软件变化请查看:[更新日志](https://github.com/lyswhut/lx-music-mobile/blob/master/CHANGELOG.md)<br>
|
软件变化请查看:[更新日志](https://github.com/lyswhut/lx-music-mobile/blob/master/CHANGELOG.md)<br>
|
||||||
软件下载请转到:[发布页面](https://github.com/lyswhut/lx-music-mobile/releases)<br>
|
软件下载请转到:[发布页面](https://github.com/lyswhut/lx-music-mobile/releases)<br>
|
||||||
|
@ -127,21 +127,10 @@ android {
|
|||||||
}
|
}
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
release {
|
release {
|
||||||
if (project.hasProperty('MYAPP_UPLOAD_STORE_FILE')) {
|
storeFile file('debug.keystore')
|
||||||
storeFile file(MYAPP_UPLOAD_STORE_FILE)
|
storePassword 'android'
|
||||||
storePassword MYAPP_UPLOAD_STORE_PASSWORD
|
keyAlias 'androiddebugkey'
|
||||||
keyAlias MYAPP_UPLOAD_KEY_ALIAS
|
keyPassword 'android'
|
||||||
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']
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
debug {
|
debug {
|
||||||
storeFile file('debug.keystore')
|
storeFile file('debug.keystore')
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
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
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
8051
package-lock.json
generated
8051
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
64
package.json
64
package.json
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "lx-music-mobile-mod",
|
"name": "lx-music-mobile",
|
||||||
"version": "3.3.5",
|
"version": "8.8.8",
|
||||||
"versionCode": 67,
|
"versionCode": 64,
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "react-native run-android --active-arch-only",
|
"dev": "react-native run-android --active-arch-only",
|
||||||
@ -13,7 +13,6 @@
|
|||||||
"rd": "react-devtools",
|
"rd": "react-devtools",
|
||||||
"menu": "adb shell input keyevent 82",
|
"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",
|
"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:android:debug": "./gradlew assembleDebug",
|
||||||
"pack": "npm run pack:android",
|
"pack": "npm run pack:android",
|
||||||
"pack:android": "cd android && gradlew.bat assembleRelease",
|
"pack:android": "cd android && gradlew.bat assembleRelease",
|
||||||
@ -28,65 +27,66 @@
|
|||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/ikunshare/lx-music-mobile-mod.git"
|
"url": "git+https://github.com/lyswhut/lx-music-mobile.git"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"music-player",
|
"music-player",
|
||||||
"react-native-app"
|
"react-native-app"
|
||||||
],
|
],
|
||||||
"author": {
|
"author": {
|
||||||
"name": "ikun0014",
|
"name": "lyswhut",
|
||||||
"email": "ikun0014@qq.com"
|
"email": "lyswhut@qq.com"
|
||||||
},
|
},
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"bugs": {
|
"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": {
|
"dependencies": {
|
||||||
"@craftzdog/react-native-buffer": "^6.0.5",
|
"@craftzdog/react-native-buffer": "^6.0.5",
|
||||||
"@react-native-async-storage/async-storage": "^1.24.0",
|
"@react-native-async-storage/async-storage": "^1.22.3",
|
||||||
"@react-native-clipboard/clipboard": "^1.14.1",
|
"@react-native-clipboard/clipboard": "^1.13.2",
|
||||||
"@react-native-community/slider": "^4.5.2",
|
"@react-native-community/slider": "^4.5.0",
|
||||||
"iconv-lite": "^0.6.3",
|
"iconv-lite": "^0.6.3",
|
||||||
"lrc-file-parser": "^2.4.1",
|
"lrc-file-parser": "^2.4.1",
|
||||||
"message2call": "^0.1.3",
|
"message2call": "^0.1.3",
|
||||||
"pako": "^2.1.0",
|
"pako": "^2.1.0",
|
||||||
"react": "18.2.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-background-timer": "github:lyswhut/react-native-background-timer#55ecaa80880e9cec1fff81f3ce10e6250ab3c40c",
|
||||||
"react-native-exception-handler": "^2.10.10",
|
"react-native-exception-handler": "^2.10.10",
|
||||||
"react-native-fast-image": "^8.6.3",
|
"react-native-fast-image": "^8.6.3",
|
||||||
"react-native-file-system": "github:lyswhut/react-native-file-system#cb3b807ac68c5cdb2c32ca10fdbb5e5209154ece",
|
"react-native-file-system": "github:lyswhut/react-native-file-system#cb3b807ac68c5cdb2c32ca10fdbb5e5209154ece",
|
||||||
"react-native-fs": "^2.20.0",
|
"react-native-fs": "^2.20.0",
|
||||||
"react-native-local-media-metadata": "github:lyswhut/react-native-local-media-metadata#5e55e67ecd1d557f29453e1cf34ff087efabbc7b",
|
"react-native-local-media-metadata": "github:lyswhut/react-native-local-media-metadata#c8377d82c04aecf5ee79d446276ab9d0c1f167be",
|
||||||
"react-native-navigation": "7.39.2",
|
"react-native-navigation": "^7.38.1",
|
||||||
"react-native-pager-view": "6.3.0",
|
"react-native-pager-view": "^6.2.3",
|
||||||
"react-native-quick-base64": "^2.1.2",
|
"react-native-quick-base64": "^2.0.8",
|
||||||
"react-native-quick-md5": "^3.0.6",
|
"react-native-quick-md5": "^3.0.6",
|
||||||
"react-native-track-player": "github:lyswhut/react-native-track-player#a567c8b0a319e41430afdb782526723243ea3950",
|
"react-native-track-player": "github:lyswhut/react-native-track-player#ddfb984ec12efb0d6ae84c924e027f872bb1481f",
|
||||||
"react-native-vector-icons": "^10.1.0"
|
"react-native-vector-icons": "^10.0.3",
|
||||||
|
"rn-fetch-blob": "^0.12.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.25.2",
|
"@babel/core": "^7.24.0",
|
||||||
"@babel/eslint-parser": "^7.25.1",
|
"@babel/eslint-parser": "^7.23.10",
|
||||||
"@babel/plugin-proposal-export-namespace-from": "^7.18.9",
|
"@babel/plugin-proposal-export-namespace-from": "^7.18.9",
|
||||||
"@babel/preset-env": "^7.25.3",
|
"@babel/preset-env": "^7.24.0",
|
||||||
"@babel/runtime": "^7.25.0",
|
"@babel/runtime": "^7.24.0",
|
||||||
"@react-native/babel-preset": "^0.74.86",
|
"@react-native/babel-preset": "^0.74.0",
|
||||||
"@react-native/metro-config": "^0.74.86",
|
"@react-native/metro-config": "^0.73.5",
|
||||||
"@react-native/typescript-config": "^0.74.86",
|
"@react-native/typescript-config": "^0.74.0",
|
||||||
"@tsconfig/react-native": "^3.0.5",
|
"@tsconfig/react-native": "^3.0.3",
|
||||||
"@types/react": "^18.3.3",
|
"@types/react": "^18.2.66",
|
||||||
"@types/react-native": "^0.72.8",
|
"@types/react-native": "^0.72.8",
|
||||||
"@types/react-native-background-timer": "^2.0.2",
|
"@types/react-native-background-timer": "^2.0.2",
|
||||||
"@types/react-native-vector-icons": "^6.4.18",
|
"@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",
|
"changelog-parser": "^3.0.1",
|
||||||
"eslint-config-standard": "^17.1.0",
|
"eslint-config-standard": "^17.1.0",
|
||||||
"eslint-config-standard-with-typescript": "^43.0.1",
|
"eslint-config-standard-with-typescript": "^43.0.1",
|
||||||
"eslint-plugin-react": "^7.35.0",
|
"eslint-plugin-react": "^7.34.1",
|
||||||
"eslint-plugin-react-hooks": "^4.6.2",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
"typescript": "^5.5.4"
|
"typescript": "^5.4.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ export interface ListMenuProps {
|
|||||||
onCopyName: (selectInfo: SelectInfo) => void
|
onCopyName: (selectInfo: SelectInfo) => void
|
||||||
onMusicSourceDetail: (selectInfo: SelectInfo) => void
|
onMusicSourceDetail: (selectInfo: SelectInfo) => void
|
||||||
onDislikeMusic: (selectInfo: SelectInfo) => void
|
onDislikeMusic: (selectInfo: SelectInfo) => void
|
||||||
|
onDownload: (selectInfo: SelectInfo) => void
|
||||||
}
|
}
|
||||||
export interface ListMenuType {
|
export interface ListMenuType {
|
||||||
show: (selectInfo: SelectInfo, position: Position) => void
|
show: (selectInfo: SelectInfo, position: Position) => void
|
||||||
@ -52,7 +53,7 @@ export default forwardRef<ListMenuType, ListMenuProps>((props: ListMenuProps, re
|
|||||||
return [
|
return [
|
||||||
{ action: 'play', label: t('play') },
|
{ action: 'play', label: t('play') },
|
||||||
{ action: 'playLater', label: t('play_later') },
|
{ action: 'playLater', label: t('play_later') },
|
||||||
// { action: 'download', label: '下载' },
|
{ action: 'download', label: '下载' },
|
||||||
{ action: 'add', label: t('add_to') },
|
{ action: 'add', label: t('add_to') },
|
||||||
{ action: 'copyName', label: t('copy_name') },
|
{ action: 'copyName', label: t('copy_name') },
|
||||||
{ action: 'musicSourceDetail', label: t('music_source_detail') },
|
{ action: 'musicSourceDetail', label: t('music_source_detail') },
|
||||||
@ -82,6 +83,8 @@ export default forwardRef<ListMenuType, ListMenuProps>((props: ListMenuProps, re
|
|||||||
case 'dislike':
|
case 'dislike':
|
||||||
props.onDislikeMusic(selectInfo)
|
props.onDislikeMusic(selectInfo)
|
||||||
break
|
break
|
||||||
|
case 'download':
|
||||||
|
props.onDownload(selectInfo)
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -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 ListMenu, { type ListMenuType, type Position, type SelectInfo } from './ListMenu'
|
||||||
import ListMusicMultiAdd, { type MusicMultiAddModalType as ListAddMultiType } from '@/components/MusicMultiAddModal'
|
import ListMusicMultiAdd, { type MusicMultiAddModalType as ListAddMultiType } from '@/components/MusicMultiAddModal'
|
||||||
import ListMusicAdd, { type MusicAddModalType as ListMusicAddType } from '@/components/MusicAddModal'
|
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 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 { createStyle } from '@/utils/tools'
|
||||||
|
import { handelDownload } from '@/screens/Home/Views/Mylist/MusicList/listAction'
|
||||||
|
|
||||||
export interface OnlineListProps {
|
export interface OnlineListProps {
|
||||||
onRefresh: ListProps['onRefresh']
|
onRefresh: ListProps['onRefresh']
|
||||||
@ -37,6 +39,7 @@ export default forwardRef<OnlineListType, OnlineListProps>(({
|
|||||||
const listMusicAddRef = useRef<ListMusicAddType>(null)
|
const listMusicAddRef = useRef<ListMusicAddType>(null)
|
||||||
const listMusicMultiAddRef = useRef<ListAddMultiType>(null)
|
const listMusicMultiAddRef = useRef<ListAddMultiType>(null)
|
||||||
const listMenuRef = useRef<ListMenuType>(null)
|
const listMenuRef = useRef<ListMenuType>(null)
|
||||||
|
const musicDownloadModalRef = useRef<MusicDownloadModalType>(null)
|
||||||
// const loadingMaskRef = useRef<LoadingMaskType>(null)
|
// const loadingMaskRef = useRef<LoadingMaskType>(null)
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
@ -103,13 +106,15 @@ export default forwardRef<OnlineListType, OnlineListProps>(({
|
|||||||
</View>
|
</View>
|
||||||
<ListMusicAdd ref={listMusicAddRef} onAdded={() => { hancelExitSelect() }} />
|
<ListMusicAdd ref={listMusicAddRef} onAdded={() => { hancelExitSelect() }} />
|
||||||
<ListMusicMultiAdd ref={listMusicMultiAddRef} onAdded={() => { hancelExitSelect() }} />
|
<ListMusicMultiAdd ref={listMusicMultiAddRef} onAdded={() => { hancelExitSelect() }} />
|
||||||
|
<MusicDownloadModal ref={musicDownloadModalRef}
|
||||||
|
onDownloadInfo={(info) => { }} />
|
||||||
<ListMenu
|
<ListMenu
|
||||||
ref={listMenuRef}
|
ref={listMenuRef}
|
||||||
onPlay={info => { handlePlay(info.musicInfo) }}
|
onPlay={info => { handlePlay(info.musicInfo) }}
|
||||||
onPlayLater={info => { hancelExitSelect(); handlePlayLater(info.musicInfo, info.selectedList, hancelExitSelect) }}
|
onPlayLater={info => { hancelExitSelect(); handlePlayLater(info.musicInfo, info.selectedList, hancelExitSelect) }}
|
||||||
onCopyName={info => { handleShare(info.musicInfo) }}
|
onCopyName={info => { handleShare(info.musicInfo) }}
|
||||||
onAdd={handleAddMusic}
|
onAdd={handleAddMusic}
|
||||||
onMusicSourceDetail={info => { void handleShowMusicSourceDetail(info.musicInfo) }}
|
onDownload={info => musicDownloadModalRef.current?.show(info.musicInfo)}
|
||||||
onDislikeMusic={info => { void handleDislikeMusic(info.musicInfo) }}
|
onDislikeMusic={info => { void handleDislikeMusic(info.musicInfo) }}
|
||||||
/>
|
/>
|
||||||
{/* <LoadingMask ref={loadingMaskRef} /> */}
|
{/* <LoadingMask ref={loadingMaskRef} /> */}
|
||||||
|
@ -33,6 +33,7 @@ export const getMusicUrl = async({
|
|||||||
if ('progress' in musicInfo) {
|
if ('progress' in musicInfo) {
|
||||||
return getDownloadMusicUrl({ musicInfo, isRefresh, onToggleSource })
|
return getDownloadMusicUrl({ musicInfo, isRefresh, onToggleSource })
|
||||||
} else if (musicInfo.source == 'local') {
|
} else if (musicInfo.source == 'local') {
|
||||||
|
|
||||||
return getLocalMusicUrl({ musicInfo, isRefresh, onToggleSource })
|
return getLocalMusicUrl({ musicInfo, isRefresh, onToggleSource })
|
||||||
} else {
|
} else {
|
||||||
return getOnlineMusicUrl({ musicInfo, isRefresh, quality, onToggleSource })
|
return getOnlineMusicUrl({ musicInfo, isRefresh, quality, onToggleSource })
|
||||||
|
@ -208,19 +208,17 @@ export const getOnlineOtherSourcePicByLocal = async(musicInfo: LX.Music.MusicInf
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TRY_QUALITYS_LIST = ['flac24bit', 'flac', '320k'] as const
|
// export const getPlayQuality = (highQuality: boolean, musicInfo: LX.Music.MusicInfoOnline): LX.Quality => {
|
||||||
type TryQualityType = typeof TRY_QUALITYS_LIST[number]
|
// let type: LX.Quality = '128k'
|
||||||
export const getPlayQuality = (highQuality: LX.Quality, musicInfo: LX.Music.MusicInfoOnline): LX.Quality => {
|
// 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'
|
let type: LX.Quality = '128k'
|
||||||
if (TRY_QUALITYS_LIST.includes(highQuality as TryQualityType)) {
|
|
||||||
let list = global.lx.qualityList[musicInfo.source]
|
let list = global.lx.qualityList[musicInfo.source]
|
||||||
|
if (musicInfo.meta._qualitys[playQuality] && list && list.includes(playQuality)) type = playQuality
|
||||||
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
|
|
||||||
}
|
|
||||||
return type
|
return type
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,6 +243,7 @@ export const getOnlineOtherSourceMusicUrl = async({ musicInfos, quality, onToggl
|
|||||||
if (retryedSource.includes(musicInfo.source)) continue
|
if (retryedSource.includes(musicInfo.source)) continue
|
||||||
retryedSource.push(musicInfo.source)
|
retryedSource.push(musicInfo.source)
|
||||||
if (!assertApiSupport(musicInfo.source)) continue
|
if (!assertApiSupport(musicInfo.source)) continue
|
||||||
|
// itemQuality = quality ?? getPlayQuality(settingState.setting['player.isPlayHighQuality'], musicInfo)
|
||||||
itemQuality = quality ?? getPlayQuality(settingState.setting['player.playQuality'], musicInfo)
|
itemQuality = quality ?? getPlayQuality(settingState.setting['player.playQuality'], musicInfo)
|
||||||
if (!musicInfo.meta._qualitys[itemQuality]) continue
|
if (!musicInfo.meta._qualitys[itemQuality]) continue
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
"change_position_list_title": "Change the position of the list",
|
"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_multi_title": "Adjust the position of the selected {num} song to",
|
||||||
"change_position_music_title": "Adjust the position of {name} 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",
|
"change_position_tip": "Please enter a new position",
|
||||||
"close": "Close",
|
"close": "Close",
|
||||||
"collect": "Collect",
|
"collect": "Collect",
|
||||||
@ -337,6 +338,7 @@
|
|||||||
"setting_play_auto_clean_played_list": "Automatically clear the played list",
|
"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_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_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_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_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",
|
"setting_play_handle_audio_focus": "When other apps play sound, automatically pause the playback",
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
"change_position_list_title": "调整列表位置",
|
"change_position_list_title": "调整列表位置",
|
||||||
"change_position_music_multi_title": "将已选的 {num} 首歌曲的位置调整到",
|
"change_position_music_multi_title": "将已选的 {num} 首歌曲的位置调整到",
|
||||||
"change_position_music_title": "将 {name} 的位置调整到",
|
"change_position_music_title": "将 {name} 的位置调整到",
|
||||||
|
"download_music_title": "下载 {name}",
|
||||||
"change_position_tip": "请输入新的位置",
|
"change_position_tip": "请输入新的位置",
|
||||||
"close": "关闭",
|
"close": "关闭",
|
||||||
"collect": "收藏",
|
"collect": "收藏",
|
||||||
@ -336,6 +337,7 @@
|
|||||||
"setting_play_auto_clean_played_list": "自动清空已播放列表",
|
"setting_play_auto_clean_played_list": "自动清空已播放列表",
|
||||||
"setting_play_auto_clean_played_list_tip": "随机播放模式下,通过 「点击」 与 「播放列表相同的列表内的歌曲」 切歌时,若启用 「自动清空已播放列表」,则已播放的歌曲将重新参与随机播放。",
|
"setting_play_auto_clean_played_list_tip": "随机播放模式下,通过 「点击」 与 「播放列表相同的列表内的歌曲」 切歌时,若启用 「自动清空已播放列表」,则已播放的歌曲将重新参与随机播放。",
|
||||||
"setting_play_cache_size": "最大缓存大小(MB)",
|
"setting_play_cache_size": "最大缓存大小(MB)",
|
||||||
|
"setting_play_select": "播放音质选择",
|
||||||
"setting_play_cache_size_no_cache": "禁用缓存",
|
"setting_play_cache_size_no_cache": "禁用缓存",
|
||||||
"setting_play_cache_size_save_tip": "缓存设置完毕,重启应用后生效",
|
"setting_play_cache_size_save_tip": "缓存设置完毕,重启应用后生效",
|
||||||
"setting_play_handle_audio_focus": "其他应用播放声音时,自动暂停播放",
|
"setting_play_handle_audio_focus": "其他应用播放声音时,自动暂停播放",
|
||||||
|
@ -68,7 +68,7 @@ const Footer = ({ componentId }: { componentId: string }) => {
|
|||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
const isAgreePact = useSettingValue('common.isAgreePact')
|
const isAgreePact = useSettingValue('common.isAgreePact')
|
||||||
// const checkUpdate = useDispatch('common', 'checkUpdate')
|
// const checkUpdate = useDispatch('common', 'checkUpdate')
|
||||||
const [time, setTime] = useState(20)
|
const [time, setTime] = useState(1)
|
||||||
|
|
||||||
const handleRejct = () => {
|
const handleRejct = () => {
|
||||||
exitApp()
|
exitApp()
|
||||||
|
@ -21,9 +21,9 @@ export interface ListMenuProps {
|
|||||||
onEditMetadata: (selectInfo: SelectInfo) => void
|
onEditMetadata: (selectInfo: SelectInfo) => void
|
||||||
onCopyName: (selectInfo: SelectInfo) => void
|
onCopyName: (selectInfo: SelectInfo) => void
|
||||||
onChangePosition: (selectInfo: SelectInfo) => void
|
onChangePosition: (selectInfo: SelectInfo) => void
|
||||||
onMusicSourceDetail: (selectInfo: SelectInfo) => void
|
|
||||||
onDislikeMusic: (selectInfo: SelectInfo) => void
|
onDislikeMusic: (selectInfo: SelectInfo) => void
|
||||||
onRemove: (selectInfo: SelectInfo) => void
|
onRemove: (selectInfo: SelectInfo) => void
|
||||||
|
onDownload: (selectInfo: SelectInfo) => void
|
||||||
}
|
}
|
||||||
export interface ListMenuType {
|
export interface ListMenuType {
|
||||||
show: (selectInfo: SelectInfo, position: Position) => void
|
show: (selectInfo: SelectInfo, position: Position) => void
|
||||||
@ -63,29 +63,38 @@ export default forwardRef<ListMenuType, ListMenuProps>((props, ref) => {
|
|||||||
const menu = [
|
const menu = [
|
||||||
{ action: 'play', label: t('play') },
|
{ action: 'play', label: t('play') },
|
||||||
{ action: 'playLater', label: t('play_later') },
|
{ action: 'playLater', label: t('play_later') },
|
||||||
// { action: 'download', label: '下载' },
|
{ action: 'download', label: '下载' },
|
||||||
{ action: 'add', label: t('add_to') },
|
{ action: 'add', label: t('add_to') },
|
||||||
{ action: 'move', label: t('move_to') },
|
{ action: 'move', label: t('move_to') },
|
||||||
{ action: 'changePosition', label: t('change_position') },
|
|
||||||
{ action: 'copyName', label: t('copy_name') },
|
{ action: 'copyName', label: t('copy_name') },
|
||||||
{ action: 'musicSourceDetail', disabled: musicInfo.source == 'local', label: t('music_source_detail') },
|
{ action: 'changePosition', label: t('change_position') },
|
||||||
// { action: 'musicSearch', label: t('music_search') },
|
|
||||||
{ action: 'dislike', disabled: hasDislike(musicInfo), label: t('dislike') },
|
{ action: 'dislike', disabled: hasDislike(musicInfo), label: t('dislike') },
|
||||||
{ action: 'remove', label: t('delete') },
|
{ 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)
|
setMenus(menu)
|
||||||
void Promise.all([hasEditMetadata(musicInfo)]).then(([_edit_metadata]) => {
|
void Promise.all([hasEditMetadata(musicInfo)]).then(([_edit_metadata]) => {
|
||||||
// console.log(_edit_metadata)
|
// console.log(_edit_metadata)
|
||||||
let isUpdated = false
|
let isUpdated = true
|
||||||
if (edit_metadata != _edit_metadata) {
|
if (edit_metadata != _edit_metadata) {
|
||||||
edit_metadata = _edit_metadata
|
edit_metadata = _edit_metadata
|
||||||
isUpdated ||= true
|
isUpdated ||= true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isUpdated) {
|
if (isUpdated) {
|
||||||
menu[menu.findIndex(m => m.action == 'editMetadata')].disabled = !edit_metadata
|
const menu = [
|
||||||
setMenus([...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)
|
props.onChangePosition(selectInfo)
|
||||||
// setVIsibleMusicPosition(true)
|
// setVIsibleMusicPosition(true)
|
||||||
break
|
break
|
||||||
case 'musicSourceDetail':
|
|
||||||
props.onMusicSourceDetail(selectInfo)
|
|
||||||
// setVIsibleMusicPosition(true)
|
|
||||||
break
|
|
||||||
case 'dislike':
|
case 'dislike':
|
||||||
props.onDislikeMusic(selectInfo)
|
props.onDislikeMusic(selectInfo)
|
||||||
break
|
break
|
||||||
case 'remove':
|
case 'remove':
|
||||||
props.onRemove(selectInfo)
|
props.onRemove(selectInfo)
|
||||||
break
|
break
|
||||||
|
case 'download':
|
||||||
|
props.onDownload(selectInfo)
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
210
src/screens/Home/Views/Mylist/MusicList/MusicDownloadModal.tsx
Normal file
210
src/screens/Home/Views/Mylist/MusicList/MusicDownloadModal.tsx
Normal 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',
|
||||||
|
},
|
||||||
|
})
|
@ -2,7 +2,7 @@ import { useCallback, useRef } from 'react'
|
|||||||
|
|
||||||
import listState from '@/store/list/state'
|
import listState from '@/store/list/state'
|
||||||
import ListMenu, { type ListMenuType, type Position, type SelectInfo } from './ListMenu'
|
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 List, { type ListType } from './List'
|
||||||
import ListMusicAdd, { type MusicAddModalType as ListMusicAddType } from '@/components/MusicAddModal'
|
import ListMusicAdd, { type MusicAddModalType as ListMusicAddType } from '@/components/MusicAddModal'
|
||||||
import ListMusicMultiAdd, { type MusicMultiAddModalType as ListAddMultiType } from '@/components/MusicMultiAddModal'
|
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 ListSearchBar, { type ListSearchBarType } from './ListSearchBar'
|
||||||
import ListMusicSearch, { type ListMusicSearchType } from './ListMusicSearch'
|
import ListMusicSearch, { type ListMusicSearchType } from './ListMusicSearch'
|
||||||
import MusicPositionModal, { type MusicPositionModalType } from './MusicPositionModal'
|
import MusicPositionModal, { type MusicPositionModalType } from './MusicPositionModal'
|
||||||
|
import MusicDownloadModal, { type MusicDownloadModalType } from './MusicDownloadModal'
|
||||||
import MetadataEditModal, { type MetadataEditType, type MetadataEditProps } from '@/components/MetadataEditModal'
|
import MetadataEditModal, { type MetadataEditType, type MetadataEditProps } from '@/components/MetadataEditModal'
|
||||||
|
|
||||||
|
|
||||||
@ -26,6 +27,7 @@ export default () => {
|
|||||||
const listMusicAddRef = useRef<ListMusicAddType>(null)
|
const listMusicAddRef = useRef<ListMusicAddType>(null)
|
||||||
const listMusicMultiAddRef = useRef<ListAddMultiType>(null)
|
const listMusicMultiAddRef = useRef<ListAddMultiType>(null)
|
||||||
const musicPositionModalRef = useRef<MusicPositionModalType>(null)
|
const musicPositionModalRef = useRef<MusicPositionModalType>(null)
|
||||||
|
const musicDownloadModalRef = useRef<MusicDownloadModalType>(null)
|
||||||
const metadataEditTypeRef = useRef<MetadataEditType>(null)
|
const metadataEditTypeRef = useRef<MetadataEditType>(null)
|
||||||
const listMenuRef = useRef<ListMenuType>(null)
|
const listMenuRef = useRef<ListMenuType>(null)
|
||||||
const layoutHeightRef = useRef<number>(0)
|
const layoutHeightRef = useRef<number>(0)
|
||||||
@ -87,7 +89,7 @@ export default () => {
|
|||||||
const handleScrollToInfo = useCallback((info: LX.Music.MusicInfo) => {
|
const handleScrollToInfo = useCallback((info: LX.Music.MusicInfo) => {
|
||||||
listRef.current?.scrollToInfo(info)
|
listRef.current?.scrollToInfo(info)
|
||||||
handleExitSearch()
|
handleExitSearch()
|
||||||
}, [handleExitSearch])
|
}, [])
|
||||||
const onLayout = useCallback((e: LayoutChangeEvent) => {
|
const onLayout = useCallback((e: LayoutChangeEvent) => {
|
||||||
layoutHeightRef.current = e.nativeEvent.layout.height
|
layoutHeightRef.current = e.nativeEvent.layout.height
|
||||||
}, [])
|
}, [])
|
||||||
@ -145,10 +147,12 @@ export default () => {
|
|||||||
onScrollToInfo={handleScrollToInfo}
|
onScrollToInfo={handleScrollToInfo}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<ListMusicAdd ref={listMusicAddRef} onAdded={hancelExitSelect} />
|
<ListMusicAdd ref={listMusicAddRef} onAdded={() => { hancelExitSelect() }} />
|
||||||
<ListMusicMultiAdd ref={listMusicMultiAddRef} onAdded={hancelExitSelect} />
|
<ListMusicMultiAdd ref={listMusicMultiAddRef} onAdded={() => { hancelExitSelect() }} />
|
||||||
<MusicPositionModal ref={musicPositionModalRef}
|
<MusicPositionModal ref={musicPositionModalRef}
|
||||||
onUpdatePosition={(info, postion) => { handleUpdateMusicPosition(postion, info.listId, info.musicInfo, info.selectedList, hancelExitSelect) }} />
|
onUpdatePosition={(info, postion) => { handleUpdateMusicPosition(postion, info.listId, info.musicInfo, info.selectedList, hancelExitSelect) }} />
|
||||||
|
<MusicDownloadModal ref={musicDownloadModalRef}
|
||||||
|
onDownloadInfo={(info) => { }} />
|
||||||
<ListMenu
|
<ListMenu
|
||||||
ref={listMenuRef}
|
ref={listMenuRef}
|
||||||
onPlay={info => { handlePlay(info.listId, info.index) }}
|
onPlay={info => { handlePlay(info.listId, info.index) }}
|
||||||
@ -156,7 +160,7 @@ export default () => {
|
|||||||
onRemove={info => { hancelExitSelect(); handleRemove(info.listId, info.musicInfo, info.selectedList, hancelExitSelect) }}
|
onRemove={info => { hancelExitSelect(); handleRemove(info.listId, info.musicInfo, info.selectedList, hancelExitSelect) }}
|
||||||
onDislikeMusic={info => { void handleDislikeMusic(info.musicInfo) }}
|
onDislikeMusic={info => { void handleDislikeMusic(info.musicInfo) }}
|
||||||
onCopyName={info => { handleShare(info.musicInfo) }}
|
onCopyName={info => { handleShare(info.musicInfo) }}
|
||||||
onMusicSourceDetail={info => { void handleShowMusicSourceDetail(info.musicInfo) }}
|
onDownload={info => musicDownloadModalRef.current?.show(info.musicInfo)}
|
||||||
onAdd={handleAddMusic}
|
onAdd={handleAddMusic}
|
||||||
onMove={handleMoveMusic}
|
onMove={handleMoveMusic}
|
||||||
onEditMetadata={handleEditMetadata}
|
onEditMetadata={handleEditMetadata}
|
||||||
|
@ -2,14 +2,17 @@ import { removeListMusics, updateListMusicPosition, updateListMusics } from '@/c
|
|||||||
import { playList, playNext } from '@/core/player/player'
|
import { playList, playNext } from '@/core/player/player'
|
||||||
import { addTempPlayList } from '@/core/player/tempPlayList'
|
import { addTempPlayList } from '@/core/player/tempPlayList'
|
||||||
import settingState from '@/store/setting/state'
|
import settingState from '@/store/setting/state'
|
||||||
import { similar, sortInsert, toOldMusicInfo } from '@/utils'
|
import { similar, sortInsert } from '@/utils'
|
||||||
import { confirmDialog, openUrl, shareMusic, toast } from '@/utils/tools'
|
import { confirmDialog, requestStoragePermission, shareMusic, toast } from '@/utils/tools'
|
||||||
import { addDislikeInfo, hasDislike } from '@/core/dislikeList'
|
import { addDislikeInfo, hasDislike } from '@/core/dislikeList'
|
||||||
import playerState from '@/store/player/state'
|
import playerState from '@/store/player/state'
|
||||||
|
|
||||||
|
import RNFetchBlob from 'rn-fetch-blob'
|
||||||
import type { SelectInfo } from './ListMenu'
|
import type { SelectInfo } from './ListMenu'
|
||||||
import { type Metadata } from '@/components/MetadataEditModal'
|
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']) => {
|
export const handlePlay = (listId: SelectInfo['listId'], index: SelectInfo['index']) => {
|
||||||
void playList(listId, 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) => {
|
export const handleRemove = (listId: SelectInfo['listId'], musicInfo: SelectInfo['musicInfo'], selectedList: SelectInfo['selectedList'], onCancelSelect: () => void) => {
|
||||||
if (selectedList.length) {
|
if (selectedList.length) {
|
||||||
void confirmDialog({
|
void confirmDialog({
|
||||||
@ -90,16 +156,9 @@ export const searchListMusic = (list: LX.Music.MusicInfo[], text: string) => {
|
|||||||
return sortedList.map(item => item.data).reverse()
|
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({
|
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'),
|
cancelButtonText: global.i18n.t('cancel_button_text_2'),
|
||||||
confirmButtonText: global.i18n.t('confirm_button_text'),
|
confirmButtonText: global.i18n.t('confirm_button_text'),
|
||||||
bgClose: false,
|
bgClose: false,
|
||||||
|
@ -46,7 +46,8 @@ export default memo(() => {
|
|||||||
|
|
||||||
const styles = createStyle({
|
const styles = createStyle({
|
||||||
content: {
|
content: {
|
||||||
marginTop: 10,
|
marginTop: 0,
|
||||||
|
marginBottom: 15,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -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',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
@ -2,13 +2,14 @@ import { memo } from 'react'
|
|||||||
|
|
||||||
import Section from '../../components/Section'
|
import Section from '../../components/Section'
|
||||||
import IsSavePlayTime from './IsSavePlayTime'
|
import IsSavePlayTime from './IsSavePlayTime'
|
||||||
import PlayHighQuality from './PlayHighQuality'
|
import IsPlayHighQuality from './IsPlayHighQuality'
|
||||||
import IsHandleAudioFocus from './IsHandleAudioFocus'
|
import IsHandleAudioFocus from './IsHandleAudioFocus'
|
||||||
import IsEnableAudioOffload from './IsEnableAudioOffload'
|
import IsEnableAudioOffload from './IsEnableAudioOffload'
|
||||||
import IsAutoCleanPlayedList from './IsAutoCleanPlayedList'
|
import IsAutoCleanPlayedList from './IsAutoCleanPlayedList'
|
||||||
import IsShowNotificationImage from './IsShowNotificationImage'
|
import IsShowNotificationImage from './IsShowNotificationImage'
|
||||||
import IsShowLyricTranslation from './IsShowLyricTranslation'
|
import IsShowLyricTranslation from './IsShowLyricTranslation'
|
||||||
import IsShowLyricRoma from './IsShowLyricRoma'
|
import IsShowLyricRoma from './IsShowLyricRoma'
|
||||||
|
import SelectPlayQuality from './SelectPlayQuality'
|
||||||
import IsS2T from './IsS2T'
|
import IsS2T from './IsS2T'
|
||||||
import MaxCache from './MaxCache'
|
import MaxCache from './MaxCache'
|
||||||
import { useI18n } from '@/lang'
|
import { useI18n } from '@/lang'
|
||||||
@ -21,14 +22,15 @@ export default memo(() => {
|
|||||||
<Section title={t('setting_player')}>
|
<Section title={t('setting_player')}>
|
||||||
<IsSavePlayTime />
|
<IsSavePlayTime />
|
||||||
<IsAutoCleanPlayedList />
|
<IsAutoCleanPlayedList />
|
||||||
|
{/* <IsPlayHighQuality /> */}
|
||||||
<IsHandleAudioFocus />
|
<IsHandleAudioFocus />
|
||||||
<IsEnableAudioOffload />
|
<IsEnableAudioOffload />
|
||||||
<IsShowNotificationImage />
|
<IsShowNotificationImage />
|
||||||
<IsShowLyricTranslation />
|
<IsShowLyricTranslation />
|
||||||
<IsShowLyricRoma />
|
<IsShowLyricRoma />
|
||||||
<IsS2T />
|
<IsS2T />
|
||||||
|
<SelectPlayQuality />
|
||||||
<MaxCache />
|
<MaxCache />
|
||||||
<PlayHighQuality />
|
|
||||||
</Section>
|
</Section>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -21,7 +21,7 @@ export default memo(() => {
|
|||||||
const progress = useVersionDownloadProgressUpdated()
|
const progress = useVersionDownloadProgressUpdated()
|
||||||
const handleOpenVersionModal = () => {
|
const handleOpenVersionModal = () => {
|
||||||
// setVersionInfo({ showModal: true })
|
// setVersionInfo({ showModal: true })
|
||||||
showModal()
|
// showModal()
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
1
src/types/common.d.ts
vendored
1
src/types/common.d.ts
vendored
@ -4,6 +4,7 @@ declare namespace LX {
|
|||||||
type OnlineSource = 'kw' | 'kg' | 'tx' | 'wy' | 'mg'
|
type OnlineSource = 'kw' | 'kg' | 'tx' | 'wy' | 'mg'
|
||||||
type Source = OnlineSource | 'local'
|
type Source = OnlineSource | 'local'
|
||||||
type Quality = '128k' | '320k' | 'flac' | 'flac24bit' | '192k' | 'ape' | 'wav'
|
type Quality = '128k' | '320k' | 'flac' | 'flac24bit' | '192k' | 'ape' | 'wav'
|
||||||
|
type
|
||||||
type QualityList = Partial<Record<LX.Source, LX.Quality[]>>
|
type QualityList = Partial<Record<LX.Source, LX.Quality[]>>
|
||||||
|
|
||||||
type ShareType = 'system' | 'clipboard'
|
type ShareType = 'system' | 'clipboard'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user