code:
function kuwo(packages) {
    const { axios, he, CookieManager } = packages;

    const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    const pageSize = 30;
    function getRandomCsrf() {
        return Array(11).fill(0).map(_ => alphabet[Math.floor(Math.random() * 36)]).join('');
    }

    async function getHeaders() {
        await CookieManager.flush();
        const csrfToken = (await CookieManager.get('www.kuwo.cn'))?.kw_token?.value ?? getRandomCsrf();

        return {
            "user-agent":
                "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36 Edg/89.0.774.63",
            accept: "application/json, text/plain, */*",
            "accept-encoding": "gzip, deflate, br",
            csrf: csrfToken,
            cookie: `kw_token=${csrfToken}`,
            referer: "http://www.kuwo.cn/",
            host: 'www.kuwo.cn',
            "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
        };
    }

    function formatMusicItem(_){
        return {
            id: _.rid,
            artwork: _.pic || _.albumpic || _.pic120,
            title: he.decode(_.name || ''),
            artist: he.decode(_.artist || ''),
            album: he.decode(_.album || '')
        }
    }

    function formatAlbumItem(_) {
        return {
            id: _.albumid,
            artist: he.decode(_.artist || ''),
            title: he.decode(_.album || ''),
            artwork: _.pic,
            description: he.decode(_.albuminfo || ''),
            date: _.releaseDate,
        }
    }

    async function searchMusic(query, page) {
        const headers = await getHeaders();
        const res = (await axios({
            method: 'get',
            url: `http://www.kuwo.cn/api/www/search/searchMusicBykeyWord?key=${query}&pn=${page}&rn=${pageSize}&httpStatus=1`,
            headers,
        }));
        const songs = res.data.data.list.filter(_ => !_.isListenFee).map(formatMusicItem)

        return {
            isEnd: res.data.data.total <= page * pageSize,
            data: songs
        }
    }

    async function searchAlbum(query, page) {
        const headers = await getHeaders();
        const res = (await axios.get('http://www.kuwo.cn/api/www/search/searchAlbumBykeyWord', {
            headers,
            params: {
                key: query,
                pn: page,
                rn: pageSize,
                httpStatus: 1
            }
        })).data;
        const albums = res.data.albumList.map(formatAlbumItem)

        return {
            isEnd: res.data.total <= page * pageSize,
            data: albums
        }

    }

    async function searchArtist(query, page) {
        const headers = await getHeaders();
        const res = (await axios.get('http://www.kuwo.cn/api/www/search/searchArtistBykeyWord', {
            headers,
            params: {
                key: query,
                pn: page,
                rn: pageSize,
                httpStatus: 1
            }
        })).data;
        const artists = res.data.list.map(_ => ({
            name: he.decode(_.name),
            id: _.id,
            avatar: _.pic || _.pic120 || _.pic700 || _.pic70,
            worksNum: _.musicNum
        }))

        return {
            isEnd: res.data.total <= page * pageSize,
            data: artists
        }
    }

    async function getArtistMusicWorks (artistItem, page) {
        const headers = await getHeaders();
        const res = (await axios.get('http://www.kuwo.cn/api/www/artist/artistMusic', {
            headers: {
                ...headers,
                referer: `http://www.kuwo.cn/singer_detail/${artistItem.id}`
            },
            params: {
                artistid: artistItem.id,
                pn: page,
                rn: pageSize,
                httpStatus: 1
            }
        })).data;
        const musicList = res.data.list.filter(_ => !_.isListenFee).map(formatMusicItem);
        return {
            isEnd: res.data.total <= page * pageSize,
            data: musicList
        }
    }

    async function getArtistAlbumWorks (artistItem, page) {
        const headers = await getHeaders();
        const res = (await axios.get('http://www.kuwo.cn/api/www/artist/artistAlbum', {
            headers: {
                ...headers,
                referer: `http://www.kuwo.cn/singer_detail/${artistItem.id}`
            },
            params: {
                artistid: artistItem.id,
                pn: page,
                rn: pageSize,
                httpStatus: 1
            }
        })).data;
        const albumList = res.data.albumList.map(formatAlbumItem);
        return {
            isEnd: res.data.total <= page * pageSize,
            data: albumList
        }
    }

    async function getArtistWorks(artistItem, page, type) {
        if(type === 'music') {
            return getArtistMusicWorks(artistItem, page);
        } else if (type === 'album') {
            return getArtistAlbumWorks(artistItem, page);
        }
    }

    async function getLyric(musicItem) {
        const headers = await getHeaders();
        const res = (await axios.get('http://m.kuwo.cn/newh5/singles/songinfoandlrc', {
            headers,
            params: {
                musicId: musicItem.id,
                httpStatus: 1
            }
        })).data;
        const list = res.data.lrclist;
        return {
            rawLrc: list.map(_ => `[${_.time}]${_.lineLyric}`).join('\n')
        }
    }

    async function getAlbumInfo(albumItem) {

        const headers = await getHeaders();
        const res = (await axios.get('http://www.kuwo.cn/api/www/album/albumInfo', {
            headers,
            params: {
                albumId: albumItem.id,
                httpStatus: 1
            }
        })).data;

        return {
            ...albumItem,
            musicList: res.data.musicList.filter(_ => !_.isListenFee).map(formatMusicItem)
        }
    }

    return {
        platform: '酷我',
        version: '0.0.0',
        appVersion: '>0.0.1-alpha.3',
        srcUrl: 'https://gitee.com/maotoumao/MusicFreePlugins/raw/master/kuwo.js',
        cacheControl: 'no-cache',
        async search(query, page, type) {
            if (type === 'music') {
                return await searchMusic(query, page);
            }
            if (type === 'album') {
                return await searchAlbum(query, page);
            }
            if (type === 'artist') {
                return await searchArtist(query, page);
            }
        },
        async getMediaSource(musicItem) {
            const headers = await getHeaders();
            const res = (await axios.get('http://www.kuwo.cn/api/v1/www/music/playUrl', {
                headers,
                params: {
                    mid: musicItem.id,
                    type: 'music',
                    httpStatus: 1
                }
            })).data;

            return {
                url: res.data.url
            };
        },
        getAlbumInfo,
        getLyric,
        getArtistWorks

    }
}


