-
扫码登录
+
{{ props.title || '扫码登录' }}
+
+
+
扫描成功
请在手机上确认登录
@@ -102,7 +102,7 @@ onUnmounted(() => {
-
使用 网易云音乐APP 扫码登录
+
diff --git a/web/src/components/login/QQMusicLogin.vue b/web/src/components/login/QQMusicLogin.vue
deleted file mode 100644
index 13d4410..0000000
--- a/web/src/components/login/QQMusicLogin.vue
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
-
从QQ音乐获取cookie并填写
-
-
-
-
-
-
diff --git a/web/src/main.ts b/web/src/main.ts
index 4d70257..ca5d9cb 100644
--- a/web/src/main.ts
+++ b/web/src/main.ts
@@ -2,14 +2,15 @@ import { createApp } from 'vue';
import { createPinia } from 'pinia';
import 'default-passive-events';
-import App from './App.vue';
-import router from './router';
-
import 'element-plus/theme-chalk/dark/css-vars.css';
import 'element-plus/theme-chalk/el-message-box.css';
import 'element-plus/theme-chalk/el-radio-group.css';
import 'element-plus/theme-chalk/el-radio.css';
import 'element-plus/theme-chalk/el-checkbox.css';
+
+import App from './App.vue';
+import router from './router';
+
import './style/main.css';
const app = createApp(App);
diff --git a/web/src/stores/play.ts b/web/src/stores/play.ts
index cc8a355..aafa3d5 100644
--- a/web/src/stores/play.ts
+++ b/web/src/stores/play.ts
@@ -244,6 +244,10 @@ export const usePlayStore = defineStore('play', {
const m = await api.musicDetail(music);
if (!m || !m.url) {
console.log('fail', music);
+ if (this.playStatus.playing) {
+ this.add([this.music]);
+ return;
+ }
const musicIndex = this.musicList.findIndex(
n => music && music.id == n.id && music.type == n.type
);
diff --git a/web/src/stores/setting.ts b/web/src/stores/setting.ts
index 012fd63..02f8e25 100644
--- a/web/src/stores/setting.ts
+++ b/web/src/stores/setting.ts
@@ -437,7 +437,7 @@ export const useSettingStore = defineStore('setting', {
const userInfo = userInfoCache[musicType as MusicType];
if (this.userInfo[musicType as MusicType]) {
this.userInfo[musicType as MusicType].cookie =
- userInfo?.cookie || {};
+ userInfo?.cookie || '';
this.setUserInfo(musicType as MusicType);
}
});
diff --git a/web/src/utils/api/api.ts b/web/src/utils/api/api.ts
index cc8af51..cded706 100644
--- a/web/src/utils/api/api.ts
+++ b/web/src/utils/api/api.ts
@@ -4,7 +4,8 @@ import {
Playlist,
RankingType,
Lyric,
- UserInfo
+ UserInfo,
+ LoginStatus
} from '../type';
import * as cloud from './cloud';
import * as qq from './qq';
@@ -65,7 +66,7 @@ export async function recommend(
export async function playlistDetail(
type: MusicType,
id: string,
- cookies?: Record
+ cookies?: Record | string
): Promise<{
total: number;
list: Music[];
@@ -149,7 +150,7 @@ export async function musicDetail(music: Music): Promise {
export async function qrCodeKey(type: MusicType): Promise<{
key: string;
- url?: string;
+ url: string;
} | null> {
const func = getFunction(type, 'qrCodeKey');
try {
@@ -160,25 +161,25 @@ export async function qrCodeKey(type: MusicType): Promise<{
return null;
}
-export async function qrCodeState(
+export async function loginStatus(
type: MusicType,
key: string
): Promise<{
- state: number | string;
- cookie: string;
-} | null> {
- const func = getFunction(type, 'qrCodeState');
+ status: LoginStatus;
+ user?: UserInfo;
+}> {
+ const func = getFunction(type, 'loginStatus');
try {
if (func) return await func(key);
} catch (e) {
console.error(e);
}
- return null;
+ return { status: 'fail' };
}
export async function userInfo(
type: MusicType,
- cookie: Record
+ cookie: Record | string
): Promise {
const func = getFunction(type, 'userInfo');
try {
@@ -191,7 +192,7 @@ export async function userInfo(
export async function yours(
type: MusicType,
- cookie: Record,
+ cookie: Record | string,
offset: number
): Promise<{
total: number;
diff --git a/web/src/utils/api/cloud.ts b/web/src/utils/api/cloud.ts
index 6630171..e6d6c32 100644
--- a/web/src/utils/api/cloud.ts
+++ b/web/src/utils/api/cloud.ts
@@ -1,10 +1,26 @@
import * as CryptoJS from 'crypto-js';
-import { Music, MusicType, Playlist, RankingType, UserInfo } from '../type';
+import {
+ Music,
+ MusicType,
+ Playlist,
+ RankingType,
+ UserInfo,
+ LoginStatus
+} from '../type';
import { httpProxy } from '../http';
-import { millisecond2Duration } from '../utils';
+import { millisecond2Duration, parseCookie } from '../utils';
import RankingHotImage from '../../assets/images/ranking-hot.jpg';
import RankingNewImage from '../../assets/images/ranking-new.jpg';
import RankingSoarImage from '../../assets/images/ranking-soar.jpg';
+const QRCode = () => import('qrcode');
+
+var qrcodeGenerate: (text: string) => Promise = async (
+ text: string
+) => {
+ const qrcode = await QRCode();
+ qrcodeGenerate = qrcode.toDataURL as any;
+ return qrcodeGenerate(text);
+};
function aesEncrypt(plain: string, key: string): string {
var iv = '0102030405060708';
@@ -509,11 +525,7 @@ export async function musicById(id: string): Promise {
method: 'POST',
data: paramData,
headers: {
- // Cookie: 'os=ios;MUSIC_U=',
- // Referer: 'https://music.163.com',
ContentType: 'application/x-www-form-urlencoded'
- // UserAgent:
- // 'Mozilla/5.0 (iPad; CPU OS 11_0 like Mac OS X) AppleWebKit/604.1.34 (KHTML, like Gecko) Version/11.0 Mobile/15A5341f Safari/604.1',
}
});
const ret = await res.json();
@@ -559,8 +571,8 @@ export async function parseLink(link: string) {
export async function qrCodeKey(): Promise<{
key: string;
- url?: string;
-}> {
+ url: string;
+} | null> {
var url = 'https://music.163.com/weapi/login/qrcode/unikey?csrf_token=';
const data = {
type: 1,
@@ -585,14 +597,20 @@ export async function qrCodeKey(): Promise<{
}
});
const ret = await res.json();
- return {
- key: (ret && ret.unikey) || ''
- };
+ if (ret && ret.unikey) {
+ return {
+ key: ret.unikey,
+ url: await qrcodeGenerate(
+ 'http://music.163.com/login?codekey=' + ret.unikey
+ )
+ };
+ }
+ return null;
}
-export async function qrCodeState(key: string): Promise<{
- state: number | string;
- cookie: string;
+export async function loginStatus(key: string): Promise<{
+ status: LoginStatus;
+ user?: UserInfo;
}> {
var url = 'https://music.163.com/weapi/login/qrcode/client/login?csrf_token=';
const data = {
@@ -620,10 +638,28 @@ export async function qrCodeState(key: string): Promise<{
}
});
const ret = await res.json();
- return {
- state: (ret && ret.code) || 0,
- cookie: res.headers.get('Set-Cookie-Renamed') || ''
- };
+ switch (ret && ret.code) {
+ case 803:
+ const cookies = parseCookie(res.headers.get('Set-Cookie-Renamed') || '');
+ const user = await userInfo(cookies);
+ if (user && user.id) {
+ user.cookie = {
+ __csrf: cookies['__csrf'] || '',
+ MUSIC_U: cookies['MUSIC_U'] || '',
+ uid: user.id.toString()
+ };
+ return {
+ status: 'success',
+ user
+ };
+ }
+ break;
+ case 801:
+ return { status: 'waiting' };
+ case 802:
+ return { status: 'authorizing' };
+ }
+ return { status: 'fail' };
}
export async function userInfo(
diff --git a/web/src/utils/api/migu.ts b/web/src/utils/api/migu.ts
index b5de8d7..c979923 100644
--- a/web/src/utils/api/migu.ts
+++ b/web/src/utils/api/migu.ts
@@ -1,9 +1,17 @@
import { httpProxy, parseHttpProxyAddress } from '../http';
-import { Music, MusicType, Playlist, RankingType, UserInfo } from '../type';
+import {
+ LoginStatus,
+ Music,
+ MusicType,
+ Playlist,
+ RankingType,
+ UserInfo
+} from '../type';
import {
duration2Millisecond,
durationTrim,
formatCookies,
+ parseCookie,
second2Duration
} from '../utils';
import RankingHotImage from '../../assets/images/ranking-hot.jpg';
@@ -54,9 +62,7 @@ export async function search(keywords: string, offset: number) {
};
}
-export async function daily(
- cookies: Record
-): Promise {
+export async function daily(cookies: string): Promise {
const user = await userInfo(cookies);
return {
id: 'daily',
@@ -67,7 +73,7 @@ export async function daily(
}
export async function yours(
- cookies: Record,
+ cookies: string,
_offset: number
): Promise<{
total: number;
@@ -162,7 +168,7 @@ export async function playlistInfo(id: string) {
};
}
-export async function dailyPlayList(cookies: Record) {
+export async function dailyPlayList(cookies: string) {
const headers: Record = {
Referer: 'https://music.migu.cn/v3',
Cookie: formatCookies(cookies)
@@ -220,10 +226,7 @@ export async function dailyPlayList(cookies: Record) {
};
}
-export async function digitalDetail(
- id: string,
- _cookies?: Record
-) {
+export async function digitalDetail(id: string, _cookies?: string) {
var url =
'https://m.music.migu.cn/migumusic/h5/digitalAlbum/info?pageSize=500&digitalAlbumId=' +
id;
@@ -273,10 +276,7 @@ export async function digitalDetail(
};
}
-export async function playlistDetail(
- id: string,
- cookies?: Record
-) {
+export async function playlistDetail(id: string, cookies?: string) {
if (id == 'daily') {
return dailyPlayList(cookies!);
}
@@ -610,9 +610,9 @@ export async function qrCodeKey(): Promise<{
};
}
-export async function qrCodeState(key: string): Promise<{
- state: number | string;
- cookie: string;
+export async function loginStatus(key: string): Promise<{
+ status: LoginStatus;
+ user?: UserInfo;
}> {
let res = await httpProxy({
url: 'https://passport.migu.cn/api/qrcWeb/qrcquery',
@@ -624,8 +624,8 @@ export async function qrCodeState(key: string): Promise<{
}
});
let ret = await res.json();
- if (ret.status == 2000 && ret.result && ret.result.token) {
- const lastCookie = res.headers.get('Set-Cookie-Renamed') || '';
+ if (ret && ret.status == 2000 && ret.result && ret.result.token) {
+ const lastCookie = parseCookie(res.headers.get('Set-Cookie-Renamed') || '');
res = await httpProxy({
url: ret.result.redirectURL + '?token=' + ret.result.token,
method: 'GET',
@@ -635,21 +635,28 @@ export async function qrCodeState(key: string): Promise<{
}
});
if (res.ok) {
- return {
- state: 2000,
- cookie: lastCookie + ';' + res.headers.get('Set-Cookie-Renamed') || ''
- };
+ const cookie = formatCookies({
+ ...lastCookie,
+ ...parseCookie(res.headers.get('Set-Cookie-Renamed') || '')
+ });
+ const user = await userInfo(cookie);
+ if (user && user.id) {
+ user.cookie = cookie;
+ return {
+ status: 'success',
+ user
+ };
+ }
}
+ } else if (ret && ret.status == 4074) {
+ return { status: 'waiting' };
}
return {
- state: ret.status,
- cookie: ''
+ status: 'fail'
};
}
-export async function userInfo(
- cookies: Record
-): Promise {
+export async function userInfo(cookies: string): Promise {
var res = await httpProxy({
url: 'https://music.migu.cn/v3/api/user/getUserInfo',
method: 'GET',
diff --git a/web/src/utils/api/qq.ts b/web/src/utils/api/qq.ts
index 5b7c133..fc70733 100644
--- a/web/src/utils/api/qq.ts
+++ b/web/src/utils/api/qq.ts
@@ -1,5 +1,12 @@
import { httpProxy, parseHttpProxyAddress } from '../http';
-import { Music, Playlist, MusicType, RankingType, UserInfo } from '../type';
+import {
+ Music,
+ Playlist,
+ MusicType,
+ RankingType,
+ UserInfo,
+ LoginStatus
+} from '../type';
import { formatCookies, generateGuid, millisecond2Duration } from '../utils';
import RankingHotImage from '../../assets/images/ranking-hot.jpg';
import RankingNewImage from '../../assets/images/ranking-new.jpg';
@@ -128,9 +135,7 @@ function removeExtJson(jsonStr: string) {
return jsonStr;
}
-export async function daily(
- cookies: Record
-): Promise {
+export async function daily(cookies: string): Promise {
const cookie = formatCookies(cookies);
var url = 'https://c.y.qq.com/node/musicmac/v6/index.html';
var res = await httpProxy({
@@ -156,7 +161,7 @@ export async function daily(
return null;
}
-export async function yours(cookies: Record): Promise<{
+export async function yours(cookies: string): Promise<{
total: number;
list: Playlist[];
}> {
@@ -707,12 +712,25 @@ export async function parseLink(link: string) {
return null;
}
-export async function userInfo(
- cookies: Record
-): Promise {
- const cookie = Object.keys(cookies)
- .map(m => `${m}=${cookies[m]}`)
- .join('; ');
+export async function loginStatus(cookie: string): Promise<{
+ status: LoginStatus;
+ user?: UserInfo;
+}> {
+ const user = await userInfo(cookie);
+ if (user && user.id) {
+ user.cookie = cookie;
+ return {
+ status: 'success',
+ user
+ };
+ }
+ return {
+ status: 'fail'
+ };
+}
+
+export async function userInfo(cookies: string): Promise {
+ const cookie = formatCookies(cookies);
var res = await httpProxy({
url: 'https://c.y.qq.com/rsc/fcgi-bin/fcg_get_profile_homepage.fcg?cid=205360838&reqfrom=1',
method: 'GET',
diff --git a/web/src/utils/type.ts b/web/src/utils/type.ts
index 55c101f..d7d64b7 100644
--- a/web/src/utils/type.ts
+++ b/web/src/utils/type.ts
@@ -117,9 +117,11 @@ export interface ShortcutKey {
status?: string;
}
+export type LoginStatus = 'success' | 'fail' | 'waiting' | 'authorizing';
+
export interface UserInfo {
id: string;
name?: string;
image?: string;
- cookie?: Record;
+ cookie?: Record | string;
}
diff --git a/web/src/utils/utils.ts b/web/src/utils/utils.ts
index 4c17c69..190d138 100644
--- a/web/src/utils/utils.ts
+++ b/web/src/utils/utils.ts
@@ -149,8 +149,11 @@ export function parseCookie(cookie: string): Record {
return cookieObj;
}
-export function formatCookies(cookies: Record): string {
+export function formatCookies(
+ cookies: Record | string
+): string {
if (!cookies) return '';
+ if (typeof cookies === 'string') return cookies;
return Object.keys(cookies)
.map(m => `${m}=${cookies[m]}`)
.join('; ');
diff --git a/web/src/views/setting.vue b/web/src/views/setting.vue
index 0a12823..dacf89a 100644
--- a/web/src/views/setting.vue
+++ b/web/src/views/setting.vue
@@ -10,9 +10,7 @@ import MiguMusicImage from '../assets/images/migu-music.webp';
import { useSettingStore } from '../stores/setting';
import { CloseType, MusicType, ShortcutType } from '../utils/type';
import { ElMessageBox } from 'element-plus';
-import CloudMusicLogin from '../components/login/CloudMusicLogin.vue';
-import MiguMusicLogin from '../components/login/MiguMusicLogin.vue';
-import QQMusicLogin from '../components/login/QQMusicLogin.vue';
+import Login from '../components/login/Login.vue';
const { currentRoute, replace } = useRouter();
const subItems = [
@@ -109,28 +107,6 @@ function setItemsIdTitle() {
const onSettingScroll = useDebounceFn(checkSettingScroll, 300);
-// function elementInView(
-// ele: Element | null,
-// tableTop?: number,
-// onlyTop?: boolean
-// ) {
-// if (!ele || !tableEle.value) return false;
-// if (tableTop == undefined) {
-// tableTop =
-// tableEle.value.parentElement?.parentElement?.getBoundingClientRect()
-// ?.top || 0;
-// const { top, bottom } = ele.getBoundingClientRect();
-// if (onlyTop) return top <= tableTop && bottom > tableTop;
-// else {
-// const pageBottom =
-// tableEle.value.parentElement?.parentElement?.getBoundingClientRect()
-// ?.bottom || 0;
-// return top >= tableTop && bottom <= pageBottom;
-// }
-// }
-// return false;
-// }
-
function checkSettingScroll() {
if (scrollByRouter) {
scrollByRouter = false;
@@ -159,20 +135,20 @@ function loginSuccess() {
function login(type: MusicType) {
let title = '网易云';
- let components = null;
+ let text = '';
switch (type) {
case MusicType.QQMusic:
title = 'QQ';
- components = QQMusicLogin;
+ text = '从QQ音乐获取cookie并填写';
break;
case MusicType.MiguMusic:
title = '咪咕';
- components = MiguMusicLogin;
+ text = '打开 咪咕音乐app
点击顶部菜单图标,然后找到扫一扫并点击';
break;
default:
title = '网易云';
- components = CloudMusicLogin;
+ text = '使用 网易云音乐APP 扫码登录';
break;
}
ElMessageBox({
@@ -181,7 +157,10 @@ function login(type: MusicType) {
showCancelButton: false,
showConfirmButton: false,
closeOnClickModal: false,
- message: h(components, {
+ message: h(Login, {
+ type,
+ qrcode: type != MusicType.QQMusic,
+ text,
onLogon: loginSuccess
})
}).catch(() => {});