首页  编辑  

无限制洛雪高清音源

Tags: /计算机文档/软件应用技巧/   Date Created:
洛雪高清,无限制,免费,免注册音源,基于 无损音乐下载 开发。点击下载 %E6%97%A0%E6%8D%9F%E9%9F%B3%E6%BA%902.js

/*!
 * @name 高品质无损音乐音源
 * @description 基于flac.music.hi.cn API的音源服务
 * @version 1.0
 * @author Kingron
 * @homepage https://flac.music.hi.cn/
*/

const API_BASE_URL = "https://flac.music.hi.cn"
const { EVENT_NAMES, request, on, send, utils, env, version } = globalThis.lx

// 全局cookie存储
let globalCookie = 'sl-session=cfvqX25YrmjOIuvFqDrbpQ==; sl-challenge-server=cloud; sl_jwt_session=cpF4eqtnrWghBeqsta5jqA==; sl_jwt_sign='
let cookieUpdateTimer = null

function log(...args) {
	console.log(...args);
	if (env !== 'mobile') return;

	// send(EVENT_NAMES.updateAlert, { 
      // log: args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : String(arg)).join(', '),
      // updateUrl: '' 
    // })
}

// 提取的f函数
function calculateArray(input) {
  let t = 1;
  const sum = input.reduce((acc, val) => acc + val, 0);
  let cycles = (6 + input.length + sum) % 6 + 6;
  
  while (cycles--) t *= 6;
  
  if (t < 6666) t *= input.length;
  if (t > 0x3f940aa) t = Math.floor(t / input.length);
  
  for (let i = 0; i < input.length; i++) {
    t += Math.pow(input[i], 3);
    t ^= i;
    t ^= input[i] + i;
  }
  
  const result = [];
  while (t > 0) {
    result.unshift(t & 63);
    t >>= 6;
  }
  
  return result;
}

// 自定义request函数,允许468状态码
function customRequest(url, options) {
  return new Promise((resolve, reject) => {
    request(url, options, (err, resp) => {
      if (err) return reject(err);
      // 允许200-299和468状态码
      if ((resp.statusCode >= 200 && resp.statusCode < 300) || resp.statusCode === 468) {
        resolve(resp);
      } else {
        reject(new Error(`请求失败,状态码: ${resp.statusCode}, 响应: ${JSON.stringify(resp)}`));
      }
    });
  });
}