code:
function migu(packages) {
  const { axios, cheerio } = packages;

  const searchRows = 20;

  async function searchBase(query, page, type) {
    const headers = {
      'Accept': 'application/json, text/javascript, */*; q=0.01',
      'Accept-Encoding': 'gzip, deflate, br',
      'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
      'Connection': 'keep-alive',
      'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
      'Host': 'm.music.migu.cn',
      'Referer': `https://m.music.migu.cn/v3/search?keyword=${encodeURIComponent(query)}`,
      'Sec-Fetch-Dest': 'empty',
      'Sec-Fetch-Mode': 'cors',
      'Sec-Fetch-Site': 'same-origin',
      'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0.1; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Mobile Safari/537.36 Edg/89.0.774.68',
      'X-Requested-With': 'XMLHttpRequest'
    };
    const params = {
      keyword: query,
      type,
      pgc: page,
      rows: searchRows
    }
    const data = await axios.get('https://m.music.migu.cn/migu/remoting/scr_search_tag', { headers, params });
    return data.data;
  }

  function musicCanPlayFilter(_) {
    return _.mp3 || _.listenUrl || _.lisQq || _.lisCr;
  }

  async function searchMusic(query, page) {
    const data = await searchBase(query, page, 2);
    const musics = data.musics.filter(musicCanPlayFilter).map(_ => ({
      id: _.id,
      artwork: _.cover,
      title: _.songName,
      artist: _.artist,
      album: _.albumName,
      url: musicCanPlayFilter(_),
      copyrightId: _.copyrightId,
      singerId: _.singerId,
    }))
    return {
      isEnd: (+data.pageNo) * searchRows >= data.pgt,
      data: musics
    };
  }

  async function searchAlbum(query, page) {
    const data = await searchBase(query, page, 4);
    const albums = data.albums.map(_ => ({
      id: _.id,
      artwork: _.albumPicM,
      title: _.title,
      date: _.publishDate,
      artist: (_.singer || []).map(s => s.name).join(','),
      singer: _.singer,
      fullSongTotal: _.fullSongTotal
    }));
    return {
      isEnd: (+data.pageNo) * searchRows >= data.pgt,
      data: albums
    }
  }

  async function searchArtist(query, page) {
    const data = await searchBase(query, page, 1);
    const artists = data.artists.map(result => ({
      name: result.title,
      id: result.id,
      avatar: result.artistPicM,
      worksNum: result.songNum
    }));
    return {
      isEnd: (+data.pageNo) * searchRows >= data.pgt,
      data: artists,
    }
  }

  async function getArtistAlbumWorks(artistItem, page) {
    const 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.9',
      'accept-encoding': 'gzip, deflate, br',
      'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
      'connection': 'keep-alive',
      'host': 'music.migu.cn',
      'referer': 'http://music.migu.cn',
      'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36',
      'Cache-Control': 'max-age=0',
    };
    const html = (await axios.get(`https://music.migu.cn/v3/music/artist/${artistItem.id}/album?page=${page}`, {
      headers
    })).data;
    const $ = cheerio.load(html);
    const rawAlbums = $('div.artist-album-list').find('li');
    const albums = [];
    for (let i = 0; i < rawAlbums.length; ++i) {
      const al = $(rawAlbums[i]);
      const artwork = al.find('.thumb-img').attr('data-original');
      albums.push({
        id: al.find('.album-play').attr('data-id'),
        title: al.find('.album-name').text(),
        artwork: artwork.startsWith('//') ? `https:${artwork}` : artwork,
        date: '',
        artist: artistItem.name
      })
    }

    return {
      isEnd: $('.pagination-next').hasClass('disabled'),
      data: albums
    }
  }

  async function getArtistWorks(artistItem, page, type) {
    if (type === 'music') {
      const headers = {
        'Accept': 'application/json, text/javascript, */*; q=0.01',
        'Accept-Encoding': 'gzip, deflate, br',
        'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
        'Connection': 'keep-alive',
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
        'Host': 'm.music.migu.cn',
        'Referer': `https://m.music.migu.cn/migu/l/?s=149&p=163&c=5123&j=l&id=${artistItem.id}`,
        'Sec-Fetch-Dest': 'empty',
        'Sec-Fetch-Mode': 'cors',
        'Sec-Fetch-Site': 'same-origin',
        'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0.1; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Mobile Safari/537.36 Edg/89.0.774.68',
        'X-Requested-With': 'XMLHttpRequest'
      };
      //  https://m.music.migu.cn/migu/remoting/cms_artist_detail_tag
      const musicList = (await axios.get('https://m.music.migu.cn/migu/remoting/cms_artist_song_list_tag', {
        headers,
        params: {
          artistId: artistItem.id,
          pageSize: 20,
          pageNo: page - 1
        }
      })).data || {};

      return {
        data: musicList.result.results.filter(musicCanPlayFilter).map(_ => ({
          id: _.songId,
          artwork: _.picM,
          title: _.songName,
          artist: (_.singerName || []).join(', '),
          album: _.albumName,
          url: musicCanPlayFilter(_),
          rawLrc: _.lyricLrc,
          copyrightId: _.copyrightId,
          singerId: _.singerId,
        }))
      }
    } else if (type === 'album') {
      return getArtistAlbumWorks(artistItem, page);
    }
  }

  async function getLyric(musicItem) {
    const headers = {
      'Accept': 'application/json, text/javascript, */*; q=0.01',
      'Accept-Encoding': 'gzip, deflate, br',
      'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
      'Connection': 'keep-alive',
      'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
      'Host': 'm.music.migu.cn',
      'Referer': `https://m.music.migu.cn/migu/l/?s=149&p=163&c=5200&j=l&id=${musicItem.copyrightId}`,
      'Sec-Fetch-Dest': 'empty',
      'Sec-Fetch-Mode': 'cors',
      'Sec-Fetch-Site': 'same-origin',
      'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0.1; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Mobile Safari/537.36 Edg/89.0.774.68',
      'X-Requested-With': 'XMLHttpRequest'
    };

    const result = (await axios.get('https://m.music.migu.cn/migu/remoting/cms_detail_tag', {
      headers,
      params: {
        cpid: musicItem.copyrightId
      }
    })).data;
    return {
      rawLrc: result.data.lyricLrc
    }
  }

  async function importMusicSheet(urlLike) {
    let id;
    if (!id) {
      id = (urlLike.match(/https?:\/\/music\.migu\.cn\/v3\/my\/playlist\/([0-9]+)/) || [])[1];
    }
    if (!id) {
      id = (urlLike.match(/https?:\/\/h5\.nf\.migu\.cn\/app\/v4\/p\/share\/playlist\/index.html\?.*id=([0-9]+)/) || [])[1];
    }
    if (!id) {
      return;
    }

    const headers = {
      host: 'm.music.migu.cn',
      'Sec-Fetch-Dest': 'empty',
      'Sec-Fetch-Mode': 'cors',
      'Sec-Fetch-Site': 'same-origin',
      'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0.1; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Mobile Safari/537.36 Edg/89.0.774.68',
      'X-Requested-With': 'XMLHttpRequest',
      'Referer': 'https://m.music.migu.cn'
    }

    const res = (await (axios.get(`https://m.music.migu.cn/migu/remoting/query_playlist_by_id_tag?onLine=1&queryChannel=0&createUserId=migu&contentCountMin=5&playListId=${id}`, {
      headers
    }))).data;

    const contentCount = parseInt(res.rsp.playList[0].contentCount);
    const cids = [];

    let pageNo = 1;
    while ((pageNo-1) * 20 < contentCount) {
      const listPage = (await axios.get(`https://music.migu.cn/v3/music/playlist/${id}?page=${pageNo}`)).data;

      const $ = cheerio.load(listPage);

      $('.row.J_CopySong').each((i, v) => cids.push($(v).attr('data-cid')));

      pageNo += 1;
    }
    if(cids.length === 0) {
      return;
    }
    const songs = (await axios({
      url: `https://music.migu.cn/v3/api/music/audioPlayer/songs?type=1&copyrightId=${cids.join(',')}`,
      headers: {
        referer: 'http://m.music.migu.cn/v3'
      },
      xsrfCookieName: 'XSRF-TOKEN',
      withCredentials: true,
    })).data;

    return songs.items.filter(_ => _.vipFlag === 0).map(_ => ({
      id: _.songId,
      artwork: _.cover,
      title: _.songName,
      artist: (_.singers || []).map(_ => _.artistName).join(', '),
      album: ((_.albums || [])[0] || {}).albumName,
      copyrightId: _.copyrightId,
      singerId: ((_.singers || [])[0] || {}).artistId,
    }))

  }

  return {
    platform: '咪咕',
    version: '0.0.3',
    appVersion: ">0.0.1-alpha.10",
    primaryKey: ['id', 'copyrightId'],
    cacheControl: 'no-store',
    srcUrl: 'https://gitee.com/maotoumao/MusicFreePlugins/raw/master/migu.js',
    async getMediaSource(musicItem, quality) {
      if(quality === 'standard' && musicItem.url) {
        return {
          url: musicItem.url
        }
      };
      let toneFlag = 'HQ';
      if(quality === 'super') {
        toneFlag = 'ZQ';
      } else if(quality === 'high') {
        toneFlag = 'SQ';
      } else if(quality === 'low') {
        toneFlag = 'PQ';
      }
      const resource = (await axios({
        url: `https://app.c.nf.migu.cn/MIGUM2.0/strategy/listen-url/v2.2?netType=01&resourceType=E&songId=${musicItem.copyrightId}&toneFlag=${toneFlag}`,
        headers: {
          referer: 'http://m.music.migu.cn/v3',
          uid: 123,
          channel: '0146741'
        }
      })).data.data;

      return {
        artwork: musicItem.artwork || ((resource.songItem.albumImgs[0] || {}).img),
        url: resource.url
      };
    },

    async search(query, page, type) {
      if (type === 'music') {
        return await searchMusic(query, page);
      }
      if (type === 'album') {
        return await searchAlbum(query, page);
      }
      if (type === 'artist') {
        return await searchArtist(query, page);
      }

    },

    async getAlbumInfo(albumItem) {
      const headers = {
        'Accept': 'application/json, text/javascript, */*; q=0.01',
        'Accept-Encoding': 'gzip, deflate, br',
        'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
        'Connection': 'keep-alive',
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
        'Host': 'm.music.migu.cn',
        'Referer': `https://m.music.migu.cn/migu/l/?record=record&id=${albumItem.id}`,
        'Sec-Fetch-Dest': 'empty',
        'Sec-Fetch-Mode': 'cors',
        'Sec-Fetch-Site': 'same-origin',
        'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0.1; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Mobile Safari/537.36 Edg/89.0.774.68',
        'X-Requested-With': 'XMLHttpRequest'
      };
      const musicList = (await axios.get('https://m.music.migu.cn/migu/remoting/cms_album_song_list_tag', {
        headers,
        params: {
          albumId: albumItem.id,
          pageSize: 30
        }
      })).data || {};
      const albumDesc = (await axios.get('https://m.music.migu.cn/migu/remoting/cms_album_detail_tag', {
        headers,
        params: {
          albumId: albumItem.id
        }
      })).data || {}; // 有个trackcount

      return {
        ...albumItem,
        description: albumDesc.albumIntro,
        musicList: musicList.result.results.filter(musicCanPlayFilter).map(_ => ({
          id: _.songId,
          artwork: _.picM,
          title: _.songName,
          artist: (_.singerName || []).join(', '),
          album: albumItem.title,
          url: musicCanPlayFilter(_),
          rawLrc: _.lyricLrc,
          copyrightId: _.copyrightId,
          singerId: _.singerId,
        }))
      }
    },
    getArtistWorks: getArtistWorks,
    getLyric: getLyric,
    importMusicSheet

  };
}

license: mit


