Skip to content

Commit 6c92dc2

Browse files
authored
Merge pull request #17 from QiaoKes/develop
Develop to release
2 parents 1bfb9f4 + ad99737 commit 6c92dc2

29 files changed

Lines changed: 1805 additions & 771 deletions

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
<img src="resource/switch.png" width="90%">
1010
<img src="resource/simple.png" width="90%">
1111
<img src="resource/mpv.png" width="90%">
12+
<img src="resource/markbtn.png" width="90%">
1213
<img src="resource/danmu.png" width="90%">
1314

1415
[演示视频](https://www.bilibili.com/video/BV12dYXzhE6U/)
@@ -23,6 +24,7 @@
2324

2425
## 更新记录
2526

27+
* 2025.8.27 - v1.7.2 支持所有页面小按钮弹出二级弹窗,选择是否mpv播放
2628
* 2025.8.26 - v1.7.1 支持记录客户端日志到%appdata%/fntv/logs,增强可排障性,数据脱敏处理
2729
* 2025.8.24 - v1.7.0 支持自动检查更新,登录界面支持配置github代理
2830
* 2025.8.24 - v1.6.4 mpv配置管理抽为单独仓库,支持Anime4K着色器预设方案

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "fntv",
3-
"version": "1.7.1",
3+
"version": "1.7.2",
44
"description": "A fntv app built with Electron",
55
"main": "src/main/main.js",
66
"scripts": {

resource/markbtn.png

624 KB
Loading

src/main/common/mainwin.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
2+
const { BrowserWindow } = require('electron');
3+
const path = require('path');
4+
5+
const mainwinConfig = {
6+
width: 1400,
7+
height: 800,
8+
minWidth: 800,
9+
minHeight: 800,
10+
autoHideMenuBar: true,
11+
show: false,
12+
icon: path.join(__dirname, '../../../build/icon.ico'),
13+
frame: false,
14+
transparent: true,
15+
webPreferences: {
16+
webgl: true,
17+
partition: 'persist:fntv',
18+
preload: path.join(__dirname, '../../preload/index.js'),
19+
nodeIntegration: true, // 开启 Node.js 支持
20+
contextIsolation: false, // 如果 preload 里要直接改 DOM,通常要关掉
21+
spellcheck: false, // 禁用拼写检查,避免输入法干扰
22+
}
23+
};
24+
25+
let mainwin = null;
26+
27+
/**
28+
* 获取主窗口实例
29+
* @returns {BrowserWindow}
30+
*/
31+
function getMainWindow() {
32+
if (!mainwin) {
33+
mainwin = new BrowserWindow(mainwinConfig);
34+
}
35+
return mainwin;
36+
}
37+
38+
module.exports = {
39+
getMainWindow,
40+
};

src/main/common/tray.js

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
const { Tray, Menu, nativeImage } = require('electron');
2+
const path = require('path');
3+
const { getInstance: getUpdateChecker } = require('../../modules/updater/updateChecker');
4+
const log = require('../../modules/logger');
5+
6+
let tray = null;
7+
let trayNotificationShown = false; // 托盘提示是否已显示过
8+
let mainWindow = null; // 主窗口引用
9+
10+
/**
11+
* 创建系统托盘
12+
* @param {BrowserWindow} mainWindow - 主窗口实例
13+
*/
14+
function createTray(mainWindowInstance) {
15+
// 保存窗口引用
16+
mainWindow = mainWindowInstance;
17+
18+
// 创建托盘图标
19+
const iconPath = path.join(__dirname, '../../../build/icon.ico');
20+
const icon = nativeImage.createFromPath(iconPath);
21+
22+
tray = new Tray(icon.resize({ width: 16, height: 16 }));
23+
24+
// 设置托盘提示文字
25+
tray.setToolTip('飞牛影视');
26+
27+
// 创建托盘菜单
28+
const contextMenu = Menu.buildFromTemplate([
29+
{
30+
label: '显示主窗口',
31+
click: () => {
32+
if (mainWindow) {
33+
if (mainWindow.isMinimized()) mainWindow.restore();
34+
if (!mainWindow.isVisible()) mainWindow.show();
35+
mainWindow.focus();
36+
}
37+
}
38+
},
39+
{
40+
type: 'separator'
41+
},
42+
{
43+
label: '检查更新',
44+
click: () => {
45+
getUpdateChecker().manualCheckForUpdates().catch(error => {
46+
log.error('手动检查更新失败:', error);
47+
});
48+
}
49+
},
50+
{
51+
type: 'separator'
52+
},
53+
{
54+
label: '退出',
55+
click: () => {
56+
// 真正退出应用
57+
require('electron').app.isQuiting = true;
58+
require('electron').app.quit();
59+
}
60+
}
61+
]);
62+
63+
// 设置托盘菜单
64+
tray.setContextMenu(contextMenu);
65+
66+
// 双击托盘图标恢复窗口
67+
tray.on('double-click', () => {
68+
if (mainWindow) {
69+
if (mainWindow.isMinimized()) mainWindow.restore();
70+
if (!mainWindow.isVisible()) mainWindow.show();
71+
mainWindow.focus();
72+
}
73+
});
74+
75+
log.info('系统托盘创建成功');
76+
}
77+
78+
/**
79+
* 显示托盘通知(仅在Windows上首次显示)
80+
*/
81+
function showTrayNotification() {
82+
if (process.platform === 'win32' && !trayNotificationShown && tray) {
83+
tray.displayBalloon({
84+
iconType: 'info',
85+
title: '飞牛影视',
86+
content: '应用已最小化到托盘,双击托盘图标或右键菜单可以恢复窗口'
87+
});
88+
trayNotificationShown = true; // 标记已显示过提示
89+
}
90+
}
91+
92+
/**
93+
* 销毁托盘
94+
*/
95+
function destroyTray() {
96+
if (tray) {
97+
tray.destroy();
98+
tray = null;
99+
log.info('系统托盘已销毁');
100+
}
101+
}
102+
103+
/**
104+
* 获取托盘实例
105+
* @returns {Tray|null} 托盘实例
106+
*/
107+
function getTray() {
108+
return tray;
109+
}
110+
111+
module.exports = {
112+
createTray,
113+
showTrayNotification,
114+
destroyTray,
115+
getTray
116+
};

src/main/common/winctrl.js

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
const path = require('path');
2+
const log = require('../../modules/logger');
3+
const { readConfig } = require('../../modules/fn_config/config');
4+
const { restoreCookies } = require('../../modules/fn_config/cookie');
5+
6+
/**
7+
* 设置窗口为半屏
8+
* @param {Electron.BrowserWindow} mainWindow - 主窗口实例
9+
*/
10+
function setHalfScreen(mainWindow) {
11+
if (!mainWindow) return;
12+
13+
mainWindow.setSize(1200, 800);
14+
mainWindow.center();
15+
mainWindow.unmaximize();
16+
}
17+
18+
/**
19+
* 设置窗口为全屏
20+
* @param {Electron.BrowserWindow} mainWindow - 主窗口实例
21+
*/
22+
function setFullScreen(mainWindow) {
23+
if (mainWindow) mainWindow.maximize();
24+
}
25+
26+
/**
27+
* 设置全屏切换
28+
* @param {Electron.BrowserWindow} mainWindow - 主窗口实例
29+
*/
30+
function setupFullScreenToggle(mainWindow) {
31+
let isFullScreen = false;
32+
mainWindow.webContents.on('before-input-event', (event, input) => {
33+
if (input.type === 'keyDown' && input.key === 'F11') {
34+
if (isFullScreen) {
35+
setHalfScreen();
36+
} else {
37+
setFullScreen();
38+
}
39+
isFullScreen = !isFullScreen;
40+
event.preventDefault();
41+
}
42+
});
43+
}
44+
45+
/**
46+
* 设置输入法相关功能
47+
* @param {Electron.BrowserWindow} mainWindow - 主窗口实例
48+
*/
49+
function setupInputMethodDisable(mainWindow) {
50+
// 禁用输入法相关功能
51+
mainWindow.webContents.on('dom-ready', () => {
52+
// 注入CSS来禁用输入法自动切换
53+
mainWindow.webContents.insertCSS(`
54+
* {
55+
ime-mode: disabled !important;
56+
-webkit-ime-mode: disabled !important;
57+
}
58+
input, textarea {
59+
ime-mode: inactive !important;
60+
-webkit-ime-mode: inactive !important;
61+
}
62+
`);
63+
});
64+
}
65+
66+
/**
67+
* 设置窗口显示事件
68+
* @param {Electron.BrowserWindow} mainWindow - 主窗口实例
69+
*/
70+
function setupWindowShowEvents(mainWindow) {
71+
mainWindow.once('ready-to-show', () => mainWindow.show());
72+
}
73+
74+
/**
75+
* 设置 cookie 恢复
76+
* @param {Electron.BrowserWindow} mainWindow - 主窗口实例
77+
*/
78+
function setupCookieRestore(mainWindow) {
79+
// 从配置中恢复 cookie
80+
const savedConfig = readConfig();
81+
if (!savedConfig || !savedConfig.token || !savedConfig.domain) {
82+
log.warn('没有找到已保存的配置,无法恢复 cookie');
83+
mainWindow.loadFile(path.join(__dirname, '../../public/login.html'));
84+
return;
85+
}
86+
87+
// 恢复 cookie 并跳转到对应的 URL
88+
log.info('恢复登录状态,即将跳转到主页面, domain:', savedConfig.domain, ' token:', savedConfig.token);
89+
90+
// 恢复 cookie
91+
restoreCookies(savedConfig.domain, savedConfig.token).then((result) => {
92+
if (result === true) {
93+
// cookie 恢复成功,跳转到主页面
94+
mainWindow.loadURL(`${savedConfig.domain}/v`);
95+
return;
96+
}
97+
98+
// cookie 恢复失败,跳转到登录页面
99+
log.warn('Cookie 恢复失败,跳转到登录页面');
100+
mainWindow.loadFile(path.join(__dirname, '../../public/login.html'));
101+
}).catch((error) => {
102+
// 出现异常,也跳转到登录页面
103+
log.error('Cookie 恢复过程中出现异常:', error);
104+
mainWindow.loadFile(path.join(__dirname, '../../public/login.html'));
105+
});
106+
}
107+
108+
module.exports = {
109+
setHalfScreen,
110+
setFullScreen,
111+
setupFullScreenToggle,
112+
setupInputMethodDisable,
113+
setupWindowShowEvents,
114+
setupCookieRestore,
115+
};

0 commit comments

Comments
 (0)