// 生成cookie的函数
async function generateCookie() {
  try {
    // 第一步:获取初始页面并提取sl-session和key
	log("第一步获取初始页面...");
    const response1 = await customRequest('https://flac.music.hi.cn/', {
      method: 'GET',
      headers: {
        'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
        'accept-language': 'zh-CN,zh;q=0.9',
      }
    });
    log('获取初始页面响应状态码:', response1.statusCode);
    
    // 提取sl-session
    let cookies = response1.headers['set-cookie'];
	cookies = Array.isArray(cookies) ? cookies : [cookies]
    let slSession = '';
    if (cookies) {
		for (const cookie of cookies) {
			const match = cookie.match(/sl-session=([^;]+)/);
			if (match) {
				slSession = match[1];
				break;
			}
		}
	}

    // 提取key
    let key = '';
    if (response1.body) {
      const keyMatch = response1.body.match(/SafeLineChallenge\("([^"]+)"/);
      if (keyMatch) {
        key = keyMatch[1];
      }
    }
    log('提取到的sl-session: ', slSession);
	log('提取到的key: ', key);

    if (!slSession || !key) {
      throw new Error('无法提取sl-session或key');
    }

    // 第二步:请求issue
    log('第二步请求Issue...');
    const response2 = await customRequest(
		'https://challenge.rivers.chaitin.cn/challenge/v2/api/issue',
		{
			method: 'POST',
			headers: {
				'Accept': '*/*',
				'Content-Type': 'application/json',
				'Origin': 'https://flac.music.hi.cn',
				'Referer': 'https://flac.music.hi.cn/',
			},
			body: {"client_id":key,"level":1}
		}
	);
    log('请求issue响应状态码:', response2.statusCode);
    let issueData = response2.body.data;
	log('获取到的issue数据:', issueData);

    // 第三步:计算result
    const result = calculateArray(issueData.data);
	log('第三步计算结果: ', result);
    // 第四步:验证result
	log("第四步验证结果...");
    const response3 = await customRequest('https://challenge.rivers.chaitin.cn/challenge/v2/api/verify', {
      method: 'POST',
      headers: {
        'Accept': '*/*',
        'Content-Type': 'application/json',
        'Origin': 'https://flac.music.hi.cn',
        'Referer': 'https://flac.music.hi.cn/',
      },
      body: {
        issue_id: issueData.issue_id,
        result: result,
        serials: [],
        client: {
          userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
          platform: 'Win32',
          language: 'zh-CN',
          vendor: 'Google Inc.',
          screen: [1920, 1080],
          visitorId: '99999999999999999999999999999999',
          score: 0,
          target: []
        }
      }
    });
	log('第四步响应状态码: ', response3.statusCode);
    let jwt = response3.body.data.jwt;
	log('获取到的JWT: ', jwt);

    // 第五步:最终请求获取cookie
	log("第五步获取最终 cookie...");
    const response4 = await customRequest('https://flac.music.hi.cn/', {
      method: 'GET',
      headers: {
        'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
        'accept-language': 'zh-CN,zh;q=0.9',
        'cookie': `sl-session=${slSession}; sl-challenge-server=cloud; sl-challenge-jwt=${jwt}`,
        'referer': 'https://flac.music.hi.cn/',
      }
    });
    log('第五步响应状态码:', response4.statusCode);

    // 提取最终的cookie
    let finalCookies = response4.headers['set-cookie'];
	if (env === 'mobile') finalCookies = finalCookies.split(";");
	log("返回的Cookies: ", JSON.stringify(finalCookies));
    let slJwtSession = '';
    let slJwtSign = '';
    let slChallengeJwt = '';

    if (finalCookies) {
      for (const cookie of finalCookies) {
        if (cookie.includes('sl_jwt_session=')) {
          const match = cookie.match(/sl_jwt_session=([^;]+)/);
          if (match) slJwtSession = match[1];
        } else if (cookie.includes('sl_jwt_sign=')) {
          const match = cookie.match(/sl_jwt_sign=([^;]+)/);
          if (match) slJwtSign = match[1];
        } else if (cookie.includes('sl-challenge-jwt=')) {
          const match = cookie.match(/sl-challenge-jwt=([^;]+)/);
          if (match) slChallengeJwt = match[1];
        }
      }
    }

    log('提取的sl_jwt_session:', slJwtSession);
    log('提取的sl_jwt_sign:', slJwtSign);
    log('提取的sl-challenge-jwt:', slChallengeJwt);

    // 生成最终cookie
    const finalCookie = `sl-session=${slSession};sl-challenge-server=cloud;sl_jwt_session=${slJwtSession};sl_jwt_sign=${slJwtSign};sl-challenge-jwt=${slChallengeJwt}`;
    log('生成的新cookie: ', finalCookie);
    return finalCookie;
  } catch (error) {
    log('生成cookie过程中出错: ', error.message, "堆栈: ", error.stack);
    throw error;
  }
}

// 初始化cookie并设置定时更新
async function initCookie() {
  try {
    globalCookie = await generateCookie();
    log('Cookie初始化成功');
    
    // 设置每小时更新一次cookie
    if (cookieUpdateTimer) {
      clearInterval(cookieUpdateTimer);
    }
    cookieUpdateTimer = setInterval(async () => {
      try {
        globalCookie = await generateCookie();
        log('Cookie定时更新成功');
      } catch (error) {
        log('Cookie定时更新失败:', error.message);
      }
    }, 60 * 60 * 1000); // 1小时
  } catch (error) {
    log('Cookie初始化失败,使用默认cookie:', error.message, "堆栈: ", error.stack);
  }
}

// 初始化cookie
initCookie();

const httpFetch = (url, body) => new Promise((resolve, reject) => {
  log("发送请求 " + url + ", 参数: " + body);
  request(
    url, 
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
        'Accept': 'application/json, text/javascript, */*; q=0.01',
        'Origin': API_BASE_URL,
        'X-Requested-With': 'XMLHttpRequest',
        'Cookie': globalCookie, // 使用动态生成的cookie
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
      },
      body: body,
    },
    (err, resp) => {
      if (err) {
        log("请求错误: ", err);
        return reject(err)
      }
      log("服务器返回: ", resp);
	  if (resp.statusCode === 468) {
		initCookie();
		reject("Session过期, 重新获取");
	  }
      resolve(resp.body)
    }
  )
})