code:
function netease(packages) {
    const { axios, CryptoJs, qs, bigInt, dayjs, cheerio } = packages;

    /** 内部的函数 */

    function a() {
        var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", c = "";
        for (d = 0; 16 > d; d += 1)
            e = Math.random() * b.length,
                e = Math.floor(e),
                c += b.charAt(e);
        return c
    }

    function b(a, b) {
        var c = CryptoJs.enc.Utf8.parse(b)
            , d = CryptoJs.enc.Utf8.parse("0102030405060708")
            , e = CryptoJs.enc.Utf8.parse(a)
            , f = CryptoJs.AES.encrypt(e, c, {
                iv: d,
                mode: CryptoJs.mode.CBC
            });
        return f.toString()
    }

    function c(text) {
        text = text.split('').reverse().join('');
        const d = '010001';
        const e = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7';
        const hexText = text.split('').map(_ => _.charCodeAt(0).toString(16)).join('');
        const res = bigInt(hexText, 16).modPow(bigInt(d, 16), bigInt(e, 16)).toString(16);

        return Array(256 - res.length).fill('0').join('').concat(res);
    }

    function getParamsAndEnc(text) {
        const first = b(text, '0CoJUm6Qyw8W8jud');
        const rand = a();
        const params = b(first, rand);

        const encSecKey = c(rand);
        return {
            params,
            encSecKey
        }
    }

    function formatMusicItem(_) {
        const album = _.al || _.album;
        return ({
            id: _.id,
            artwork: album.picUrl,
            title: _.name,
            artist: (_.ar || _.artists)[0].name,
            album: album.name,
            url: `https://music.163.com/song/media/outer/url?id=${_.id}.mp3`,
            qualities: {
                low: {
                    size: (_.l || {}).size
                },
                standard: {
                    size: (_.m || {}).size
                },
                high: {
                    size: (_.h || {}).size
                },
                super: {
                    size: (_.sq || {}).size
                },
            }
        })
    }

    function formatAlbumItem(_) {
        return ({
            id: _.id,
            artist: _.artist.name,
            title: _.name,
            artwork: _.picUrl,
            description: '',
            date: dayjs.unix(_.publishTime / 1000).format("YYYY-MM-DD"),
        });
    }

    function musicCanPlayFilter(_) {
        return ((_.fee === 0) || _.fee === 8) && _.privilege.st >= 0;
    }

    const pageSize = 30;
    async function searchBase(query, page, type) {
        const data = {
            's': query,
            'limit': pageSize,
            'type': type,
            'offset': (page - 1) * pageSize,
            'csrf_token': ''
        }
        const pae = getParamsAndEnc(JSON.stringify(data));
        const paeData = qs.stringify(pae);

        const headers = {
            'authority': 'music.163.com',
            'user-agent':
                'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36',
            'content-type': 'application/x-www-form-urlencoded',
            'accept': '*/*',
            'origin': 'https://music.163.com',
            'sec-fetch-site': 'same-origin',
            'sec-fetch-mode': 'cors',
            'sec-fetch-dest': 'empty',
            'referer': 'https://music.163.com/search/',
            'accept-language': 'zh-CN,zh;q=0.9',
        }

        const res = (await axios({
            method: 'post',
            url: 'https://music.163.com/weapi/cloudsearch/get/web?csrf_token=',
            headers,
            data: paeData
        })).data;

        return res;
    }

    async function searchMusic(query, page) {

        const res = await searchBase(query, page, 1);

        const songs = res.result.songs.filter(musicCanPlayFilter).map(formatMusicItem)

        return {
            isEnd: res.result.songCount <= page * pageSize,
            data: songs
        }

    }

    async function searchAlbum(query, page) {
        const res = await searchBase(query, page, 10);

        const albums = res.result.albums.map(formatAlbumItem)

        return {
            isEnd: res.result.albumCount <= page * pageSize,
            data: albums
        }
    }

    async function searchArtist(query, page) {
        const res = await searchBase(query, page, 100);

        const artists = res.result.artists.map(_ => ({
            name: _.name,
            id: _.id,
            avatar: _.img1v1Url,
            worksNum: _.albumSize
        }))

        return {
            isEnd: res.result.artistCount <= page * pageSize,
            data: artists
        }
    }

    async function getArtistWorks(artistItem, page, type) {
        const data = {
            'csrf_token': ''
        }
        const pae = getParamsAndEnc(JSON.stringify(data));
        const paeData = qs.stringify(pae);

        const headers = {
            'authority': 'music.163.com',
            'user-agent':
                'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36',
            'content-type': 'application/x-www-form-urlencoded',
            'accept': '*/*',
            'origin': 'https://music.163.com',
            'sec-fetch-site': 'same-origin',
            'sec-fetch-mode': 'cors',
            'sec-fetch-dest': 'empty',
            'referer': 'https://music.163.com/search/',
            'accept-language': 'zh-CN,zh;q=0.9',
        }

        if (type === 'music') {
            const res = (await axios({
                method: 'post',
                url: `https://music.163.com/weapi/v1/artist/${artistItem.id}?csrf_token=`,
                headers,
                data: paeData
            })).data;
            return {
                isEnd: true,
                data: res.hotSongs.filter(musicCanPlayFilter).map(formatMusicItem)
            }
        } else if (type === 'album') {
            const res = (await axios({
                method: 'post',
                url: `https://music.163.com/weapi/artist/albums/${artistItem.id}?csrf_token=`,
                headers,
                data: paeData
            })).data;
            return {
                isEnd: true,
                data: res.hotAlbums.map(formatAlbumItem)
            }
        }

    }

    async function getTopListDetail(topListItem) {
        const musicList = await getSheetMusicById(topListItem.id);
        return {
            ...topListItem,
            musicList
        }
    }

    async function getLyric(musicItem) {
        const headers = {
            'Referer': 'https://y.music.163.com/',
            'Origin': 'https://y.music.163.com/',
            'authority': 'music.163.com',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36',
            'Content-Type': 'application/x-www-form-urlencoded',
        };
        const data = { id: musicItem.id, lv: -1, tv: -1, csrf_token: '' }
        const pae = getParamsAndEnc(JSON.stringify(data));
        const paeData = qs.stringify(pae);

        const result = (await axios({
            method: 'post',
            url: `https://interface.music.163.com/weapi/song/lyric?csrf_token=`,
            headers,
            data: paeData
        })).data;

        return {
            rawLrc: result.lrc.lyric
        }
    }

    async function getAlbumInfo(albumItem) {
        const headers = {
            'Referer': 'https://y.music.163.com/',
            'Origin': 'https://y.music.163.com/',
            'authority': 'music.163.com',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36',
            'Content-Type': 'application/x-www-form-urlencoded',
        };
        const data = { "resourceType": 3, "resourceId": albumItem.id, "limit": 15, "csrf_token": "" }
        const pae = getParamsAndEnc(JSON.stringify(data));
        const paeData = qs.stringify(pae);

        const res = (await axios({
            method: 'post',
            url: `https://interface.music.163.com/weapi/v1/album/${albumItem.id}?csrf_token=`,
            headers,
            data: paeData
        })).data;

        return {
            ...albumItem,
            description: res.album.description,
            musicList: (res.songs || []).filter(musicCanPlayFilter).map(formatMusicItem)
        }

    }

    async function getValidMusicItems(trackIds) {
        const headers = {
            'Referer': 'https://y.music.163.com/',
            'Origin': 'https://y.music.163.com/',
            'authority': 'music.163.com',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36',
            'Content-Type': 'application/x-www-form-urlencoded'
        };
        try {
            const data = { csrf_token: '', ids: `[${trackIds.join(',')}]`, level: 'standard', encodeType: 'flac' }
            const pae = getParamsAndEnc(JSON.stringify(data));
            const urlencoded = qs.stringify(pae);
            const res = (await axios({
                method: 'post',
                url: `https://music.163.com/weapi/song/enhance/player/url/v1?csrf_token=`,
                headers,
                data: urlencoded
            })).data;

            const validTrackIds = res.data.filter(_ => _.url).map(_ => _.id);
            const songDetails = (await axios.get(`https://music.163.com/api/song/detail/?id=${validTrackIds[0]}&ids=[${validTrackIds.join(',')}]`, { headers })).data;
            const validMusicItems = songDetails.songs.filter(_ => ((_.fee === 0) || _.fee === 8)).map(formatMusicItem);
            return validMusicItems;
        } catch (e) {
            return []
        }

    }

    async function getSheetMusicById(id) {
        const headers = {
            'Referer': 'https://y.music.163.com/',
            'Origin': 'https://y.music.163.com/',
            'authority': 'music.163.com',
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36',
        };
        const sheetDetail = (await axios.get(`https://music.163.com/api/v3/playlist/detail?id=${id}&n=5000`, {
            headers
        })).data;
        const trackIds = sheetDetail.playlist.trackIds.map(_ => _.id);
        let result = [];
        let idx = 0;
        while ((idx * 200) < trackIds.length) {
            const res = await getValidMusicItems(trackIds.slice(idx * 200, (idx + 1) * 200));
            result = result.concat(res);
            ++idx;
        }
        return result;
    }

    async function importMusicSheet(urlLike) {
        const matchResult = urlLike.match(/(?:https:\/\/y\.music\.163.com\/m\/playlist\?id=([0-9]+))|(?:https?:\/\/music\.163\.com\/playlist\/([0-9]+)\/.*)/);
        const id = matchResult[1] || matchResult[2];
        return getSheetMusicById(id);
    }

    async function getTopLists() {
        const res = await axios.get('https://music.163.com/discover/toplist', {
            headers: {
                referer: 'https://music.163.com/',
                'user-agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.54"
            }
        });
        const $ = cheerio.load(res.data);
        const children = $('.n-minelst').children();
        const groups = [];
        let currentGroup = {};
        for (let c of children) {
            if (c.tagName == 'h2') {
                if (currentGroup.title) {
                    groups.push(currentGroup);
                }
                currentGroup = {};
                currentGroup.title = $(c).text();
                currentGroup.data = [];
            } else if (c.tagName === 'ul') {
                let sections = $(c).children();
                currentGroup.data = sections.map((index, element) => {
                    const ele = $(element);
                    const id = ele.attr('data-res-id');
                    const coverImg = ele.find('img').attr('src');
                    const title = ele.find('p.name').text();
                    const description = ele.find('p.s-fc4').text();

                    return {
                        id,
                        coverImg,
                        title,
                        description
                    }
                }).toArray()
            }
        }
        if (currentGroup.title) {
            groups.push(currentGroup);
        }

        return groups;

    }

    return {
        platform: '网易云',
        version: '0.0.7',
        srcUrl: 'https://gitee.com/maotoumao/MusicFreePlugins/raw/master/netease.js',
        cacheControl: 'no-store',
        async search(query, page, type) {
            if (type === 'music') {
                return await searchMusic(query, page);
            }
            if (type === 'album') {
                return await searchAlbum(query, page);
            }
            if (type === 'artist') {
                return await searchArtist(query, page);
            }
        },
        getMediaSource(musicItem, quality) {
            return {
                url: `https://music.163.com/song/media/outer/url?id=${musicItem.id}.mp3`
            };
        },
        getAlbumInfo,
        getLyric,
        getArtistWorks,
        importMusicSheet,
        getTopLists,
        getTopListDetail
    }
}


