mirror of
https://github.com/algerkong/AlgerMusicPlayer.git
synced 2026-04-03 14:20:50 +08:00
feat: mac 添加权限
This commit is contained in:
@@ -16,5 +16,7 @@
|
||||
<true/>
|
||||
<key>com.apple.security.files.downloads.read-write</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.microphone</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -24,6 +24,7 @@ mac:
|
||||
- NSCameraUsageDescription: Application requests access to the device's camera.
|
||||
- NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder.
|
||||
- NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder.
|
||||
- NSMicrophoneUsageDescription: Application requests access to the microphone for audio visualization.
|
||||
notarize: false
|
||||
dmg:
|
||||
artifactName: ${name}-${version}.${ext}
|
||||
|
||||
@@ -52,6 +52,31 @@ export default {
|
||||
copyFailed: 'Copy failed',
|
||||
backgroundDownload: 'Background Download'
|
||||
},
|
||||
disclaimer: {
|
||||
title: 'Terms of Use',
|
||||
warning:
|
||||
'This application is a development test version. Functions are not yet perfect, and there may be many problems and bugs. It is for learning and exchange only.',
|
||||
item1:
|
||||
'This application is for personal learning, research and technical exchange only. Please do not use it for any commercial purposes.',
|
||||
item2:
|
||||
'Please delete it within 24 hours after downloading. If you need to use it for a long time, please support the genuine music service.',
|
||||
item3:
|
||||
'By using this application, you understand and assume the relevant risks. The developer is not responsible for any loss.',
|
||||
agree: 'I have read and agree',
|
||||
disagree: 'Disagree and Exit'
|
||||
},
|
||||
donate: {
|
||||
title: 'Support Developer',
|
||||
subtitle: 'Your support is my motivation',
|
||||
tip: 'Donation is completely voluntary. All functions can be used normally without donation. Thank you for your understanding and support!',
|
||||
wechat: 'WeChat',
|
||||
alipay: 'Alipay',
|
||||
wechatQR: 'WeChat QR Code',
|
||||
alipayQR: 'Alipay QR Code',
|
||||
scanTip: 'Please use your phone to scan the QR code above to donate',
|
||||
enterApp: 'Enter App',
|
||||
noForce: 'No forced donation, click to enter'
|
||||
},
|
||||
coffee: {
|
||||
title: 'Buy me a coffee',
|
||||
alipay: 'Alipay',
|
||||
|
||||
@@ -52,6 +52,31 @@ export default {
|
||||
copyFailed: 'コピーに失敗しました',
|
||||
backgroundDownload: 'バックグラウンドダウンロード'
|
||||
},
|
||||
disclaimer: {
|
||||
title: '使用上の注意',
|
||||
warning:
|
||||
'このアプリは開発テスト版であり、機能が不完全で、多くの問題やバグが存在する可能性があります。学習と交流のみを目的としています。',
|
||||
item1:
|
||||
'このアプリは個人の学習、研究、技術交流のみを目的としています。商業目的で使用しないでください。',
|
||||
item2:
|
||||
'ダウンロード後24時間以内に削除してください。長期使用を希望される場合は、正規の音楽サービスをサポートしてください。',
|
||||
item3:
|
||||
'このアプリを使用することで、関連するリスクを理解し、負担するものとします。開発者は一切の損失に対して責任を負いません。',
|
||||
agree: '以上の内容を読み、同意します',
|
||||
disagree: '同意せずに終了'
|
||||
},
|
||||
donate: {
|
||||
title: '開発者を支援',
|
||||
subtitle: '皆様のサポートが私の原動力です',
|
||||
tip: '寄付は完全に任意です。寄付しなくてもすべての機能を通常通り使用できます。ご理解とご支援に感謝します!',
|
||||
wechat: 'WeChat',
|
||||
alipay: 'Alipay',
|
||||
wechatQR: 'WeChat 受取コード',
|
||||
alipayQR: 'Alipay 受取コード',
|
||||
scanTip: 'スマートフォンのアプリで上記のQRコードをスキャンして寄付してください',
|
||||
enterApp: 'アプリに入る',
|
||||
noForce: '寄付は強制ではありません。クリックして入れます'
|
||||
},
|
||||
coffee: {
|
||||
title: 'コーヒーをおごる',
|
||||
alipay: 'Alipay',
|
||||
|
||||
@@ -51,6 +51,31 @@ export default {
|
||||
copyFailed: '복사 실패',
|
||||
backgroundDownload: '백그라운드 다운로드'
|
||||
},
|
||||
disclaimer: {
|
||||
title: '이용 안내',
|
||||
warning:
|
||||
'본 앱은 개발 테스트 버전으로 기능이 아직 미흡하며, 다수의 문제와 버그가 존재할 수 있습니다. 학습 및 교류 목적으로만 사용하십시오.',
|
||||
item1:
|
||||
'본 앱은 개인의 학습, 연구 및 기술 교류 목적으로만 사용되며, 상업적 용도로 사용하지 마십시오.',
|
||||
item2:
|
||||
'다운로드 후 24시간 이내에 삭제해 주십시오. 장기 사용을 원하시면 정품 음악 서비스를 이용해 주십시오.',
|
||||
item3:
|
||||
'본 앱을 사용함으로써 관련 위험을 이해하고 감수하는 것으로 간주합니다. 개발자는 어떠한 손실에 대해서도 책임을 지지 않습니다.',
|
||||
agree: '숙지하였으며 이에 동의합니다',
|
||||
disagree: '동의하지 않음 및 정지'
|
||||
},
|
||||
donate: {
|
||||
title: '개발자 지원',
|
||||
subtitle: '여러분의 지원이 저의 원동력입니다',
|
||||
tip: '후원은 완전히 자율적입니다. 후원하지 않더라도 모든 기능을 정상적으로 사용할 수 있습니다. 이해와 지원에 감사드립니다!',
|
||||
wechat: 'WeChat',
|
||||
alipay: 'Alipay',
|
||||
wechatQR: 'WeChat 결제 코드',
|
||||
alipayQR: 'Alipay 결제 코드',
|
||||
scanTip: '휴대전화로 위 QR 코드를 스캔하여 후원해 주세요',
|
||||
enterApp: '앱 시작하기',
|
||||
noForce: '후원은 강제가 아닙니다. 클릭하여 시작할 수 있습니다'
|
||||
},
|
||||
coffee: {
|
||||
title: '커피 한 잔 사주세요',
|
||||
alipay: '알리페이',
|
||||
|
||||
@@ -50,6 +50,27 @@ export default {
|
||||
copyFailed: '复制失败',
|
||||
backgroundDownload: '后台下载'
|
||||
},
|
||||
disclaimer: {
|
||||
title: '使用须知',
|
||||
warning: '本应用为开发测试版本,功能尚不完善,可能存在较多问题和 Bug,仅供学习交流使用。',
|
||||
item1: '本应用仅供个人学习、研究和技术交流使用,请勿用于任何商业用途。',
|
||||
item2: '请在下载后 24 小时内删除,如需长期使用请支持正版音乐服务。',
|
||||
item3: '使用本应用即表示您理解并承担相关风险,开发者不对任何损失负责。',
|
||||
agree: '我已阅读并同意',
|
||||
disagree: '不同意并退出'
|
||||
},
|
||||
donate: {
|
||||
title: '支持开发者',
|
||||
subtitle: '您的支持是我前进的动力',
|
||||
tip: '捐赠完全自愿,不捐赠也可以正常使用所有功能,感谢您的理解与支持!',
|
||||
wechat: '微信',
|
||||
alipay: '支付宝',
|
||||
wechatQR: '微信收款码',
|
||||
alipayQR: '支付宝收款码',
|
||||
scanTip: '请使用手机扫描上方二维码进行捐赠',
|
||||
enterApp: '进入应用',
|
||||
noForce: '不强制捐赠,点击即可进入'
|
||||
},
|
||||
coffee: {
|
||||
title: '请我喝咖啡',
|
||||
alipay: '支付宝',
|
||||
|
||||
@@ -50,6 +50,27 @@ export default {
|
||||
copyFailed: '複製失敗',
|
||||
backgroundDownload: '背景下載'
|
||||
},
|
||||
disclaimer: {
|
||||
title: '使用說明',
|
||||
warning: '本程式為開發測試版本,功能尚未完善,可能存在諸多問題及臭蟲,僅供學習交流使用。',
|
||||
item1: '本程式僅供個人學習、研究及技術交流之目的,不得用於任何商業用途。',
|
||||
item2: '請在下載後 24 小時內刪除,若對您有所幫助,請支持正版音樂。',
|
||||
item3: '使用本程式即代表您已了解並同意相關風險,開發者對任何損失概不負責。',
|
||||
agree: '我已了解並同意',
|
||||
disagree: '不同意並退出'
|
||||
},
|
||||
donate: {
|
||||
title: '支援開發者',
|
||||
subtitle: '您的支援是我持續更新的動力',
|
||||
tip: '捐贈完全採自願原則。即使不捐贈,您依然可以正常使用所有功能。感謝您的理解與支援!',
|
||||
wechat: '微信支付',
|
||||
alipay: '支付寶',
|
||||
wechatQR: '微信收款碼',
|
||||
alipayQR: '支付寶收款碼',
|
||||
scanTip: '請使用手機 App 掃描 QR Code 進行捐贈',
|
||||
enterApp: '進入程式',
|
||||
noForce: '捐贈並非強制,您可以點擊按鈕直接進入'
|
||||
},
|
||||
coffee: {
|
||||
title: '請我喝杯咖啡',
|
||||
alipay: '支付寶',
|
||||
|
||||
@@ -143,6 +143,12 @@ export function initializeWindowManager() {
|
||||
}
|
||||
});
|
||||
|
||||
// 强制退出应用(用于免责声明拒绝等场景)
|
||||
ipcMain.on('quit-app', () => {
|
||||
setAppQuitting(true);
|
||||
app.quit();
|
||||
});
|
||||
|
||||
ipcMain.on('mini-tray', (event) => {
|
||||
const win = BrowserWindow.fromWebContents(event.sender);
|
||||
if (win) {
|
||||
|
||||
@@ -23,14 +23,57 @@ ipcMain.handle('unblock-music', async (_event, id, songData, enabledSources) =>
|
||||
}
|
||||
});
|
||||
|
||||
async function startMusicApi(): Promise<void> {
|
||||
console.log('MUSIC API STARTED');
|
||||
|
||||
const port = (store.get('set') as any).musicApiPort || 30488;
|
||||
|
||||
await server.serveNcmApi({
|
||||
port
|
||||
/**
|
||||
* 检查端口是否可用
|
||||
*/
|
||||
function checkPortAvailable(port: number): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
const net = require('net');
|
||||
const tester = net
|
||||
.createServer()
|
||||
.once('error', () => {
|
||||
resolve(false);
|
||||
})
|
||||
.once('listening', () => {
|
||||
tester.close(() => resolve(true));
|
||||
})
|
||||
.listen(port);
|
||||
});
|
||||
}
|
||||
|
||||
async function startMusicApi(): Promise<void> {
|
||||
console.log('MUSIC API STARTING...');
|
||||
|
||||
const settings = store.get('set') as any;
|
||||
let port = settings?.musicApiPort || 30488;
|
||||
const maxRetries = 10;
|
||||
|
||||
// 检查端口是否可用,如果不可用则尝试下一个端口
|
||||
for (let i = 0; i < maxRetries; i++) {
|
||||
const isAvailable = await checkPortAvailable(port);
|
||||
if (isAvailable) {
|
||||
break;
|
||||
}
|
||||
console.log(`端口 ${port} 被占用,尝试切换到端口 ${port + 1}`);
|
||||
port++;
|
||||
}
|
||||
|
||||
// 如果端口发生变化,保存新端口到配置
|
||||
const originalPort = settings?.musicApiPort || 30488;
|
||||
if (port !== originalPort) {
|
||||
console.log(`端口从 ${originalPort} 切换到 ${port}`);
|
||||
store.set('set', { ...settings, musicApiPort: port });
|
||||
}
|
||||
|
||||
try {
|
||||
await server.serveNcmApi({
|
||||
port
|
||||
});
|
||||
console.log(`MUSIC API STARTED on port ${port}`);
|
||||
} catch (error) {
|
||||
console.error(`MUSIC API 启动失败:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export { startMusicApi };
|
||||
|
||||
7
src/preload/index.d.ts
vendored
7
src/preload/index.d.ts
vendored
@@ -4,6 +4,7 @@ interface API {
|
||||
minimize: () => void;
|
||||
maximize: () => void;
|
||||
close: () => void;
|
||||
quitApp: () => void;
|
||||
dragStart: (data: any) => void;
|
||||
miniTray: () => void;
|
||||
miniWindow: () => void;
|
||||
@@ -25,11 +26,7 @@ interface API {
|
||||
importLxMusicScript: () => Promise<{ name: string; content: string } | null>;
|
||||
invoke: (channel: string, ...args: any[]) => Promise<any>;
|
||||
getSearchSuggestions: (keyword: string) => Promise<any>;
|
||||
lxMusicHttpRequest: (request: {
|
||||
url: string;
|
||||
options: any;
|
||||
requestId: string;
|
||||
}) => Promise<any>;
|
||||
lxMusicHttpRequest: (request: { url: string; options: any; requestId: string }) => Promise<any>;
|
||||
lxMusicHttpCancel: (requestId: string) => Promise<void>;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ const api = {
|
||||
minimize: () => ipcRenderer.send('minimize-window'),
|
||||
maximize: () => ipcRenderer.send('maximize-window'),
|
||||
close: () => ipcRenderer.send('close-window'),
|
||||
quitApp: () => ipcRenderer.send('quit-app'),
|
||||
dragStart: (data) => ipcRenderer.send('drag-start', data),
|
||||
miniTray: () => ipcRenderer.send('mini-tray'),
|
||||
miniWindow: () => ipcRenderer.send('mini-window'),
|
||||
@@ -61,11 +62,8 @@ const api = {
|
||||
getSearchSuggestions: (keyword: string) => ipcRenderer.invoke('get-search-suggestions', keyword),
|
||||
|
||||
// 落雪音乐 HTTP 请求(绕过 CORS)
|
||||
lxMusicHttpRequest: (request: {
|
||||
url: string;
|
||||
options: any;
|
||||
requestId: string;
|
||||
}) => ipcRenderer.invoke('lx-music-http-request', request),
|
||||
lxMusicHttpRequest: (request: { url: string; options: any; requestId: string }) =>
|
||||
ipcRenderer.invoke('lx-music-http-request', request),
|
||||
|
||||
lxMusicHttpCancel: (requestId: string) => ipcRenderer.invoke('lx-music-http-cancel', requestId)
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<n-message-provider>
|
||||
<router-view></router-view>
|
||||
<traffic-warning-drawer v-if="!isElectron"></traffic-warning-drawer>
|
||||
<disclaimer-modal></disclaimer-modal>
|
||||
</n-message-provider>
|
||||
</n-dialog-provider>
|
||||
</n-config-provider>
|
||||
@@ -18,6 +19,7 @@ import { computed, nextTick, onMounted, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import DisclaimerModal from '@/components/common/DisclaimerModal.vue';
|
||||
import TrafficWarningDrawer from '@/components/TrafficWarningDrawer.vue';
|
||||
import { usePlayerStore } from '@/store/modules/player';
|
||||
import { useSettingsStore } from '@/store/modules/settings';
|
||||
|
||||
@@ -237,6 +237,7 @@ import { useI18n } from 'vue-i18n';
|
||||
// 导入收款码图片
|
||||
import alipayQRCode from '@/assets/alipay.png';
|
||||
import wechatQRCode from '@/assets/wechat.png';
|
||||
import { isElectron, isLyricWindow } from '@/utils';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
@@ -250,20 +251,8 @@ const qrcodeType = ref<'wechat' | 'alipay'>('wechat');
|
||||
const isTransitioning = ref(false); // 防止用户点击过快
|
||||
|
||||
// 检查是否需要显示免责声明
|
||||
const shouldShowDisclaimer = (): boolean => {
|
||||
const agreedTime = localStorage.getItem(DISCLAIMER_AGREED_KEY);
|
||||
|
||||
// 从未同意过
|
||||
if (!agreedTime) return true;
|
||||
|
||||
const savedTime = parseInt(agreedTime, 10);
|
||||
const now = Date.now();
|
||||
|
||||
// 随机 3-10 天后再次显示
|
||||
const randomDays = Math.floor(Math.random() * 8) + 3; // 3-10 天
|
||||
const intervalMs = randomDays * 24 * 60 * 60 * 1000;
|
||||
|
||||
return now - savedTime >= intervalMs;
|
||||
const shouldShowDisclaimer = () => {
|
||||
return !localStorage.getItem(DISCLAIMER_AGREED_KEY);
|
||||
};
|
||||
|
||||
// 处理同意
|
||||
@@ -278,13 +267,18 @@ const handleAgree = () => {
|
||||
}, 300);
|
||||
};
|
||||
|
||||
// 处理不同意 - 关闭窗口
|
||||
// 处理不同意 - 退出应用
|
||||
const handleDisagree = () => {
|
||||
if (isTransitioning.value) return;
|
||||
isTransitioning.value = true;
|
||||
|
||||
// Web 环境下尝试关闭窗口
|
||||
window.close();
|
||||
if (isElectron) {
|
||||
// Electron 环境下强制退出应用
|
||||
window.api?.quitApp?.();
|
||||
} else {
|
||||
// Web 环境下尝试关闭窗口
|
||||
window.close();
|
||||
}
|
||||
isTransitioning.value = false;
|
||||
};
|
||||
|
||||
@@ -316,6 +310,9 @@ const handleEnterApp = () => {
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
// 歌词窗口不显示免责声明
|
||||
if (isLyricWindow.value) return;
|
||||
|
||||
// 检查是否需要显示免责声明
|
||||
if (shouldShowDisclaimer()) {
|
||||
showDisclaimer.value = true;
|
||||
|
||||
Reference in New Issue
Block a user