mf转lx需要修改的地方和lx模板

回复
rxxx
网站管理员
帖子: 49
注册时间: 2025年 11月 4日 11:57 星期二

mf转lx需要修改的地方和lx模板

帖子 rxxx »

需要修改:(现在已不用修改,代码通用了)
1. axios_1.default 改成 httpFetch
2.如果是通过歌名搜索的要将${title}改成${encodeURIComponent(title)}

不再需要进软件测试,已有js测试文档

代码: 全选

const request = require('request');
/**
--复制中间的代码出来使用就行--
const musicItem = {'id':'193290598','title':'如愿','artist':'王菲'};  //kw
const musicItem = {'id':'ec1a18bbd6b61cb203e656b91f5cf2d1','title':'如愿','artist':'王菲'};	//kg
const musicItem = {'id':'1888381008','title':'如愿','artist':'王菲'};	//wy
const musicItem = {'songmid':'001ufyHx10iWpg','title':'如愿','artist':'王菲'};	//qq 
const quality = '128k';
const quality = '320k';
const quality = 'flac';

const title = musicItem.title
${encodeURIComponent(title)}

console.log("res : ",res);

*/
const musicItem = {'id':'193290598','title':'如愿','artist':'王菲'};  //kw
const quality = '320k';

const kw_apis = [test_api];

//-------------------------------------------------------------------------------------------------------------------------------------------------------
//api 代码部分

async function test_api(musicItem, quality) {
	const qlist = {'128k':'standard', '320k':'exhigh', 'flac':'lossless', 'def':'exhigh',}
	const q = qlist[quality] ?? qlist.def
    const res = (await httpFetch(`https://musicapi.haitangw.net/music1/kw.php?type=mp3&id=${musicItem.id}&level=${q}`));
	return res.request?.res?.responseUrl || res.headers?.location || '';
}

//-------------------------------------------------------------------------------------------------------------------------------------------------------

/////其他 

/** 统一日志 **/
function logApi(apiName, msg, err = null) {
    if (err) {
        console.error(`[${apiName}] ${msg}`, err.message || err);
    } else {
        console.log(`[${apiName}] ${msg}`);
    }
}

/** httpFetch - 自用修改的 **/
//如果lx源res.headers?.location得不到地址,试试res.request?.href
//return res.request?.res?.responseUrl || res.headers?.location || res.request?.href || '';
async function httpFetch(config = {}, options = {}) {
  // ✔ 支持 httpFetch(url, options)
  if (typeof config === "string") {config = { url: config, ...options };}

  const {url, method = "GET", headers = {}, data, timeout = 10000, followRedirect = false,} = config;//因res.headers?.location需要:followRedirect = false关闭了跳转,需要时:打开true

  return new Promise((resolve, reject) => {
    request(url,{method, headers, timeout,  followRedirect, body: method === "POST" && typeof data === "object" ? JSON.stringify(data) : data,},
      (err, resp, body) => {
        if (err) return reject(err);

        let parsed = body;
        try {if (typeof body === "string") parsed = JSON.parse(body);} catch {parsed = body}

        resolve({data: parsed,body: parsed,status: resp.statusCode,headers: resp.headers || {},request: resp.request,});//body: parsed,兼容LX,data:parsed 方便不用修改代码
      }
    );
  });
} 

/** 通用执行器 **/
async function getMediaSource(musicItem, quality) {
    console.log("========== 开始获取音源 ==========");
    //自动选择自己定义的apis不用一个一个修改了
	let apiList = typeof wy_apis !== "undefined" ? wy_apis : typeof kw_apis !== "undefined" ? kw_apis : typeof kg_apis !== "undefined" ? kg_apis : typeof qq_apis !== "undefined" ? qq_apis : [];
	if (typeof qq_apis !== "undefined" && apiList === qq_apis) musicItem = { ...musicItem, id: musicItem.songmid };
        const apiName = api.name;
        try {
            logApi(apiName, "开始:");
            // ⏱️ 记录开始时间
            const startTime = Date.now();
            const musicUrl = await api(musicItem, quality);
            // ⏱️ 结束时间 + 耗时
            const costTime = Date.now() - startTime;
			logApi(apiName, `耗时: ${costTime}ms`);
            if (musicUrl && musicUrl !== "None") {
                logApi(apiName, `成功: ${musicUrl}`);
				//自己测试所有api的话,注释掉下面这行
                //return {url: musicUrl,source: apiName,};
            } else {
                logApi(apiName, `获取失败!`);
            }
        } catch (err) {
            logApi(apiName, `执行异常:`, err);
        }
    }
    console.log("========== 所有API获取完成 ==========");
    return {url: "",source: "",};
}