code:
function qianqian(packages) {
    const { axios, dayjs, CryptoJs } = packages;

    const pageSize = 20;

    const secret = '0b50b02fd0d73a9c4c8c3a781c30845f';
    function getSignedParams(e) {
        if ("[object Object]" !== Object.prototype.toString.call(e))
            throw new Error("The parameter of query must be a Object.");
        var t = Math.floor(Date.now() / 1e3);
        Object.assign(e, {
            timestamp: t
        });
        var n = Object.keys(e);
        n.sort();
        for (var r = "", i = 0; i < n.length; i++) {
            var s = n[i];
            r += (0 == i ? "" : "&") + s + "=" + e[s]
        }
        return {
            ...e,
            sign: CryptoJs.MD5(r += secret).toString(),
            timestamp: t
        }
    }

    const searchHeaders = {
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36',
        'referer': 'https://music.91q.com/',
        'from': 'web',
        'accept': 'application/json, text/plain, */*',
        'accept-encoding': 'gzip, deflate, br',
        'accept-language': 'zh-CN,zh;q=0.9'
    };

    function formatMusicItem(_) {
        return {
            id: _.id || _.assetId,
            artwork: _.pic,
            title: _.title,
            artist: (_.artist || []).map(ar => ar.name).join('、'),
            artistItems: (_.artist || []).map(formatArtistItem),
            album: _.albumTitle,
            lrc: _.lyric
        }
    }

    function formatAlbumItem(_) {
        return {
            id: _.albumAssetCode,
            artist: (_.artist || []).map(ar => ar.name).join('、'),
            title: _.title,
            artwork: _.pic,
            description: '',
            date: dayjs(_.releaseDate).format('YYYY-MM-DD'),
        }
    }

    function formatArtistItem(_) {
        return {
            name: _.name,
            id: _.artistCode,
            avatar: _.pic,
            worksNum: _.trackTotal,
            description: _.introduce,
        }
    }

    function musicCanPlayFilter(_) {
        return !_.pay_model;
    }

    async function searchBase(query, page, type) {
        const res = (await axios.get('https://music.91q.com/v1/search', {
            headers: searchHeaders,
            params: getSignedParams({
                appid: '16073360',
                type,
                word: query,
                pageNo: page,
                pageSize
            })
        }));

        return res.data;
    }

    async function searchMusic(query, page) {
        const res = await searchBase(query, page, 1);

        return {
            isEnd: res.data.total <= page * pageSize,
            data: res.data.typeTrack.filter(musicCanPlayFilter).map(formatMusicItem)
        }
    }

    async function searchAlbum(query, page) {
        const res = await searchBase(query, page, 3);

        return {
            isEnd: res.data.total <= page * pageSize,
            data: res.data.typeAlbum.map(formatAlbumItem)
        }
    }

    async function searchArtist(query, page) {
        const res = await searchBase(query, page, 2);

        return {
            isEnd: res.data.total <= page * pageSize,
            data: res.data.typeArtist.map(formatArtistItem)
        }
    }

    async function getArtistMusicWorks(artistItem, page) {
        const headers = {
            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36',
            'referer': `https://music.91q.com/search?word=${encodeURIComponent(artistItem.name)}`,
            'from': 'web',
            'accept': 'application/json, text/plain, */*',
            'accept-encoding': 'gzip, deflate, br',
            'accept-language': 'zh-CN,zh;q=0.9'
        };
        const res = (await axios.get('https://music.91q.com/v1/artist/song', {
            headers,
            params: getSignedParams({
                appid: '16073360',
                artistCode: artistItem.id,
                pageNo: page,
                pageSize
            })
        })).data;

        return {
            isEnd: res.data.total <= page * pageSize,
            data: res.data.result.filter(musicCanPlayFilter).map(formatMusicItem)
        }
    }

    async function getArtistAlbumWorks(artistItem, page) {
        const headers = {
            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36',
            'referer': `https://music.91q.com/search?word=${encodeURIComponent(artistItem.name)}`,
            'from': 'web',
            'accept': 'application/json, text/plain, */*',
            'accept-encoding': 'gzip, deflate, br',
            'accept-language': 'zh-CN,zh;q=0.9'
        };
        const res = (await axios.get('https://music.91q.com/v1/artist/album', {
            headers,
            params: getSignedParams({
                appid: '16073360',
                artistCode: artistItem.id,
                pageNo: page,
                pageSize
            })
        })).data;

        return {
            isEnd: res.data.total <= page * pageSize,
            data: res.data.result.map(formatAlbumItem)
        }
    }

    async function getArtistWorks(artistItem, page, type) {
        if (type === 'music') {
            return getArtistMusicWorks(artistItem, page);
        } else if (type === 'album') {
            return getArtistAlbumWorks(artistItem, page);
        }
    }

    async function getLyric(musicItem) {
        return {
            lrc: musicItem.lrc
        }
    }

    async function getAlbumInfo(albumItem) {
        if (albumItem.musicList) {
            return albumItem;
        } else {
            const headers = {
                'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36',
                'referer': `https://music.91q.com/search?word=${encodeURIComponent(albumItem.name)}`,
                'from': 'web',
                'accept': 'application/json, text/plain, */*',
                'accept-encoding': 'gzip, deflate, br',
                'accept-language': 'zh-CN,zh;q=0.9'
            };
            const res = (await axios.get('https://music.91q.com/v1/album/info', {
                headers,
                params: getSignedParams({
                    appid: '16073360',
                    albumAssetCode: albumItem.id,
                })
            }));
            const musicList = (res.data.data.trackList || []).filter(musicCanPlayFilter).map(_ => ({
                ...formatMusicItem(_),
                artwork: albumItem.artwork,
                album: albumItem.name
            }))
            return {
                ...albumItem,
                musicList
            }
        }
    }

    return {
        platform: '千千音乐',
        version: '0.0.0',
        srcUrl: 'https://gitee.com/maotoumao/MusicFreePlugins/raw/master/qianqian.js',
        cacheControl: 'no-cache',
        async search(query, page, type) {
            if (type === 'music') {
                return await searchMusic(query, page);
            }
            if (type === 'album') {
                return await searchAlbum(query, page);
            }
            if (type === 'artist') {
                return await searchArtist(query, page);
            }
        },
        async getMediaSource(musicItem) {

            const headers = {
                'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1',
                'referer': `https://music.91q.com/artist/${(musicItem.artistItems[0] || {}).id}`,
                'from': 'webapp_music',
                'accept': 'application/json, text/plain, */*',
                'accept-encoding': 'gzip, deflate, br',
                'accept-language': 'zh-CN,zh;q=0.9'
            }
            const res = (await axios.get('https://music.91q.com/v1/song/tracklink', {
                headers,
                params: getSignedParams({
                    appid: '16073360',
                    TSID: musicItem.id,
                })
            })).data;

            return {
                url: res.data.path
            };
        },
        getAlbumInfo,
        getLyric,
        getArtistWorks

    }
}


code:
function qmkg(packages) {
    const { axios, cheerio } = packages;

    async function parseMusicItemFromUrl(shareUrl) {
        const html = (await axios.get(shareUrl)).data;
        const $ = cheerio.load(html);
        const script = $("script:contains('window.__DATA__')").text();
        const jsonStr = script.match(/window\.__DATA__ \=(.+);\s*$/);
        if (jsonStr) {
            const result = JSON.parse(jsonStr[1]);
            const musicItem = {
                id: result.detail.ksong_mid,
                shareid: result.shareid,
                lrc: result.lyric,
                artwork: result.detail.cover,
                title: result.detail.song_name,
                artist: `${result.detail.nick} (原唱: ${result.detail.singer_name})`,
                album: result.detail.content,
                url: result.detail.playurl,
                detail: result.detail
            }
            return musicItem;
        }
    }

    return {
        platform: '全民K歌',
        version: '0.0.0',
        srcUrl: 'https://gitee.com/maotoumao/MusicFreePlugins/raw/master/qmkg.js',
        cacheControl: 'no-store',
        async getMediaSource(musicItem) {
            if (musicItem.shareid) {
                const newItem = await parseMusicItemFromUrl(`https://kg.qq.com/node/play?s=${musicItem.shareid}`);
                return {
                    url: newItem.url,
                }
            }
            return {
                url: musicItem.url,
            }
        },
        async importMusicItem(shareUrl) {
            return parseMusicItemFromUrl(shareUrl);
        }
    }

}


