虽然网易云音乐已经推出了 linux 版,但是诸如本地歌曲扫描、心动模式等功能有所缺失,并且更新相对较慢,因此这里介绍一种使用 wine 完美运行网易云音乐的方法
目前兼容情况
- 主界面正常打开,可以账号密码登录、扫码登录、第三方登录,保存登录信息
- dock栏及托盘图标显示正常,托盘图标可以左右键点击
- 中文字体显示正常,无乱码zai
- 可以扫描磁盘上其他文件夹内的歌曲并识别,并且重启后无需重新识别
- MV可以正常播放,mini模式和桌面歌词均显示正常
目前已知兼容问题
- 由于上游软件 wine 的影响,wine 中运行的所有软件无法在非相同分辨率的第二屏中运行
具体效果图可以拖到最后,推荐在 deepin
系统中使用
Contents
安装 wine
开启 i386
支持
sudo dpkg --add-architecture i386
安装公钥
wget -nc https://dl.winehq.org/wine-builds/Release.key
wget -nc https://dl.winehq.org/wine-builds/winehq.key
sudo apt-key add Release.key
sudo apt-key add winehq.key
需要注意的是,wine 自 2018-12-19 更改了公钥,因此这里安装了两个
添加仓库
版本号 | 命令 |
---|---|
Ubuntu 18.10 | sudo apt-add-repository ‘deb [https://dl.winehq.org/wine-builds/ubuntu/](https://dl.winehq.org/wine-builds/ubuntu/) cosmic main’ |
Ubuntu 18.04 / Linux Mint 19.x | sudo apt-add-repository ‘deb [https://dl.winehq.org/wine-builds/ubuntu/](https://dl.winehq.org/wine-builds/ubuntu/) bionic main’ |
Ubuntu 16.04 / Linux Mint 18.x | sudo apt-add-repository ‘deb [https://dl.winehq.org/wine-builds/ubuntu/](https://dl.winehq.org/wine-builds/ubuntu/) xenial main’ |
Ubuntu 14.04 / Linux Mint 17.x | sudo apt-add-repository ‘deb [https://dl.winehq.org/wine-builds/ubuntu/](https://dl.winehq.org/wine-builds/ubuntu/) trusty main’ |
Debain 8 | echo “deb [https://dl.winehq.org/wine-builds/debian/](https://dl.winehq.org/wine-builds/debian/) jessie main” | sudo tee -a /etc/apt/sources.list.d/winehq.conf |
Debain 9 | echo “deb [https://dl.winehq.org/wine-builds/debian/](https://dl.winehq.org/wine-builds/debian/) stretch main” | sudo tee -a /etc/apt/sources.list.d/winehq.conf |
Debain 10 | echo “deb [https://dl.winehq.org/wine-builds/debian/](https://dl.winehq.org/wine-builds/debian/) buster main” | sudo tee -a /etc/apt/sources.list.d/winehq.conf |
更新仓库
sudo apt-get update
安装软件包
sudo apt install --install-recommends winehq-devel
当然也可以选择安装稳定版
sudo apt install --install-recommends winehq-stable
个人比较喜欢开发版
配置 wine
首次启动
winecfg
首次启动会创建容器,提示要求安装扩展包,按照提示安装即可
但是由于总所周知的原因,安装非常缓慢甚至会失败,但是这并不影响正常使用
修改 windows 版本
网易云音乐只能在 Windows XP 容器中运行
安装中文字体
你可以从 windows 文件夹中拷贝字体目录到 wine 目录中,也可以下载我从 windows 10 中 Fonts 文件夹内打好的字体包
sudo apt-get install tar
wget https://uploads.limstash.com/fonts.tar.gz
tar -xvzf fonts.tar.gz
rm -rf ~/.wine/Windows/Fonts
mv Fonts ~/.wine/Windows/Fonts
再次运行 winecfg
在 [显示] 选项卡中可以看到中文字体显示已经正常
关闭允许桌面管理器装饰窗口
运行 winecfg
在 [显示] 选项卡中取消勾选 [允许窗口管理器装饰窗口] 以修复网易云音乐最大化后超出屏幕边界的问题
调整屏幕分辨率
将屏幕分辨率调整至 105 以上,以修复托盘图标无法点击的问题(在屏幕分辨率大于一定值后即可点击,但不确定具体为多少,可以自行尝试)
设置语言编码
sudo cd /usr/bin
sudo mkdir wine-run
sudo mv wine wine-run
sudo vi wine
按下 i
写入
#!/bin/bash
export LANG=zh_CN.UTF-8
export LANGUAGE=zh_CN:zh
export LC_CTYPE="zh_CN.UTF-8"
export LC_NUMERIC="zh_CN.UTF-8"
export LC_TIME="zh_CN.UTF-8"
export LC_COLLATE="zh_CN.UTF-8"
export LC_MONETARY="zh_CN.UTF-8"
export LC_MESSAGES="zh_CN.UTF-8"
export LC_PAPER="zh_CN.UTF-8"
export LC_NAME="zh_CN.UTF-8"
export LC_ADDRESS="zh_CN.UTF-8"
export LC_TELEPHONE="zh_CN.UTF-8"
export LC_MEASUREMENT="zh_CN.UTF-8"
export LC_IDENTIFICATION="zh_CN.UTF-8"
export LC_ALL="zh_CN.UTF-8"
/usr/bin/wine-run/wine "$@"
然后按 Esc
退出编辑模式,按 ZZ
保存
如果在 Ubuntu
中使用 Fcitx
输入法可能会遇到中文输入法问题,请在文件末尾附加一段
export GTK_IM_MODULE=fcitx
export QT_IM_MODULE=fcitx
export XMODIFIERS=@im=fcitx
/usr/bin/wine-run/wine "$@"
赋予可执行权限
sudo chmod +x ./wine
字体平滑显示
安装 winetricks
wget https://raw.githubusercontent.com/Winetricks/winetricks/master/src/winetricks
sudo chmod +x ./winetricks
配置 fontsmooth=rgb
./winetricks settings fontsmooth=rgb
安装网易云音乐
下载安装包
wget https://d1.music.126.net/dmusic/cloudmusicsetup_2.5.2.197409.exe
wine cloudmusicsetup_2.5.2.197409.exe
当然也可以自己到官网下载最新的安装包,在这篇文章发出的时候安装包最新为 2.5.2
,使用
wine 安装包文件名
即可安装
禁用缩放
之前设置了屏幕分辨率以解决托盘图标问题,在 设置 > 常规 > 高清屏适配 中勾选 “禁用系统缩放比率”
更改字体
在 设置 > 字体 > 常规 中将字体更改为微软雅黑
黑边框及边框阴影
由于上游软件原因,会出现一个无法跟随移动的黑边框,在 deepin 开启窗口特效后体现为边框阴影
在启动软件时通过劫持边框窗口的方式可以去掉主程序的边框,但后期弹出的登录界面无法去除
当然登录一般情况下你只需要登录一次
sudo apt-get install g++ git libx11-dev
git clone https://gitee.com/limstash/wine_cloudmusic.git
cd wine_cloudmusic
g++ disable_cloudmusic_shadows.cpp -o disable_cloudmusic_shadows -lX11
cp disable_cloudmusic_shadows ~/.wine
那么每次运行网易云音乐后只需要再运行 “disable_cloudmusic_shadows` 即可关闭黑边框
vi ~/.local/share/applications/wine/Programs/网易云音乐.desktop
将其中的 Exec
项目改为
Exec=/bin/bash /home/你的用户名/.wine/start.sh
可以通过添加一行使启动项出现在音乐栏中
Categories=Music
新建一个启动脚本
vi ~/.wine/start.sh
写入
#!/bin/bash
env WINEPREFIX="/home/你的用户名/.wine" wine "C:\\Windows\\Command\\Start.exe" "C:\\Program Files (x86)\\Netease\\Cloudmusic\\cloudmusic.exe"
sleep 10
/home/你的用户名/.wine/disable_cloudmusic_shadows
赋予可执行权限
sudo chmod +x ~/.wine/start.sh
效果图
全屏显示
播放器与桌面歌词
识别已下载的歌曲
Mini 模式
登录
MV 播放
本文同步发布于 deepin
源码
/**
* https://www.cnblogs.com/Seiyagoo/p/3496945.html
* https://blog.csdn.net/ice__snow/article/details/79979298
* https://blog.csdn.net/lvbian/article/details/23031635
*
* This code is collected and generalized by hytzongxuan (hytzongxuan@gmail.com)
*/
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <sys/types.h>
#include <dirent.h>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <list>
#include <map>
#define BUF_SIZE 1024
using namespace std;
string PID[50];
int tot;
class WindowsMatchingPid
{
public:
WindowsMatchingPid(Display *display, Window wRoot, unsigned long pid)
: _display(display), _pid(pid)
{
_atomPID = XInternAtom(display, "_NET_WM_PID", True);
if (_atomPID == None)
{
cout << "No such atom" << endl;
return;
}
search(wRoot);
}
const list<Window> &result() const { return _result; }
private:
unsigned long _pid;
Atom _atomPID;
Display *_display;
list<Window> _result;
void search(Window w)
{
Atom type;
int format;
unsigned long nItems;
unsigned long bytesAfter;
unsigned char *propPID = 0;
if (Success == XGetWindowProperty(_display, w, _atomPID, 0, 1, False, XA_CARDINAL,
&type, &format, &nItems, &bytesAfter, &propPID))
{
if (propPID != 0)
{
if (_pid == *((unsigned long *)propPID))
_result.push_back(w);
XFree(propPID);
}
}
Window wRoot;
Window wParent;
Window *wChild;
unsigned nChildren;
if (0 != XQueryTree(_display, w, &wRoot, &wParent, &wChild, &nChildren))
{
for (unsigned i = 0; i < nChildren; i++)
search(wChild[i]);
}
}
};
void getPidByName(char *task_name)
{
DIR *dir;
struct dirent *ptr;
FILE *fp;
char filepath[50];
char cur_task_name[50];
char buf[BUF_SIZE];
dir = opendir("/proc");
if (NULL != dir)
{
while ((ptr = readdir(dir)) != NULL)
{
if ((strcmp(ptr->d_name, ".") == 0) || (strcmp(ptr->d_name, "..") == 0))
continue;
if (DT_DIR != ptr->d_type)
continue;
sprintf(filepath, "/proc/%s/status", ptr->d_name);
fp = fopen(filepath, "r");
if (NULL != fp)
{
if (fgets(buf, BUF_SIZE - 1, fp) == NULL)
{
fclose(fp);
continue;
}
sscanf(buf, "%*s %s", cur_task_name);
if (!strcmp(task_name, cur_task_name))
{
PID[++tot] = ptr->d_name;
}
fclose(fp);
}
}
closedir(dir);
}
}
struct CloudMusicWindow
{
Window window;
int height, width;
CloudMusicWindow() {}
CloudMusicWindow(Window _win, int _h, int _w)
{
window = _win;
height = _h;
width = _w;
}
};
CloudMusicWindow cloudmusicwindow[50];
map<pair<int, int>, int> Map;
int window_cnt;
void findWindow(string x)
{
int pid = atoi(x.c_str());
Display *display = XOpenDisplay(0);
WindowsMatchingPid match(display, XDefaultRootWindow(display), pid);
const list<Window> &result = match.result();
for (list<Window>::const_iterator it = result.begin(); it != result.end(); it++)
{
XWindowAttributes attrs;
XGetWindowAttributes(display, (*it), &attrs);
Map[make_pair(attrs.height, attrs.width)]++;
cloudmusicwindow[++window_cnt] = CloudMusicWindow((*it), attrs.height, attrs.width);
}
}
int main(int argc, char **argv)
{
Display *display;
Window window;
XEvent event;
int s;
display = XOpenDisplay(NULL);
if (display == NULL)
{
fprintf(stderr, "Cannot open display\n");
exit(1);
}
getPidByName("cloudmusic.exe");
for (register int i = 1; i <= tot; i++)
{
findWindow(PID[i]);
}
s = DefaultScreen(display);
window = XCreateSimpleWindow(display, RootWindow(display, s), 10, 10, 1, 1, 1,
BlackPixel(display, s), WhitePixel(display, s));
XSelectInput(display, window, ExposureMask | KeyPressMask);
XMapWindow(display, window);
for (register int i = 1; i <= window_cnt; i++)
{
if (Map[make_pair(cloudmusicwindow[i].height, cloudmusicwindow[i].width)] == 2)
{
XReparentWindow(display, cloudmusicwindow[i].window, window, 1, 1);
}
}
XCloseDisplay(display);
return 0;
}