// 示例调用
(async () => {
  const redirectUrl = await getMediaSource(musicItem, quality);
  console.log("最终的重定向 URL:", redirectUrl);
})();

下载电脑版测试api时,需要调试时:(设置为true,就可以看到运行日志)
//const DEV_ENABLE = true //代码已开启

代码: 全选

/*!
 * @name rx 20260707
 */
const rx_version = '20260707';
const kw_apis = [test_api];
const wy_apis = [test_api];
const qq_apis = [test_api];
const kg_apis = [test_api];
//-------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------

async function test_api(musicItem, quality) {
	const qlist = {'128k':'6', '320k':'8', 'flac':'10', 'def':'8',}
	const q = qlist[quality] ?? qlist.def
    const res = (await httpFetch(`https://api.vkeys.cn/v2/music/tencent?mid=${musicItem.id}&quality=${q}`));
    return res?.data?.data?.url || "";
}


//-------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------
//====复制上面部分使用
//====
//====
//====
//====
//====


//下面是固定代码,复制时复制上面部分
/** 统一日志 **/
function logApi(apiName, msg, err = null) {
    if (err) {
        console.error(`[${apiName}] ${msg}`, err.message || err);
    } else {
        console.log(`[${apiName}] ${msg}`);
    }
}

/**
 * 通用执行器
 * @param {string} source tx | kw | kg | wy
 * @param {object} musicItem
 * @param {string} quality
 * @returns {string} musicUrl
 */
async function getMediaSource(source, musicItem, quality) {
    console.log("========== 开始获取音源 ==========");

    // 根据 source 选择 API 列表
    const apiMap = {tx: qq_apis,kw: kw_apis,kg: kg_apis,wy: wy_apis,};
    const apis = apiMap[source];
    // source 不合法
    if (!apis) {console.log(`未知音源类型: ${source}`); return "";}
	//apis开始执行
    for (const api of apis) {
        const apiName = api.name;
        try {
            logApi(apiName, "开始:");
            const musicUrl = await api(musicItem, quality);
            if (musicUrl && musicUrl !== "None") {
                logApi(apiName, `成功: ${musicUrl}`);
                // 获取成功立即返回
                //return musicUrl;
            } else {
                logApi(apiName, "获取失败!");
            }
        } catch (err) {
            logApi(apiName, "执行异常", err);
        }
    }
}

/**
 * 洛雪调用接口 - handleGetMusicUrl
 * 自己在里面改变了调用,自己使用
 */
const handleGetMusicUrl = async (source, musicInfo, quality) => {
  //console.log('musicInfo:',musicInfo);//显示传入值,方便查看信息
  const songId = musicInfo.hash ?? musicInfo.songmid;//kg使用的hash,其他都是songmid
  //const musicItem = {[source === 'tx' ? 'songmid' : 'id']: songId,'title':musicInfo.name,'artist':musicInfo.singer ?? musicInfo.artist};//因musicfree代码格式,改变传入值,方便代码拿来直接使用,不用改原代码
  const musicItem = {'id': songId,'title':musicInfo.name,'artist':musicInfo.singer ?? musicInfo.artist};//因musicfree代码格式,改变传入值,方便代码拿来直接使用,不用改原代码
  const musicUrl = await getMediaSource(source, musicItem, quality)

  if (!musicUrl) {
    throw new Error('rx get musicUrl failed')
  }

  return musicUrl
}