code:
function qq(packages) {
    const { axios, CryptoJs } = packages;

    const pageSize = 20;

    function formatMusicItem(_) {
        const albumid = _.albumid || (_.album || {}).id;
        const albummid = _.albummid || (_.album || {}).mid;
        const albumname = _.albumname || (_.album || {}).title;
        return {
            id: _.id || _.songid,
            songmid: _.mid || _.songmid,
            title: _.title || _.songname,
            artist: _.singer.map(s => s.name).join(', '),
            artwork: albummid ? `https://y.gtimg.cn/music/photo_new/T002R300x300M000${albummid}.jpg` : undefined,
            album: albumname,
            lrc: _.lyric || undefined,
            albumid: albumid,
            albummid: albummid,
        }
    }

    function formatAlbumItem(_) {
        return {
            id: _.albumID || _.albumid,
            albumMID: _.albumMID || _.album_mid,
            title: _.albumName || _.album_name,
            artwork: _.albumPic || `https://y.gtimg.cn/music/photo_new/T002R300x300M000${_.albumMID || _.album_mid}.jpg`,
            date: _.publicTime || _.pub_time,
            singerID: _.singerID || _.singer_id,
            artist: _.singerName || _.singer_name,
            singerMID: _.singerMID || _.singer_mid,
            description: _.desc
        }
    }

    function formatArtistItem(_) {
        return {
            name: _.singerName,
            id: _.singerID,
            singerMID: _.singerMID,
            avatar: _.singerPic,
            worksNum: _.songNum
        }
    }

    const searchTypeMap = {
        0: 'song',
        2: 'album',
        1: 'singer',
        3: 'songlist',
        7: 'lyric',
        12: 'mv',
    };

    const headers = {
        referer: 'https://y.qq.com',
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36',
        Cookie: 'uin='
    }

    const validSongFilter = (item) => {
        return item.pay.pay_play === 0 || item.pay.payplay === 0
    }

    async function searchBase(query, page, type) {

        const res = (await axios({
            "url": "https://u.y.qq.com/cgi-bin/musicu.fcg",
            "method": "POST",
            "data": {
                "req_1": {
                    "method": "DoSearchForQQMusicDesktop",
                    "module": "music.search.SearchCgiService",
                    "param": {
                        "num_per_page": pageSize,
                        "page_num": page,
                        "query": query,
                        "search_type": type
                    }
                }
            },
            "headers": headers,
            "xsrfCookieName": "XSRF-TOKEN",
            "withCredentials": true
        }
        )).data;

        return {
            isEnd: res.req_1.data.meta.sum <= page * pageSize,
            data: res.req_1.data.body[searchTypeMap[type]].list
        }
    }

    async function searchMusic(query, page) {
        const songs = await searchBase(query, page, 0);

        return {
            isEnd: songs.isEnd,
            data: songs.data.filter(validSongFilter).map(formatMusicItem)
        }
    }

    async function searchAlbum(query, page) {
        const albums = await searchBase(query, page, 2);

        return {
            isEnd: albums.isEnd,
            data: albums.data.map(formatAlbumItem)
        }
    }

    async function searchArtist(query, page) {
        const artists = await searchBase(query, page, 1);

        return {
            isEnd: artists.isEnd,
            data: artists.data.map(formatArtistItem)
        }
    }

    function getQueryFromUrl(key, search) {
        try {
            const sArr = search.split('?');
            let s = '';
            if (sArr.length > 1) {
                s = sArr[1];
            } else {
                return key ? undefined : {};
            }
            const querys = s.split('&');
            const result = {};
            querys.forEach((item) => {
                const temp = item.split('=');
                result[temp[0]] = decodeURIComponent(temp[1]);
            });
            return key ? result[key] : result;
        } catch (err) {
            // 除去search为空等异常
            return key ? '' : {};
        }
    }

    // geturl
    function changeUrlQuery(obj, baseUrl) {
        const query = getQueryFromUrl(null, baseUrl);
        let url = baseUrl.split('?')[0];

        const newQuery = { ...query, ...obj };
        let queryArr = [];
        Object.keys(newQuery).forEach((key) => {
            if (newQuery[key] !== undefined && newQuery[key] !== '') {
                queryArr.push(`${key}=${encodeURIComponent(newQuery[key])}`);
            }
        });
        return `${url}?${queryArr.join('&')}`.replace(/\?$/, '');
    }

    const typeMap = {
        m4a: {
            s: 'C400',
            e: '.m4a',
        },
        128: {
            s: 'M500',
            e: '.mp3',
        },
        320: {
            s: 'M800',
            e: '.mp3',
        },
        ape: {
            s: 'A000',
            e: '.ape',
        },
        flac: {
            s: 'F000',
            e: '.flac',
        },
    };

    async function getSourceUrl(id, type = '128') {
        const mediaId = id;
        let uin = ''
        const guid = (Math.random() * 10000000).toFixed(0);
        const typeObj = typeMap[type];
        const file = `${typeObj.s}${id}${mediaId}${typeObj.e}`;
        const url = changeUrlQuery({
            '-': 'getplaysongvkey',
            g_tk: 5381,
            loginUin: uin,
            hostUin: 0,
            format: 'json',
            inCharset: 'utf8',
            outCharset: 'utf-8¬ice=0',
            platform: 'yqq.json',
            needNewCode: 0,
            data: JSON.stringify({
                req_0: {
                    module: 'vkey.GetVkeyServer',
                    method: 'CgiGetVkey',
                    param: {
                        filename: [file],
                        guid: guid,
                        songmid: [id],
                        songtype: [0],
                        uin: uin,
                        loginflag: 1,
                        platform: '20',
                    },
                },
                comm: {
                    uin: uin,
                    format: 'json',
                    ct: 19,
                    cv: 0,
                    authst: '',
                },
            }),
        }, 'https://u.y.qq.com/cgi-bin/musicu.fcg')
        return (await axios({
            method: 'GET',
            url: url,
            xsrfCookieName: 'XSRF-TOKEN',
            withCredentials: true
        })).data
    }

    async function getAlbumInfo(albumItem) {
        const url = changeUrlQuery({
            data: JSON.stringify({
                comm: {
                    ct: 24,
                    cv: 10000
                },
                albumSonglist: {
                    method: "GetAlbumSongList",
                    param: {
                        albumMid: albumItem.albumMID,
                        albumID: 0,
                        begin: 0,
                        num: 999,
                        order: 2
                    },
                    module: "music.musichallAlbum.AlbumSongList"
                }
            })
        }, 'https://u.y.qq.com/cgi-bin/musicu.fcg?g_tk=5381&format=json&inCharset=utf8&outCharset=utf-8');
        const res = (await axios({
            "url": url,
            "headers": headers,
            "xsrfCookieName": "XSRF-TOKEN",
            "withCredentials": true
        }
        )).data;
        return {
            ...albumItem,
            musicList: res.albumSonglist.data.songList.filter(_ => validSongFilter(_.songInfo)).map((item) => {
                const _ = item.songInfo;
                return formatMusicItem(_)
            })
        }
    }

    async function getArtistSongs(artistItem, page) {
        const url = changeUrlQuery({
            data: JSON.stringify({
                comm: {
                    ct: 24,
                    cv: 0
                },
                singer: {
                    method: "get_singer_detail_info",
                    param: {
                        sort: 5,
                        singermid: artistItem.singerMID,
                        sin: (page - 1) * pageSize,
                        num: pageSize,
                    },
                    module: "music.web_singer_info_svr"
                }
            })
        }, 'http://u.y.qq.com/cgi-bin/musicu.fcg')

        const res = (await axios({
            "url": url,
            "method": "get",
            "headers": headers,
            "xsrfCookieName": "XSRF-TOKEN",
            "withCredentials": true
        }
        )).data;
        return {
            isEnd: res.singer.data.total_song <= page * pageSize,
            data: res.singer.data.songlist.filter(validSongFilter).map(formatMusicItem)
        }
    }

    async function getArtistAlbums(artistItem, page) {
        const url = changeUrlQuery({
            data: JSON.stringify({
                comm: {
                    ct: 24,
                    cv: 0
                },
                singerAlbum: {
                    method: "get_singer_album",
                    param: {
                        singermid: artistItem.singerMID,
                        order: "time",
                        begin: (page - 1) * pageSize,
                        num: pageSize / 1,
                        exstatus: 1
                    },
                    module: "music.web_singer_info_svr"
                }
            })
        }, 'http://u.y.qq.com/cgi-bin/musicu.fcg');
        const res = (await axios({
            url,
            "method": "get",
            "headers": headers,
            "xsrfCookieName": "XSRF-TOKEN",
            "withCredentials": true
        }
        )).data;
        return {
            isEnd: res.singerAlbum.data.total <= page * pageSize,
            data: res.singerAlbum.data.list.map(formatAlbumItem)
        }
    }

    async function getArtistWorks(artistItem, page, type) {
        if (type === 'music') {
            return getArtistSongs(artistItem, page);
        }
        if (type === 'album') {
            return getArtistAlbums(artistItem, page);
        }

    }

    async function getLyric(musicItem) {
        const result = (await axios({
            "url": `http://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg?songmid=${musicItem.songmid}&pcachetime=${new Date().getTime()}&g_tk=5381&loginUin=0&hostUin=0&inCharset=utf8&outCharset=utf-8&notice=0&platform=yqq&needNewCode=0`,
            "headers": { "Referer": "https://y.qq.com", "Cookie": "uin=" },
            "method": "get",
            "xsrfCookieName": "XSRF-TOKEN",
            "withCredentials": true
        }
        )).data;

        const res = JSON.parse(result.replace(/callback\(|MusicJsonCallback\(|jsonCallback\(|\)$/g, ''))

        return {
            rawLrc: CryptoJs.enc.Base64.parse(res.lyric).toString(CryptoJs.enc.Utf8)
        }
    }

    async function importMusicSheet(urlLike) {
        //
        let id;
        if (!id) {
            id = (urlLike.match(/https?:\/\/i\.y\.qq\.com\/n2\/m\/share\/details\/taoge\.html\?.*id=([0-9]+)/) || [])[1];
        }
        if (!id) {
            id = (urlLike.match(/https?:\/\/y\.qq\.com\/n\/ryqq\/playlist\/([0-9]+)/) || [])[1];
        }
        if (!id) {
            return;
        }

        const result = (await axios({
            "url": `http://c.y.qq.com/qzone/fcg-bin/fcg_ucc_getcdinfo_byids_cp.fcg?type=1&utf8=1&disstid=${id}&loginUin=0`,
            "headers": { "Referer": "https://y.qq.com/n/yqq/playlist", "Cookie": "uin=" },
            "method": "get",
            "xsrfCookieName": "XSRF-TOKEN",
            "withCredentials": true
        }
        )).data;
        const res = JSON.parse(result.replace(/callback\(|MusicJsonCallback\(|jsonCallback\(|\)$/g, ''))
        console.log(res);
        return res.cdlist[0].songlist.filter(validSongFilter).map(formatMusicItem);
    }

    // 接口参考：https://jsososo.github.io/QQMusicApi/#/
    return {
        platform: 'QQ音乐',
        version: '0.0.2',
        srcUrl: 'https://gitee.com/maotoumao/MusicFreePlugins/raw/master/qq.js',
        cacheControl: 'no-cache',
        async search(query, page, type) {
            if (type === 'music') {
                return await searchMusic(query, page);
            }
            if (type === 'album') {
                return await searchAlbum(query, page);
            }
            if (type === 'artist') {
                return await searchArtist(query, page);
            }
        },
        async getMediaSource(musicItem) {
            let purl = '';
            let domain = '';
            const result = await getSourceUrl(musicItem.songmid);
            if (result.req_0 && result.req_0.data && result.req_0.data.midurlinfo) {
                purl = result.req_0.data.midurlinfo[0].purl;
            }
            if (domain === '') {
                domain =
                    result.req_0.data.sip.find(i => !i.startsWith('http://ws')) ||
                    result.req_0.data.sip[0];
            }

            return {
                url: `${domain}${purl}`
            };
        },
        getLyric,
        getAlbumInfo,
        getArtistWorks,
        importMusicSheet
    }
}


code:
# No binary files!

*.dll
*.exe
*.sys

# Includes *.bak etc
UnityPlayer.dll.*

# dxvk cache and debug files
*.dxvk-cache
*.log

# version control
*.diff
*.patch

# Script outputs
updater/gi_patch_*

repo_name: ElecDashi/dawn

path: .gitignore


code:
GAME VERSION 4.7.0 INFORMATION

Refer to the main README.md file for setup instructions.

patch.sh                 : launcher.bat setup and domain blocking
patch_anti_logincrash.sh : Obsolete
patch_revert.sh          : Reverts all patch changes (including old patch data)
patch_files/*            : Files required by the patch script


code:
GAME VERSION 4.8.0 INFORMATION

Refer to the main README.md file for setup instructions.

patch.sh                 : launcher.bat setup and domain blocking
patch_anti_logincrash.sh : Obsolete
patch_revert.sh          : Reverts all patch changes (including old patch data)
patch_files/*            : Files required by the patch script


code:
#!/usr/bin/env bash

echo "Please use the newest revert script."
echo "It will also work on older patch installations."

exit 1


code:
GAME VERSION 5.0.0 INFORMATION

Refer to the main README.md file for setup instructions.

patch.sh                 : launcher.bat setup and domain blocking
patch_anti_logincrash.sh : Obsolete
patch_revert.sh          : Reverts all patch changes (including old patch data)
patch_files/*            : Files required by the patch script


code:
#!/usr/bin/env bash

# DESCRIPTION
#     This is the main patch script to ensure basic game functionality in Wine.
#
# EXIT STATUS
#     0      : The patch was applied successfully.
#     other  : Patch failure. See stdout or stderr output for details.

# Templates for the next patch testing phase
#echo " === !! UNTESTED PATCH. CHECK FOR BANS USING A TRASH ACCOUNT !! ==="
#echo " ===      REPORT RESULTS TO THE MAIN REPOSITORY. THANK YOU.     ==="
#echo ""
#echo "If you would like to test this patch, modify this script and remove the line below this one."
#exit 1

echo "[NOTE] As of game version 3.7.0, it is assumed that binary patches are no longer"
echo "       needed for upcoming game versions. If you face issues while starting the"
echo "       game or logging in, please consult TROUBLESHOOTING.md for workarounds."
echo ""

# MacOS and *BSD do not have md5sum: use md5 instead
if [[ $(uname) == "Darwin" || $(uname) == *"BSD" ]]; then
	md5sum() {
		md5 -q $@
	}
fi

DIR=$(dirname "${BASH_SOURCE[0]}")
DATADIR=$(find . -maxdepth 1 -type d -name "*_Data")
FILE="UnityPlayer.dll"
sum=($(md5sum $FILE))
reltype="" # os, cn, bb
block_analytics=1

# original hashes
# TODO: Switch back to file hashes on occasion
if [ -e "GenshinImpact.exe" ]; then
	reltype="os"
	echo "--- Applying for: International version"
fi
if [ -e "YuanShen.exe" ]; then
	reltype="bb"
	echo "--- Applying for: bilibili/miHoYo CN version"
fi
if [ -z "$reltype" ]; then
	# The patch might corrupt invalid/outdated files if this check is skippd.
	echo "[ERROR] Wrong file version or the patch is already applied"
	echo " -> md5sum: ${sum}" && exit 1
fi

# =========== DO NOT REMOVE START ===========
if [[ -e "$DIR/$FILE" ]]; then
	# There is a good reason for this check. Do not pollute the game directory.
	echo "[ERROR] Invalid patch download directory. Please move all"
	echo "        patch files outside the game directory prior executing."
	echo " -> See README.md for proper installation instructions" && exit 1
fi
# ===========  DO NOT REMOVE END  ===========

echo
echo "--- Setting up blocked servers"
# START OF SUDO DANGER ZONE
etc_hosts="$(cat /etc/hosts)"

# See dev_tools/network.md (up-to-date as of 5.0.0)
declare -a domains
if [[ "$reltype" == "os" ]]; then
	domains+=("overseauspider.yuanshen.com")
	domains+=("osuspider.yuanshen.com")
	domains+=("ys-log-upload-os.hoyoverse.com")
	domains+=("sg-public-data-api.hoyoverse.com")
	domains+=("sdk-log-upload-os.hoyoverse.com")
else
	# Possibly outdated as of 4.6.0
	domains+=("log-upload.mihoyo.com")
	domains+=("uspider.yuanshen.com")
	domains+=("ys-log-upload.mihoyo.com")
	domains+=("sdk-log-upload.mihoyo.com")
fi

servers="# Genshin logging servers (do not remove!)"
for i in "${domains[@]}"; do
	servers=$(echo -e "${servers}\n0.0.0.0 $i")
done

if [[ ! "$etc_hosts" == *"$servers"* ]]; then
	echo "[Optional] Block analytics domains via /etc/hosts"
	echo "           The game collects uniquely identifiable information about your system."
	echo "           For details, refer to the official privacy page or the file"
	echo "           '../static/analytics_data.md' contained in this repository."
	echo "$servers"
	read -r -p "Would you like to block the analytics servers above? [Y/n]: " choice
	if [[ ! "$choice" == [Nn0]* ]]; then
		echo "-- Adding analytics servers"
		echo "$servers" | sudo tee -a /etc/hosts
		if [ $? -ne 0 ]; then
			read -p "--- Failed. Please edit /etc/hosts manually. Enter to continue."
		fi
	else
		block_analytics=0
	fi
else
	echo "--- Analytics servers are already blocked. Skip."
fi

# "Fix" to allow start-up 1 week *before* the next update. See issue #471
bb_domain="dispatchcnglobal.yuanshen.com"
servers=$(cat <<EOF
# Workaround for BiliBili startup issue
0.0.0.0 ${bb_domain}

EOF
)
if [[ $reltype == "bb" ]] && [[ ! "$etc_hosts" == *"$servers"* ]]; then
	echo "[Info] Detected BiliBili variant. It is likely that the game will no longer start"
	echo "       1 week before the next update. Would you like to block the following domain"
	echo "       with /etc/hosts as a workaround? If not, cancel this step using Ctrl+C."

	echo "$servers"
	echo "$servers" | sudo tee -a /etc/hosts
	if [ $? -eq 0 ]; then
		domains+=($bb_domain)
	else
		echo "--- Cancelled. Domain not added."
	fi
fi

etc_hosts=""
# END OF SUDO DANGER ZONE

# ===========================================================

echo "--- Adding launcher script"
cp -f "$DIR/patch_files/launcher.bat" .
if [ "$block_analytics" -eq 0 ]; then
	# Remove the lines related to domain checking
	sed -i "/REM DOMAIN_CHECK_BEGIN/,/REM DOMAIN_CHECK_END/d" "launcher.bat"
fi
if [[ "$reltype" != "os" ]]; then
	# Same thing but different
	sed -i "s/GenshinImpact/YuanShen/g" "launcher.bat"
fi

n=0
placeholder="UNKNOWN_DOMAIN"
while true; do
	[ "$n" -eq "${#domains[@]}" ] && break

	grep --quiet "$placeholder" -- "launcher.bat"
	if [ $? -eq 1 ]; then
		# TODO: There are currently only 5 placeholders
		echo "[ERROR] Domain blocking code template is too short."
		break
	fi

	sed -i "1,// s/${placeholder}/${domains[${n}]}/" "launcher.bat"
	n=$((n + 1))
done

sed -i "/REM SCRIPT_SANITY_BEGIN/,/REM SCRIPT_SANITY_END/d" "launcher.bat"

# Done!
echo "==> Patch applied! Enjoy the game."
echo
echo "[NOTICE] Please refrain from sharing this project in public. Thank you."

exit 0


code:
#!/usr/bin/env bash

# DESCRIPTION
#     Legacy file. Kept for script compatibility.

echo "[INFO] This patch is obsolete as of game version 3.8.0."
exit 0


code:
@echo off

REM Notice: This file is overwritten for each patch for safety reasons.
REM         Hence, any manual changes will be overwritten by the next patch.

REM ============ AVOID CHANGES HERE ============

REM SCRIPT_SANITY_BEGIN
echo ERROR: This file needs postprocessing. Run the script to generate it.
exit
REM SCRIPT_SANITY_END
REM DOMAIN_CHECK_BEGIN
SET perr1=1
SET perr2=1
SET perr3=1
SET perr4=1
SET perr5=1

setlocal EnableDelayedExpansion
FOR /f "delims=" %%i IN ('ping -n 1 -w 1 UNKNOWN_DOMAIN') DO (
	echo %%i | find "[0.0.0.0]" >nul
	IF !ERRORLEVEL! EQU 0 SET "perr1=0"
	echo %%i | find "could not find host" >nul
	IF !ERRORLEVEL! EQU 0 SET "perr1=0"
)
FOR /f "delims=" %%i IN ('ping -n 1 -w 1 UNKNOWN_DOMAIN') DO (
	echo %%i | find "[0.0.0.0]" >nul
	IF !ERRORLEVEL! EQU 0 SET "perr2=0"
	echo %%i | find "could not find host" >nul
	IF !ERRORLEVEL! EQU 0 SET "perr2=0"
)
FOR /f "delims=" %%i IN ('ping -n 1 -w 1 UNKNOWN_DOMAIN') DO (
	echo %%i | find "[0.0.0.0]" >nul
	IF !ERRORLEVEL! EQU 0 SET "perr3=0"
	echo %%i | find "could not find host" >nul
	IF !ERRORLEVEL! EQU 0 SET "perr3=0"
)
FOR /f "delims=" %%i IN ('ping -n 1 -w 1 UNKNOWN_DOMAIN') DO (
	echo %%i | find "[0.0.0.0]" >nul
	IF !ERRORLEVEL! EQU 0 SET "perr4=0"
	echo %%i | find "could not find host" >nul
	IF !ERRORLEVEL! EQU 0 SET "perr4=0"
)
FOR /f "delims=" %%i IN ('ping -n 1 -w 1 UNKNOWN_DOMAIN') DO (
	echo %%i | find "[0.0.0.0]" >nul
	IF !ERRORLEVEL! EQU 0 SET "perr5=0"
	echo %%i | find "could not find host" >nul
	IF !ERRORLEVEL! EQU 0 SET "perr5=0"
)

REM There's no OR operator for "IF"
SET perr=0
IF %perr1% EQU 1 SET perr=1
IF %perr2% EQU 1 SET perr=1
IF %perr3% EQU 1 SET perr=1
IF %perr4% EQU 1 SET perr=1
IF %perr5% EQU 1 SET perr=1

IF %perr% NEQ 0 (
	REM Show the message to the user
	echo ERROR: You opted to block the analytics servers but they are reachable. Please re-run the patch script. >_error.txt
	notepad _error.txt
	del _error.txt
	exit
)
REM DOMAIN_CHECK_END

REM ============= Launch the game =============
REM https://docs.unity3d.com/Manual/PlayerCommandLineArguments.html
REM Append the arguments to the command: launcher.bat arg1 arg2 ...

start /wait GenshinImpact.exe %*


code:
#!/usr/bin/env bash

ANCHOR=""
DATADIR=$(find . -maxdepth 1 -type d -name "*_Data")

if [ -e "GenshinImpact.exe" ]; then
	ANCHOR="GenshinImpact.exe"
fi
if [ -e "YuanShen.exe" ]; then
	ANCHOR="YuanShen.exe"
fi
if [ -z "$ANCHOR" ]; then
	echo "==> Game executable not found. Wrong directory?"
	exit 1
fi

# Restore files that have a backup
UPDLL="UnityPlayer.dll"
CEXE1="$DATADIR/upload_crash.exe"
CEXE2="$DATADIR/Plugins/crashreport.exe"
VULKAN1="$DATADIR/Plugins/vulkan-1.dll"
GGM="$DATADIR/globalgamemanagers"

# macOS has a different version of stat
if [ $(uname) = "Darwin" ]; then
	STATFLAGS="-f %a"
else
	STATFLAGS="-c %Y"
fi

# Reference timestamp to avoid restoring old/invalid backups
ref_timestamp=($(stat $STATFLAGS "$ANCHOR"))

restore_file() {
	# $1 -> File name
	# $2 -> Human readable description
	# $3 -> Overwrite existing file?

	if [ ! -e "$1.bak" ]; then
		echo "--- Nothing to restore for '$1' (no .bak file)"
		return 0
	fi

	if [ -e "$1" -a "$3" != "overwrite" ]; then
		echo "--- $2 is already restored"
		return 0
	fi

	# Do not restore backups from old game versions
	difftime=$(( $ref_timestamp - $(stat $STATFLAGS "$1.bak") ))
	# Strip negative sign is equal to abs()
	if [ ${difftime#-} -gt 3600 ]; then
		echo "--- Backup '$1.bak' is older than the current game version. Ignoring."
		return 1
	fi

	# Restore from backup
	mv -f "$1.bak" "$1"
	echo "--- Restored: $2"
	return 0
}

restore_file "$UPDLL" "Main patch" overwrite
restore_file "$VULKAN1" "Workaround for Wine bug 45277"
restore_file "$CEXE1" "Crash reporter 1"
restore_file "$CEXE2" "Crash reporter 2"
restore_file "mhypbase.dll" "'Blackbox' DLL"
restore_file "$GGM" "macOS rendering issue patch" overwrite

echo
echo "--> Removing all newly added files"
# The error messages for inexistent files are intended
rm "launcher.bat"
rm "mhyprot2_running.reg"

# dxvk files
rm *.dxvk-cache
rm *_d3d9.log
rm *_d3d11.log
rm *_dxgi.log

echo "==> Patch reverted."
exit 0


code:
## Credits

Original research: https://www.playonlinux.com/en/app-4228.html

*If you are on this list but do not agree with the license, please open an issue or contact me directly.*

List ordered by first contribution in the project.

User Krock ([PlayOnLinux](https://www.playonlinux.com/en/profil-95230.html), NotABug)

 * Initial Ghidra experiments, self-declared project leader

User timbuntu ([PlayOnLinux](https://www.playonlinux.com/de/profil-95714.html), NotABug)

 * First success on version 1.0.1
 * MITM logs

User geearf2 (Freenode IRC)

 * Providing game data from 25 Oct

User SeppNel ([PlayOnLinux](https://www.playonlinux.com/en/profil-95643.html), NotABug)

 * Windows/Linux comparisons on the same system
 * Network and xLua logs
 * Hash information from a working system

User 0x90 ([PlayOnLinux](https://www.playonlinux.com/en/profil-96196.html))

 * Research about in-memory modifications
 * x64dbg instructions

User Th1nkCh3ck (NotABug)

 * General information research

User humanik12 (unknowncheats)

 * Damn, you dude rock. I almost gave up, but you gave me the final hint and motivation.

User Makksim (NotABug)

 * Summary of the mhyprot2 service behaviour

User tiagop (NotABug, Email)

 * Information collecting about Wine projects

User Kowalski (NotABug)

 * .pdb symbols file

User WerWolv (Discord)

 * Assistance understanding the WinAPI and internals

Various (unknowncheats.me)

 * General game information

User loentar (NotABug)

 * Game updater and patch script

User yagocl (NotABug)

 * Documentation improvements and new instructions

User y0soro (NotABug)

 * Patch for the Chinese game version

User 3Shain (NotABug)

* Investigating macOS support.


code:
# Frequently Asked Questions

Q: (obsolete) Can I get banned if I use this patch?
A: Since version 4.0.0, the main patch script no longer modifies any game files
and can be considered safe.

Q: Why does the game create a `driverError.log` file?
A: The kernel service used for cheat prevention cannot be loaded into Wine/Proton
because it is not an operating system, but an userspace ABI for Windows executables.

Q: How can I analyse my gacha wishes?
A: Obtain the URL from the in-game browser cache.

 1. Open the in-game wish history (table view)
 2. Extract the URL using the script [compat/get_gacha_url.sh](compat/get_gacha_url.sh)
 3. Use the URL on websites like paimon.moe / genshin-wishes.com / etc

Q: How do I skip the download of unused voice-over packs?
A: Either remove them in your game settings, or:

 1. Locate the file `audio_lang_*` (where `*` stands for the newest) inside your game data files
 2. Remove the unwanted pack lines (except maybe `English(US)`?)
 3. Save
 4. Navigate to `StreamingAssets/AudioAssets/`
 5. Remove the pack directories that are no longer contained within `audio_lang_*`
 6. Proceed to download the other game data archives

Q: Where is `output_log.txt`? (game log and crash backtrace)
A: `$WINEPREFIX/drive_c/USERNAME/AppData/LocalLow/miHoYo/Genshin Impact/` where `$WINEPREFIX`
depends on the Wine setup/configuration.

Q: Where can I find crash information?
A: A `crash.dmp` minidump and `error.log` file are saved to `$WINEPREFIX/drive_c/USERNAME/Temp/mihoyocrash_*`.
Former can partially be analysed using `winedbg` or `windbg` depending on the progress of Wine.
See also: [TROUBLESHOOTING.md](TROUBLESHOOTING.md#how-to-report-issues)

Q: Where is `$WINEPREFIX` by default?

 * System-wide Wine installation: `~/.wine`
 * Lutris: `~/Games`
 * Steam: in `steamapps`, usually `~/.steam/steam/steamapps/compatdata/ANYNUMBER`


code:
# Game installation instructions

Minimal requirements:

 * Vulkan-capable graphics card (["How to check?"](TROUBLESHOOTING.md#graphics-bugsissues))
     * For older GPUs: try WineD3D. This will not result in a good gaming experience.
 * Any installation of Wine 7.13 (or newer)
     * Older versions will leak "Wine" in the device information
     * When using the official launcher, Wine 8.7 (or newer) is highly recommended
 * MS core fonts
     * Ubuntu/Debian: install `ttf-mscorefonts-installer`
         * If the package dependency `update-notifier-common` is not wanted, the
           [Debian package](http://ftp.debian.org/debian/pool/contrib/m/msttcorefonts/) can be used.
     * Arch/Manjaro: install `ttf-ms-fonts`
     * Alternatively: winetricks `corefonts` verb (after wineprefix setup)

### Table of contents

* [Lutris](#lutris)
* [Steam](#steam)
* [SteamOS](#steamos)
* [Custom](#custom)
* [macOS instructions](#macos-instructions)
* [Bilibili](#bilibili)
* [Controller Configuration](#controller-configuration)

#### Lutris

1. Download the Windows installer from the official site
2. Add the installer binary to your library
3. Set up a wineprefix for the game
    * This is where the game will be installed. 40+ GiB of free space is highly recommended.
    * Grab a recent Wine runner. See [TROUBLESHOOTING](TROUBLESHOOTING.md) for problematic versions
4. Install the components the game needs to run flawlessly
    * Enable DXVK in `Configuration` -> `Runner options` (1.7.3 or newer is recommended)
    * Navigate to `Configuration -> Runner options` -> `DLL overrides` (table) and add the
      entry `avcodec-58.dll` with value `d`. This avoids crashes (see Issue #339)
5. Launch the downloaded installer .exe
    * Use the `Run EXE inside wine prefix` menu action for this.
    * The C++ Redistributable Package is optional. You may cancel it.
    * Download and install the game files.
    * **Once it is done, do not press launch.** Exit the launcher.
6. Apply the patch script: ["how?"](README.md)
7. Change the executable path. `Configuration` -> `Game options`
    * Executable: `launcher.bat`
    * Working directory: select the directory where `GenshinImpact.exe` resides
8. Launch the game

**Important note:**

After setup you might have multiple Wine versions and multiple WINEPREFIX directories
on your system. Hence running the `wine` command from your terminal will use another
environment than Lutris does.

To open a command prompt which uses the correct environment, do either:

 * Games -> Wine options (see bottom status bar or left side) -> `Wine console`
 * Change the `Executable` path to `cmd.exe`

#### Steam

1. Download the official launcher
2. "Add non-Steam game..." -> Browse -> Show all files -> Select the installer executable -> Done
3. Setup proton (if not done using global settings)
    * Navigate: Gear icon -> "Settings" -> "Compatibility"
    * Check "Force compatibility layer ..."
    * Select a recent Proton version
4. Install the launcher and start it
    * The C++ Redistributable Package is optional. You may cancel it.
    * Download and install the game files.
5. Apply the patch script: ["how?"](README.md)
6. Update the target. Gear icon -> "Settings":
    * Target: `explorer.exe`
    * Execution directory: the directory where `GenshinImpact.exe` resides
    * Start options: `%command% /desktop=anyname,1920x1080 cmd /c launcher.bat`
    * Adjust the screen resolution above, if necessary.
7. Play.
    * For CN version + Steam Deck: you might need to [add environment variables](TROUBLESHOOTING.md#game-crashes-on-launch) to start the game.

#### SteamOS

This section is based on the Steam instructions above.

1. The patches may modify `/etc/hosts` which is not writable by default.
     * Option 1: Use `sudo steamos-readonly disable` before running the main patch
       script and make the file system readonly again after completion: `sudo steamos-readonly enable`
         * Note: The changes done by the script might be undone by system updates.
     * Option 2: Allow the analytics domains in the patch script. Refer to [static/analytics_summary.txt](static/analytics_summary.txt)
       for an overview of the collected data.
     * Option 3: Use a semi-isolated container to avoid this problem such as Flatpak.
       For example, the third party launcher "An Anime Game Launcher"
       (see [updater/README.md](updater/README.md) for links) offers such package.
2. For Chinese/Japanese players: (to fix [startup crashes](TROUBLESHOOTING.md#game-crashes-on-launch))
     1. Run the script `compat/steamos_CJ_locale_enabler.sh` to generate the necessary locale
     2. Configure `LC_ALL` as described in [TROUBLESHOOTING.md](TROUBLESHOOTING.md#game-crashes-on-launch)

#### Custom

Script-based approach to use any Wine version on any WINEPREFIX.
If you do not understand what each step is supposed to do, use Lutris instead.

For the final game launch script after the prefix setup, use steps 1, 2 and 6.

	# 1. Specify the path to wine(64)
	#    This can be either system-wide or a custom installation
	WINE="wine64"
	#WINE="/path/to/custom/wine/build/bin/wine64"

	# 2. Specify a WINEPREFIX
	export WINEPREFIX="/absolute/path/to/.prefix/"

	# 3. Create or update WINEPREFIX
	"$WINE" wineboot -u

	# 4. Install the most recent DXVK version
	#    Use the newest Winetricks script from GitHub
	export WINE
	sh winetricks dxvk

	# 5. Install the game (if not already done)
	"$WINE" "/path/to/installer.exe"

	# 6. Game launch
	#    Using a virtual desktop with a size of 1920x1080 pixels
	cd "/path/to/Genshin Impact Game"
	export WINEESYNC=1  # requires wine-staging or Proton, see TWEAKS.md
	"$WINE" explorer /desktop=anyname,1920x1080 cmd /c launcher.bat

	# 7. Game update
	#    See `updater/README.md` or attempt to use the official launcher:
	"$WINE" "/path/to/official/launcher.exe"

#### macOS instructions

It's highly recommended to use CrossOver using a recent [DXVK-macOS](https://github.com/Gcenx/DXVK-macOS) version.

 * Note: Confirmed working with DXVK-macOS 1.10.3-47.

MoltenVK does yet not support `VK_EXT_transform_feedback` (see issue #374), which can be
worked around by the following patch. Check the script contents for details.

    bash "/path/to/dawn/compat/macos_cpu_skinning.sh"

To fix further rendering issues on low-end Macs, open or create the file `dxvk.conf` in the game
directory and add the following line: (requires a working DXVK installation)

    dxgi.customDeviceDesc = "AMD Radeon Pro 5300M"

 * Note: CrossOver might come with an old DXVK version which does not handle this option.
 * Note: Setting the device ID and vendor ID does not have any effect.

Confirm whether the changes are in effect:

1. Start the game
2. Open the `*_dxgi.log` file in the game directory
2. The line starting containing `DXVK:` must mention a recent version (e.g. 1.10.3)
3. The line below `Effective configuration:` must mention the GPU specified in `dxvk.conf`.
4. Visual test: compare the in-game graphics

#### Bilibili

**Chinese**

(feel free to translate / 随意翻译)

**English**

Wine Gecko 2.47.2 (and older) cannot render the in-game Bilibili login page.
Whereas the game comes with the Chromium Embedded Framework (CEF), the Bilibili
SDK does not make use of it. Instead it relies on the Internet Explorer libraries.

Known workaround: Check the "Links" section below.

Note: That project is not directly supported by the patch. Please report issues
and questions to its dedicated page.

**Links / 网页链接**

 * Workaround: https://github.com/QiE2035/gs_bili

#### Controller Configuration

*This section should not be documented in this repository.
If you know of a better public place for documentation, please open an issue
so that this information is available to more people.*

Only follow these instructions if the newest Wine version does not already support your controller.

##### Dualsense

*Source: lahvuun, Issue #216*

1. Open `regedit` in your WINEPREFIX
2. Navigate to `HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\winebus`
3. Create a new DWORD, value `0`, named `Enable SDL`
4. If there is yet no success: check the udev rule section below

Caveats: No force feedback or haptics (vibrations)

##### Other

*Source: Alex72, Issue #228*

General idea: specify the environment variable `SDL_GAMECONTROLLERCONFIG=?????`.

Helpful resources to determine the value (`?????`):

 * For permission-related issues: check the udev rule section below
 * General mapping information: [SDL_GameControllerDB](https://github.com/gabomdq/SDL_GameControllerDB)
 * [SDL2 controller test tool](https://github.com/libsdl-org/SDL/blob/main/test/controllermap.c)
    * Required packages: build tools and SDL2
       * Debian/Ubuntu: `libsdl2-dev`
    * Compile with `gcc -o controllermap controllermap.c $(sdl2-config --libs --cflags)`
    * If compiling fails, either:
       * Install a newer SDL2 library
       * or download an older `controllermap.c` file which matches your installed version
 * [SDL2 Gamepad Mapper](https://gitlab.com/ryochan7/sdl2-gamepad-mapper)
 * [AntiMicroX](https://github.com/AntiMicroX/antimicrox)

##### udev rule configuration

Some specific controllers might lack read permission.

 * If [game-devices-udev](https://gitlab.com/fabiscafe/game-devices-udev) lists your controller, install it.
    * Run `udevadm control --reload-rules` or reboot to apply the changes
 * Arch Wiki instructions for other controllers: [Gamepad](https://wiki.archlinux.org/title/Gamepad)

Maybe helpful: `wine control.exe` shows a simple controller/gamepad testing application.


code:
## Game Status v5.0.0

Scroll down for the setup instructions.

![Game is playable!](static/LOGO-IF-WORKING.jpg)
<!-- ![Work in progress](static/LOGO-IF-WIP.jpg) -->

**Supported versions:** International, China (channel 1 and 14)

Note: as reported in [Issue#401](https://notabug.org/Krock/dawn/issues/401), the CN game
servers are likely to prevent players on Linux and macOS from logging in for unknown reasons.

### Situation

Before game version 3.8.0, a patch was needed to work around the game's cheat prevention kernel service which is and will not be supported by Wine.
Since version 3.8.0 no additional patches are needed and the game is playable (almost) out of the box (🎉).
To get the best gaming experience please check the setup instructions below.

**Similar games. Format: link, caveats (Proton/Wine rating)**

 * [Scarlet Nexus](https://store.steampowered.com/app/775500/SCARLET_NEXUS/), singleplayer (Platinum)
 * [Little Witch Nobeta](https://store.steampowered.com/app/1049890/Little_Witch_Nobeta/), singleplayer (Gold)
 * [Ashen](https://store.steampowered.com/app/649950/Ashen/) (Platinum)
 * [Tower Hunter: Erza's Trial](https://store.steampowered.com/app/844850/Tower_Hunter_Erzas_Trial/), singleplayer, 2D scroller (Platinum)
 * [Haven](https://www.gog.com/game/haven), PEGI 18 (Gold)
 * [Pine](https://www.gog.com/game/pine), no story, singleplayer (Native)
 * ~~Tower of Fantasy~~ (Broken: anticheat)
 * ~~Wuthering Waves~~ (Broken: anticheat)

*This list is incomplete. <https://areweanticheatyet.com/> might provide information about specific titles.*

**Related compatibility projects**

 * [jadeite](https://codeberg.org/mkrsym1/jadeite): HI3 patch
     * Relevant 3rd party launcher: [Honkers Launcher](https://github.com/an-anime-team/honkers-launcher)
 * [jadeite](https://codeberg.org/mkrsym1/jadeite): HSR patch
     * Relevant 3rd party launcher: [The Honkers Railway Launcher](https://github.com/an-anime-team/the-honkers-railway-launcher)
 * Reverse: 1999. For compatibility and usage notes, see  [Issue#451](https://notabug.org/Krock/dawn/issues/451).
 * Punishing: Gray Raven
     * Compatible by using the modified "Reverse: 1999" binary. See [Issue#454](https://notabug.org/Krock/dawn/issues/454).
     * ~~[Borb Launcher](https://github.com/an-anime-team/an-anime-borb-launcher)~~ (no longer maintained)
     * Relevant 3rd party launcher: [Babylonia](https://github.com/ALEZ-DEV/Babylonia-terminal) (CLI)

### Project rules

 1. miHoYo reserves the right to distribute files. Please do not share any game data in public.
 2. For your own sake: **Please do not share this project in public**.
    * Reduces the risk of this project to be taken down or used against our reverse-engineering efforts
    * Solution: Use indirect/redirect links and private messages
 3. Please do not abuse this project for cheating. We just want to play the game.

For project contributors: Beware that your log files may contain personal information, such as MAC addresses or login data.

### [Game installation instructions](INSTALLATION.md)

Please check the [Game installation instructions](INSTALLATION.md) for the minimal
requirements and a guided installation of the game using Lutris, Steam, macOS or
custom scripts.

Afterwards, it is recommended to execute `patch.sh` so that `launcher.bat` is generated
within the game directory because all instructions assume its presence. Note: the main
patch script no longer modifies any game files and is considered harmless.

Follow the script as instructed. `GAMEVER` stands for the game version, i.e. `3.2.0` becomes `320`.

	# Download the repository (or use the download button)
	git clone https://notabug.org/Krock/dawn --depth 1

	# Tip: Drag&drop the desired directory or file into the terminal to copy its path

	cd "/path/to/Genshin Impact Game"
	bash "/path/to/dawn/GAMEVER/patch.sh"

	# Start the game after checking the domains
	wine cmd /c launcher.bat

To stay up-to-date with the blocked domains, it is recommended to re-run the "patch" script
after each game update.

**Undo**

Old patch residues can be removed by executing `patch_revert.sh` in the same manner
as `patch.sh` above. To remove the blocked logging servers, edit `/etc/hosts` with
superuser permissions.

### [Troubleshooting](TROUBLESHOOTING.md)

Please **do not report any** game issues to an official channel. Your options are:

 * [Troubleshooting](TROUBLESHOOTING.md) page for common issues and solutions
 * Create a [new issue](https://notabug.org/Krock/dawn/issues) here. See also: [closed/solved issues](https://notabug.org/Krock/dawn/issues?state=closed&labels=2029)
 * Post your problem on a Wine/dxvk-specific page

### [Frequently Asked Questions](FAQ.md)

### [Game updater & patch](updater/)

### [Performance & visual tweaks](TWEAKS.md)

### [Credits](CREDITS.md)

License: MIT