function getItem(list, info) {
  let filter = list.filter(item => item.id === info.musicInfo.songmid);
  if (filter.length > 0) return filter[0];

  filter = list.filter(item => 
    item.artist.toLowerCase() === info.musicInfo.singer.toLowerCase()
    && item.name.toLowerCase() === info.musicInfo.name.toLowerCase()
  );
  if (filter.length > 0) return filter[0];
    
  filter = list.filter(item => item.artist.toLowerCase() === info.musicInfo.singer.toLowerCase());
  if (filter.length > 0) return filter[0];

  filter = list.filter(item => item.name.toLowerCase() === info.musicInfo.name.toLowerCase());
  if (filter.length > 0) return filter[0];

  return list[0];
}

const mapBitrate = {
    '128k': '128',
    '320k': '320',
    flac: '2000',
    flac24bit: '2000',
  }
const mapFormat = {
	'128k': 'mp3',
    '320k': 'mp3',
    flac: 'flac',
    flac24bit: 'flac',
}

// 获取音乐URL
const musicUrl = async(source, info) => {
	log("运行 musicUrl: ", source, info);
	let songid = "";
	if (source == "kw") {
	  songid = info.musicInfo.songmid;
	} else {
		const key = encodeURIComponent(info.musicInfo.name + info.musicInfo.singer)
		const url = `${API_BASE_URL}/ajax.php?act=search`
		const response = await httpFetch(url, `keyword=${key}&page=1&size=30`);
		if (response.code === 0) {
			const item = getItem(response.data.list, info);
			log("获取链接: ", item);
			songid = item.id;
		} else {
			return Promise.reject("搜索失败");
		}
	}

	const url = `${API_BASE_URL}/ajax.php?act=getUrl`
	const resp = await httpFetch(url, `songid=${songid}&format=${mapFormat[info.type]}&bitrate=${mapBitrate[info.type]}`);
	if (resp.code === 0) {
	  return Promise.resolve(resp.data.url);
	} else {
	  return Promise.reject("获取歌曲地址失败");
	}
}

// 获取歌词
const lyricUrl = async(source, info) => {
  log("运行 lyricUrl", info);
  return Promise.resolve('')
}

// 获取专辑信息
const picUrl = async(source, info) => {
  log("运行 picUrl", info);  
  return Promise.resolve("")
}

on(EVENT_NAMES.request, ({ source, action, info }) => {
  switch (action) {
    case 'musicUrl':
      return musicUrl(source, info)
        .then(data => Promise.resolve(data))
        .catch(err => Promise.reject(err))
    case 'lyric':
      return lyricUrl(source, info)
        .then(data => Promise.resolve(data))
        .catch(err => Promise.reject(err))
    case 'pic':
      return picUrl(source, info)
        .then(data => Promise.resolve(data))
        .catch(err => Promise.reject(err))
    default:
      error(`action(${action}) not support`)
      return Promise.reject('action not support')
  }
})

send(EVENT_NAMES.inited, { 
  status: true, 
  openDevTools: false, 
  sources: {
    kw: {
      name: '酷我音乐',
      type: 'music',
      actions: ['musicUrl'],
      qualitys: ['128k', '320k', 'flac', 'flac24bit'],
    },
    kg: {
      name: '酷狗音乐',
      type: 'music',
      actions: ['musicUrl'],
      qualitys: ['128k', '320k', 'flac', 'flac24bit'],
    },
    tx: {
      name: 'QQ音乐',
      type: 'music',
      actions: ['musicUrl'],
      qualitys: ['128k', '320k', 'flac', 'flac24bit'],
    },
    wy: {
      name: '网易云音乐',
      type: 'music',
      actions: ['musicUrl'],
      qualitys: ['128k', '320k', 'flac', 'flac24bit'],
    },
    mg: {
      name: '咪咕音乐',
      type: 'music',
      actions: ['musicUrl'],
      qualitys: ['128k', '320k', 'flac', 'flac24bit'],
    },
    local: {
      name: '本地音乐',
      type: 'music',
      actions: ['musicUrl', 'lyric', 'pic'],
      qualitys: [],
    }
  }
})
无损音源2.js (14.0KB)