/** httpFetch - 自用修改的 **/
//如果lx源res.headers?.location得不到地址,试试res.request?.href
//return res.request?.res?.responseUrl || res.headers?.location || res.request?.href || '';
async function httpFetch(config = {}, options = {}) {
  // ✔ 支持 httpFetch(url, options)
  if (typeof config === "string") {config = { url: config, ...options };}

  const {url, method = "GET", headers = {}, data, timeout = 10000, followRedirect = false,} = config;//因res.headers?.location需要:followRedirect = false关闭了跳转,需要时:打开true

  return new Promise((resolve, reject) => {
    request(url,{method, headers, timeout,  followRedirect, body: method === "POST" && typeof data === "object" ? JSON.stringify(data) : data,},
      (err, resp, body) => {
        if (err) return reject(err);

        let parsed = body;
        try {if (typeof body === "string") parsed = JSON.parse(body);} catch {parsed = body}

        resolve({data: parsed,body: parsed,status: resp.statusCode,headers: resp.headers || {},request: resp.request,});//body: parsed,兼容LX,data:parsed 方便不用修改代码
      }
    );
  });
} 
//自用修改的httpFetch
//如果上面那个版本不能使用,试试这个老版,适应老接口
//需要放到代码最上面
/*
const httpFetch = (config = {}) =>
  new Promise((resolve, reject) => {
    if (typeof config === 'string') config = { url: config }
	
    const { url, method = 'GET', headers = {}, data, timeout = 10000, maxRedirects } = config
	
    request( url, {method, headers, timeout, maxRedirects, body: method === 'POST' && typeof data === 'object' ? JSON.stringify(data) : data,},
      (err, resp, body) => {
        if (err) return reject(err)

        let d = body
        try { if (typeof body === 'string') d = JSON.parse(body) } catch { d = body }
        resolve({status: resp.statusCode, headers: resp.headers, data: d, body: d, request: resp.request,})//body: d兼容LX,data:d方便不用修改代码
      }
    )
  })
  */

//上面是自己编写代码
//下面是原代码
const DEV_ENABLE = true
//const DEV_ENABLE = false
const MUSIC_QUALITY = {
  kw: ["128k", "320k", "flac", "flac24bit"],
  kg: ["128k", "320k", "flac", "flac24bit"],
  tx: ["128k", "320k", "flac", "flac24bit"],
  wy: ["128k", "320k", "flac", "flac24bit"],
  mg: ["128k", "320k", "flac", "flac24bit"],
}

const MUSIC_SOURCE = Object.keys(MUSIC_QUALITY)
const { EVENT_NAMES, request, on, send, utils, env, version } = globalThis.lx


const musicSources = {}
MUSIC_SOURCE.forEach(item => {
  musicSources[item] = {
    name: item,
    type: 'music',
    actions: ['musicUrl'],
    qualitys: MUSIC_QUALITY[item],
  }
})
on(EVENT_NAMES.request, ({ action, source, info }) => {
  switch (action) {
    case 'musicUrl':
      if (env != 'mobile') {
        console.group(`Handle Action(musicUrl)`)
        console.log('source', source)
        console.log('quality', info.type)
        console.log('musicInfo', info.musicInfo)
        console.groupEnd()
      } else {
        console.log(`Handle Action(musicUrl)`)
        console.log('source', source)
        console.log('quality', info.type)
        console.log('musicInfo', info.musicInfo)
      }
      return handleGetMusicUrl(source, info.musicInfo, info.type)
        .then(data => Promise.resolve(data))
        .catch(err => Promise.reject(err))
    default:
      console.error(`action(${action}) not support`)
      return Promise.reject('action not support')
  }
})

// ========== 检查更新功能 ==========
// LX Music 官方规范:脚本需要处理 checkUpdate 事件来响应更新检查
const checkUpdate = async () => {
  try {
    console.log("rx checkUpdate begin: ")
    const res = await httpFetch(`https://rxsub.eu.org/music/lx/rx.js?t=${Date.now()}`);
    const js = typeof res.body === "string" ? res.body : "";

    const match = js.match(/const\s+rx_version\s*=\s*['"]([^'"]+)['"]/);
    if (!match) throw new Error("未找到版本号");

    const latestVersion = match[1];

	if (latestVersion !== rx_version) {
		send(EVENT_NAMES.updateAlert, {
			log: `${latestVersion}`,
			updateUrl: "https://rxsub.eu.org/music/lx/rx.js"
		});
	}
  } catch (err) {
    console.error("[更新检查失败]", err);
    return {
      version: rx_version,
      haveUpdate: false,
    };
  }
}
if (true) checkUpdate()
send(EVENT_NAMES.inited, { status: true, openDevTools: DEV_ENABLE, sources: musicSources })
回复