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 = {
|
||||
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',
|
||||
|
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@ -10,5 +10,6 @@
|
||||
"google-cn",
|
||||
"google"
|
||||
],
|
||||
"i18n-ally.sortKeys": true
|
||||
}
|
||||
"i18n-ally.sortKeys": true,
|
||||
"java.configuration.updateBuildConfiguration": "disabled"
|
||||
}
|
@ -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>
|
||||
|
@ -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')
|
||||
|
@ -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
8035
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",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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} /> */}
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 🚀"
|
||||
}
|
||||
}
|
@ -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": "🚀 程序更新 🚀"
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
@ -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
|
||||
}
|
||||
|
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 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}
|
||||
|
@ -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,
|
||||
|
@ -46,7 +46,8 @@ export default memo(() => {
|
||||
|
||||
const styles = createStyle({
|
||||
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 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>
|
||||
)
|
||||
})
|
||||
|
@ -21,7 +21,7 @@ export default memo(() => {
|
||||
const progress = useVersionDownloadProgressUpdated()
|
||||
const handleOpenVersionModal = () => {
|
||||
// setVersionInfo({ showModal: true })
|
||||
showModal()
|
||||
// showModal()
|
||||
}
|
||||
|
||||
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 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'
|
||||
|
Loading…
x
Reference in New Issue
Block a user