Update On Sat Jul 6 20:29:38 CEST 2024

This commit is contained in:
github-action[bot]
2024-07-06 20:29:38 +02:00
parent 06deae9882
commit 9e781f8d94
89 changed files with 2270 additions and 1471 deletions
+1
View File
@@ -694,3 +694,4 @@ Update On Tue Jul 2 20:34:08 CEST 2024
Update On Wed Jul 3 20:29:03 CEST 2024
Update On Thu Jul 4 20:30:48 CEST 2024
Update On Fri Jul 5 20:32:45 CEST 2024
Update On Sat Jul 6 20:29:28 CEST 2024
+2
View File
@@ -869,6 +869,7 @@ dependencies = [
"base64 0.22.1",
"chrono",
"clap",
"cocoa 0.25.0",
"ctrlc",
"dashmap 6.0.1",
"deelevate",
@@ -890,6 +891,7 @@ dependencies = [
"md-5",
"nanoid",
"num_cpus",
"objc",
"once_cell",
"open 5.2.0",
"parking_lot",
+4
View File
@@ -101,6 +101,10 @@ md-5 = "0.10.6"
hex = "0.4"
rand = "0.8"
[target.'cfg(target_os = "macos")'.dependencies]
cocoa = "0.25.0"
objc = "0.2.7"
[target.'cfg(windows)'.dependencies]
deelevate = "0.2.0"
winreg = { version = "0.52", features = ["transactions"] }
+8
View File
@@ -3,6 +3,14 @@
windows_subsystem = "windows"
)]
#[cfg(target_os = "macos")]
#[macro_use]
extern crate cocoa;
#[cfg(target_os = "macos")]
#[macro_use]
extern crate objc;
mod cmds;
mod config;
mod core;
@@ -17,6 +17,40 @@ use serde_yaml::Mapping;
use std::net::TcpListener;
use tauri::{api::process::Command, App, AppHandle, Manager};
#[cfg(target_os = "macos")]
fn set_window_controls_pos(window: cocoa::base::id, x: f64, y: f64) {
use cocoa::{
appkit::{NSView, NSWindow, NSWindowButton},
foundation::NSRect,
};
unsafe {
let close = window.standardWindowButton_(NSWindowButton::NSWindowCloseButton);
let miniaturize = window.standardWindowButton_(NSWindowButton::NSWindowMiniaturizeButton);
let zoom = window.standardWindowButton_(NSWindowButton::NSWindowZoomButton);
let title_bar_container_view = close.superview().superview();
let close_rect: NSRect = msg_send![close, frame];
let button_height = close_rect.size.height;
let title_bar_frame_height = button_height + y;
let mut title_bar_rect = NSView::frame(title_bar_container_view);
title_bar_rect.size.height = title_bar_frame_height;
title_bar_rect.origin.y = NSView::frame(window).size.height - title_bar_frame_height;
let _: () = msg_send![title_bar_container_view, setFrame: title_bar_rect];
let window_buttons = vec![close, miniaturize, zoom];
let space_between = NSView::frame(miniaturize).origin.x - NSView::frame(close).origin.x;
for (i, button) in window_buttons.into_iter().enumerate() {
let mut rect: NSRect = NSView::frame(button);
rect.origin.x = x + (i as f64 * space_between);
button.setFrameOrigin(rect.origin);
}
}
}
pub fn find_unused_port() -> Result<u16> {
match TcpListener::bind("127.0.0.1:0") {
Ok(listener) => {
@@ -244,6 +278,18 @@ pub fn create_window(app_handle: &AppHandle) {
#[cfg(target_os = "macos")]
{
fn set_controls_and_log_error(app_handle: &tauri::AppHandle, window_name: &str) {
match app_handle.get_window(window_name).unwrap().ns_window() {
Ok(raw_window) => {
let window_id: cocoa::base::id = raw_window as _;
set_window_controls_pos(window_id, 33.0, 26.0);
}
Err(err) => {
log::error!(target: "app", "failed to get ns_window, {err}");
}
}
}
match builder
.decorations(true)
.hidden_title(true)
@@ -252,9 +298,16 @@ pub fn create_window(app_handle: &AppHandle) {
{
Ok(win) => {
#[cfg(debug_assertions)]
{
win.open_devtools();
}
win.open_devtools();
set_controls_and_log_error(&app_handle, "main");
let app_handle_clone = app_handle.clone();
win.on_window_event(move |event| {
if let tauri::WindowEvent::Resized(_) = event {
set_controls_and_log_error(&app_handle_clone, "main");
}
});
}
Err(err) => {
log::error!(target: "app", "failed to create window, {err}");
@@ -19,7 +19,7 @@
"@mui/icons-material": "5.16.0",
"@mui/lab": "5.0.0-alpha.171",
"@mui/material": "5.16.0",
"@mui/x-data-grid": "7.8.0",
"@mui/x-data-grid": "7.9.0",
"@nyanpasu/interface": "workspace:^",
"@nyanpasu/ui": "workspace:^",
"@tauri-apps/api": "1.6.0",
@@ -6,6 +6,7 @@ import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
import { LayoutControl } from "../layout/layout-control";
import styles from "./app-container.module.scss";
import AppDrawer from "./app-drawer";
import { alpha, useTheme } from "@mui/material";
const OS = getSystem();
@@ -21,6 +22,8 @@ export const AppContainer = ({
// wait: 100,
// });
const { palette } = useTheme();
return (
<Paper
square
@@ -46,7 +49,17 @@ export const AppContainer = ({
<LayoutControl className="fixed right-6 top-1.5 !z-50" />
)}
<div className="h-9" data-windrag />
{OS === "macos" && (
<div
className="fixed z-50 left-6 top-3 h-8 w-[4.5rem] rounded-full"
style={{ backgroundColor: alpha(palette.primary.main, 0.1) }}
/>
)}
<div
className={OS === "macos" ? "h-[2.75rem]" : "h-9"}
data-windrag
/>
{children}
</div>
@@ -7,6 +7,8 @@ import { useState } from "react";
import { Panel } from "react-resizable-panels";
import AnimatedLogo from "../layout/animated-logo";
import RouteListItem from "./modules/route-list-item";
import { classNames } from "@/utils";
import getSystem from "@/utils/get-system";
export const AppDrawer = ({ isDrawer }: { isDrawer?: boolean }) => {
const { palette } = useTheme();
@@ -21,7 +23,7 @@ export const AppDrawer = ({ isDrawer }: { isDrawer?: boolean }) => {
className={clsx(
isDrawer ? ["max-w-60", "min-w-28"] : "w-full",
"p-4",
"pt-8",
getSystem() === "macos" ? "pt-14" : "pt-8",
"h-full",
"flex",
"flex-col",
@@ -70,7 +72,10 @@ export const AppDrawer = ({ isDrawer }: { isDrawer?: boolean }) => {
const DrawerTitle = () => {
return (
<div
className="flex items-center gap-2 fixed z-10 top-1.5 left-6"
className={classNames(
"flex items-center gap-2 fixed z-10",
getSystem() === "macos" ? "left-[6.5rem] top-3" : "left-6 top-1.5",
)}
data-windrag
>
<IconButton
+2 -2
View File
@@ -3,7 +3,7 @@
"latest": {
"mihomo": "v1.18.6",
"mihomo_alpha": "alpha-0e22876",
"clash_rs": "v0.1.18",
"clash_rs": "v0.1.19",
"clash_premium": "2023-09-05-gdcc8d87"
},
"arch_template": {
@@ -36,5 +36,5 @@
"darwin-x64": "clash-darwin-amd64-n{}.gz"
}
},
"updated_at": "2024-07-01T22:20:09.970Z"
"updated_at": "2024-07-05T22:20:22.228Z"
}
+1 -1
View File
@@ -74,7 +74,7 @@
"@tauri-apps/cli": "1.6.0",
"@types/fs-extra": "11.0.4",
"@types/lodash-es": "4.17.12",
"@types/node": "20.14.9",
"@types/node": "20.14.10",
"autoprefixer": "10.4.19",
"conventional-changelog-conventionalcommits": "8.0.0",
"cross-env": "7.0.3",
+65 -121
View File
@@ -24,7 +24,7 @@ importers:
devDependencies:
'@commitlint/cli':
specifier: 19.3.0
version: 19.3.0(@types/node@20.14.9)(typescript@5.5.3)
version: 19.3.0(@types/node@20.14.10)(typescript@5.5.3)
'@commitlint/config-conventional':
specifier: 19.2.2
version: 19.2.2
@@ -38,8 +38,8 @@ importers:
specifier: 4.17.12
version: 4.17.12
'@types/node':
specifier: 20.14.9
version: 20.14.9
specifier: 20.14.10
version: 20.14.10
autoprefixer:
specifier: 10.4.19
version: 10.4.19(postcss@8.4.39)
@@ -178,7 +178,7 @@ importers:
version: 11.11.5(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1)
'@generouted/react-router':
specifier: 1.19.5
version: 1.19.5(react-router-dom@6.24.1(react-dom@19.0.0-rc-fb9a90fa48-20240614(react@19.0.0-rc-fb9a90fa48-20240614))(react@19.0.0-rc-fb9a90fa48-20240614))(react@19.0.0-rc-fb9a90fa48-20240614)(vite@5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0))
version: 1.19.5(react-router-dom@6.24.1(react-dom@19.0.0-rc-fb9a90fa48-20240614(react@19.0.0-rc-fb9a90fa48-20240614))(react@19.0.0-rc-fb9a90fa48-20240614))(react@19.0.0-rc-fb9a90fa48-20240614)(vite@5.3.3(@types/node@20.14.10)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0))
'@juggle/resize-observer':
specifier: 3.4.0
version: 3.4.0
@@ -195,8 +195,8 @@ importers:
specifier: 5.16.0
version: 5.16.0(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react-dom@19.0.0-rc-fb9a90fa48-20240614(react@19.0.0-rc-fb9a90fa48-20240614))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1)
'@mui/x-data-grid':
specifier: 7.8.0
version: 7.8.0(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(@mui/material@5.16.0(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react-dom@19.0.0-rc-fb9a90fa48-20240614(react@19.0.0-rc-fb9a90fa48-20240614))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react-dom@19.0.0-rc-fb9a90fa48-20240614(react@19.0.0-rc-fb9a90fa48-20240614))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1)
specifier: 7.9.0
version: 7.9.0(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(@mui/material@5.16.0(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react-dom@19.0.0-rc-fb9a90fa48-20240614(react@19.0.0-rc-fb9a90fa48-20240614))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react-dom@19.0.0-rc-fb9a90fa48-20240614(react@19.0.0-rc-fb9a90fa48-20240614))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1)
'@nyanpasu/interface':
specifier: workspace:^
version: link:../interface
@@ -299,10 +299,10 @@ importers:
version: 7.15.0(eslint@8.57.0)(typescript@5.5.3)
'@vitejs/plugin-react':
specifier: 4.3.1
version: 4.3.1(vite@5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0))
version: 4.3.1(vite@5.3.3(@types/node@20.14.10)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0))
'@vitejs/plugin-react-swc':
specifier: 3.7.0
version: 3.7.0(vite@5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0))
version: 3.7.0(vite@5.3.3(@types/node@20.14.10)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0))
clsx:
specifier: 2.1.1
version: 2.1.1
@@ -320,19 +320,19 @@ importers:
version: 2.1.3
vite:
specifier: 5.3.3
version: 5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0)
version: 5.3.3(@types/node@20.14.10)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0)
vite-plugin-monaco-editor:
specifier: npm:vite-plugin-monaco-editor-new@1.1.3
version: vite-plugin-monaco-editor-new@1.1.3(monaco-editor@0.50.0)
vite-plugin-sass-dts:
specifier: 1.3.22
version: 1.3.22(postcss@8.4.39)(prettier@3.3.2)(sass@1.77.6)(vite@5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0))
version: 1.3.22(postcss@8.4.39)(prettier@3.3.2)(sass@1.77.6)(vite@5.3.3(@types/node@20.14.10)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0))
vite-plugin-svgr:
specifier: 4.2.0
version: 4.2.0(rollup@4.17.2)(typescript@5.5.3)(vite@5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0))
version: 4.2.0(rollup@4.17.2)(typescript@5.5.3)(vite@5.3.3(@types/node@20.14.10)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0))
vite-tsconfig-paths:
specifier: 4.3.2
version: 4.3.2(typescript@5.5.3)(vite@5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0))
version: 4.3.2(typescript@5.5.3)(vite@5.3.3(@types/node@20.14.10)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0))
frontend/ui:
dependencies:
@@ -1169,16 +1169,6 @@ packages:
'@types/react':
optional: true
'@mui/private-theming@5.15.20':
resolution: {integrity: sha512-BK8F94AIqSrnaPYXf2KAOjGZJgWfvqAVQ2gVR3EryvQFtuBnG6RwodxrCvd3B48VuMy6Wsk897+lQMUxJyk+6g==}
engines: {node: '>=12.0.0'}
peerDependencies:
'@types/react': npm:types-react@rc
react: npm:react@rc
peerDependenciesMeta:
'@types/react':
optional: true
'@mui/private-theming@5.16.0':
resolution: {integrity: sha512-sYpubkO1MZOnxNyVOClrPNOTs0MfuRVVnAvCeMaOaXt6GimgQbnUcshYv2pSr6PFj+Mqzdff/FYOBceK8u5QgA==}
engines: {node: '>=12.0.0'}
@@ -1202,22 +1192,6 @@ packages:
'@emotion/styled':
optional: true
'@mui/system@5.15.20':
resolution: {integrity: sha512-LoMq4IlAAhxzL2VNUDBTQxAb4chnBe8JvRINVNDiMtHE2PiPOoHlhOPutSxEbaL5mkECPVWSv6p8JEV+uykwIA==}
engines: {node: '>=12.0.0'}
peerDependencies:
'@emotion/react': ^11.5.0
'@emotion/styled': ^11.3.0
'@types/react': npm:types-react@rc
react: npm:react@rc
peerDependenciesMeta:
'@emotion/react':
optional: true
'@emotion/styled':
optional: true
'@types/react':
optional: true
'@mui/system@5.16.0':
resolution: {integrity: sha512-9YbkC2m3+pNumAvubYv+ijLtog6puJ0fJ6rYfzfLCM47pWrw3m+30nXNM8zMgDaKL6vpfWJcCXm+LPaWBpy7sw==}
engines: {node: '>=12.0.0'}
@@ -1242,16 +1216,6 @@ packages:
'@types/react':
optional: true
'@mui/utils@5.15.20':
resolution: {integrity: sha512-mAbYx0sovrnpAu1zHc3MDIhPqL8RPVC5W5xcO1b7PiSCJPtckIZmBkp8hefamAvUiAV8gpfMOM6Zb+eSisbI2A==}
engines: {node: '>=12.0.0'}
peerDependencies:
'@types/react': npm:types-react@rc
react: npm:react@rc
peerDependenciesMeta:
'@types/react':
optional: true
'@mui/utils@5.16.0':
resolution: {integrity: sha512-kLLi5J1xY+mwtUlMb8Ubdxf4qFAA1+U7WPBvjM/qQ4CIwLCohNb0sHo1oYPufjSIH/Z9+dhVxD7dJlfGjd1AVA==}
engines: {node: '>=12.0.0'}
@@ -1262,14 +1226,20 @@ packages:
'@types/react':
optional: true
'@mui/x-data-grid@7.8.0':
resolution: {integrity: sha512-X3t6EVSZ28vVKY9NfqKcClchw2o/KmHsywybp1tNFevIJiwjZSp7NDJ091GyTqMgyDt1Dy5z2hGxoTDUYYfeGg==}
'@mui/x-data-grid@7.9.0':
resolution: {integrity: sha512-RkrVD+tfcR/h3j2p2uqohxA00C5tCJIV5gb5+2ap8XdM0Y8XMF81bB8UADWenU5W83UTErWvtU7n4gCl7hJO9g==}
engines: {node: '>=14.0.0'}
peerDependencies:
'@mui/material': ^5.15.14
react: npm:react@rc
react-dom: npm:react-dom@rc
'@mui/x-internals@7.9.0':
resolution: {integrity: sha512-RJRrM6moaDZ8S11gDt8OKVclKm2v9khpIyLkpenNze+tT4dQYoU3liW5P2t31hA4Na/T6JQKNosB4qmB2TYfZw==}
engines: {node: '>=14.0.0'}
peerDependencies:
react: npm:react@rc
'@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
@@ -1935,8 +1905,8 @@ packages:
'@types/node@20.12.10':
resolution: {integrity: sha512-Eem5pH9pmWBHoGAT8Dr5fdc5rYA+4NAovdM4EktRPVAAiJhmWWfQrA0cFhAbOsQdSfIHjAud6YdkbL69+zSKjw==}
'@types/node@20.14.9':
resolution: {integrity: sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==}
'@types/node@20.14.10':
resolution: {integrity: sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==}
'@types/parse-json@4.0.2':
resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
@@ -5905,11 +5875,11 @@ snapshots:
'@babel/helper-validator-identifier': 7.24.5
to-fast-properties: 2.0.0
'@commitlint/cli@19.3.0(@types/node@20.14.9)(typescript@5.5.3)':
'@commitlint/cli@19.3.0(@types/node@20.14.10)(typescript@5.5.3)':
dependencies:
'@commitlint/format': 19.3.0
'@commitlint/lint': 19.2.2
'@commitlint/load': 19.2.0(@types/node@20.14.9)(typescript@5.5.3)
'@commitlint/load': 19.2.0(@types/node@20.14.10)(typescript@5.5.3)
'@commitlint/read': 19.2.1
'@commitlint/types': 19.0.3
execa: 8.0.1
@@ -5956,7 +5926,7 @@ snapshots:
'@commitlint/rules': 19.0.3
'@commitlint/types': 19.0.3
'@commitlint/load@19.2.0(@types/node@20.14.9)(typescript@5.5.3)':
'@commitlint/load@19.2.0(@types/node@20.14.10)(typescript@5.5.3)':
dependencies:
'@commitlint/config-validator': 19.0.3
'@commitlint/execute-rule': 19.0.0
@@ -5964,7 +5934,7 @@ snapshots:
'@commitlint/types': 19.0.3
chalk: 5.3.0
cosmiconfig: 9.0.0(typescript@5.5.3)
cosmiconfig-typescript-loader: 5.0.0(@types/node@20.14.9)(cosmiconfig@9.0.0(typescript@5.5.3))(typescript@5.5.3)
cosmiconfig-typescript-loader: 5.0.0(@types/node@20.14.10)(cosmiconfig@9.0.0(typescript@5.5.3))(typescript@5.5.3)
lodash.isplainobject: 4.0.6
lodash.merge: 4.6.2
lodash.uniq: 4.5.0
@@ -6338,13 +6308,13 @@ snapshots:
postcss: 7.0.32
purgecss: 2.3.0
'@generouted/react-router@1.19.5(react-router-dom@6.24.1(react-dom@19.0.0-rc-fb9a90fa48-20240614(react@19.0.0-rc-fb9a90fa48-20240614))(react@19.0.0-rc-fb9a90fa48-20240614))(react@19.0.0-rc-fb9a90fa48-20240614)(vite@5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0))':
'@generouted/react-router@1.19.5(react-router-dom@6.24.1(react-dom@19.0.0-rc-fb9a90fa48-20240614(react@19.0.0-rc-fb9a90fa48-20240614))(react@19.0.0-rc-fb9a90fa48-20240614))(react@19.0.0-rc-fb9a90fa48-20240614)(vite@5.3.3(@types/node@20.14.10)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0))':
dependencies:
fast-glob: 3.3.2
generouted: 1.19.5(vite@5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0))
generouted: 1.19.5(vite@5.3.3(@types/node@20.14.10)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0))
react: 19.0.0-rc-fb9a90fa48-20240614
react-router-dom: 6.24.1(react-dom@19.0.0-rc-fb9a90fa48-20240614(react@19.0.0-rc-fb9a90fa48-20240614))(react@19.0.0-rc-fb9a90fa48-20240614)
vite: 5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0)
vite: 5.3.3(@types/node@20.14.10)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0)
'@humanwhocodes/config-array@0.11.14':
dependencies:
@@ -6450,15 +6420,6 @@ snapshots:
'@emotion/styled': 11.11.5(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1)
'@types/react': types-react@19.0.0-rc.1
'@mui/private-theming@5.15.20(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1)':
dependencies:
'@babel/runtime': 7.24.7
'@mui/utils': 5.15.20(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1)
prop-types: 15.8.1
react: 19.0.0-rc-fb9a90fa48-20240614
optionalDependencies:
'@types/react': types-react@19.0.0-rc.1
'@mui/private-theming@5.16.0(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1)':
dependencies:
'@babel/runtime': 7.24.7
@@ -6479,22 +6440,6 @@ snapshots:
'@emotion/react': 11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1)
'@emotion/styled': 11.11.5(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1)
'@mui/system@5.15.20(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1)':
dependencies:
'@babel/runtime': 7.24.7
'@mui/private-theming': 5.15.20(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1)
'@mui/styled-engine': 5.15.14(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react@19.0.0-rc-fb9a90fa48-20240614)
'@mui/types': 7.2.14(types-react@19.0.0-rc.1)
'@mui/utils': 5.15.20(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1)
clsx: 2.1.1
csstype: 3.1.3
prop-types: 15.8.1
react: 19.0.0-rc-fb9a90fa48-20240614
optionalDependencies:
'@emotion/react': 11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1)
'@emotion/styled': 11.11.5(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1)
'@types/react': types-react@19.0.0-rc.1
'@mui/system@5.16.0(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1)':
dependencies:
'@babel/runtime': 7.24.7
@@ -6515,16 +6460,6 @@ snapshots:
optionalDependencies:
'@types/react': types-react@19.0.0-rc.1
'@mui/utils@5.15.20(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1)':
dependencies:
'@babel/runtime': 7.24.7
'@types/prop-types': 15.7.12
prop-types: 15.8.1
react: 19.0.0-rc-fb9a90fa48-20240614
react-is: 18.3.1
optionalDependencies:
'@types/react': types-react@19.0.0-rc.1
'@mui/utils@5.16.0(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1)':
dependencies:
'@babel/runtime': 7.24.7
@@ -6535,12 +6470,13 @@ snapshots:
optionalDependencies:
'@types/react': types-react@19.0.0-rc.1
'@mui/x-data-grid@7.8.0(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(@mui/material@5.16.0(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react-dom@19.0.0-rc-fb9a90fa48-20240614(react@19.0.0-rc-fb9a90fa48-20240614))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react-dom@19.0.0-rc-fb9a90fa48-20240614(react@19.0.0-rc-fb9a90fa48-20240614))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1)':
'@mui/x-data-grid@7.9.0(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(@mui/material@5.16.0(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react-dom@19.0.0-rc-fb9a90fa48-20240614(react@19.0.0-rc-fb9a90fa48-20240614))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react-dom@19.0.0-rc-fb9a90fa48-20240614(react@19.0.0-rc-fb9a90fa48-20240614))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1)':
dependencies:
'@babel/runtime': 7.24.7
'@mui/material': 5.16.0(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react-dom@19.0.0-rc-fb9a90fa48-20240614(react@19.0.0-rc-fb9a90fa48-20240614))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1)
'@mui/system': 5.15.20(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1)
'@mui/utils': 5.15.20(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1)
'@mui/system': 5.16.0(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1))(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1)
'@mui/utils': 5.16.0(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1)
'@mui/x-internals': 7.9.0(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1)
clsx: 2.1.1
prop-types: 15.8.1
react: 19.0.0-rc-fb9a90fa48-20240614
@@ -6551,6 +6487,14 @@ snapshots:
- '@emotion/styled'
- '@types/react'
'@mui/x-internals@7.9.0(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1)':
dependencies:
'@babel/runtime': 7.24.7
'@mui/utils': 5.16.0(react@19.0.0-rc-fb9a90fa48-20240614)(types-react@19.0.0-rc.1)
react: 19.0.0-rc-fb9a90fa48-20240614
transitivePeerDependencies:
- '@types/react'
'@nodelib/fs.scandir@2.1.5':
dependencies:
'@nodelib/fs.stat': 2.0.5
@@ -7039,12 +6983,12 @@ snapshots:
dependencies:
'@types/http-cache-semantics': 4.0.4
'@types/keyv': 3.1.4
'@types/node': 20.14.9
'@types/node': 20.14.10
'@types/responselike': 1.0.3
'@types/conventional-commits-parser@5.0.0':
dependencies:
'@types/node': 20.14.9
'@types/node': 20.14.10
'@types/d3-array@3.2.1': {}
@@ -7178,7 +7122,7 @@ snapshots:
'@types/fs-extra@11.0.4':
dependencies:
'@types/jsonfile': 6.1.4
'@types/node': 20.14.9
'@types/node': 20.14.10
'@types/geojson@7946.0.14': {}
@@ -7194,11 +7138,11 @@ snapshots:
'@types/jsonfile@6.1.4':
dependencies:
'@types/node': 20.14.9
'@types/node': 20.14.10
'@types/keyv@3.1.4':
dependencies:
'@types/node': 20.14.9
'@types/node': 20.14.10
'@types/lodash-es@4.17.12':
dependencies:
@@ -7218,7 +7162,7 @@ snapshots:
dependencies:
undici-types: 5.26.5
'@types/node@20.14.9':
'@types/node@20.14.10':
dependencies:
undici-types: 5.26.5
@@ -7240,7 +7184,7 @@ snapshots:
'@types/responselike@1.0.3':
dependencies:
'@types/node': 20.14.9
'@types/node': 20.14.10
'@types/unist@2.0.10': {}
@@ -7248,7 +7192,7 @@ snapshots:
'@types/yauzl@2.10.3':
dependencies:
'@types/node': 20.14.9
'@types/node': 20.14.10
optional: true
'@typescript-eslint/eslint-plugin@7.15.0(@typescript-eslint/parser@7.15.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0)(typescript@5.5.3)':
@@ -7334,21 +7278,21 @@ snapshots:
'@ungap/structured-clone@1.2.0': {}
'@vitejs/plugin-react-swc@3.7.0(vite@5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0))':
'@vitejs/plugin-react-swc@3.7.0(vite@5.3.3(@types/node@20.14.10)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0))':
dependencies:
'@swc/core': 1.6.1
vite: 5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0)
vite: 5.3.3(@types/node@20.14.10)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0)
transitivePeerDependencies:
- '@swc/helpers'
'@vitejs/plugin-react@4.3.1(vite@5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0))':
'@vitejs/plugin-react@4.3.1(vite@5.3.3(@types/node@20.14.10)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0))':
dependencies:
'@babel/core': 7.24.5
'@babel/plugin-transform-react-jsx-self': 7.24.5(@babel/core@7.24.5)
'@babel/plugin-transform-react-jsx-source': 7.24.1(@babel/core@7.24.5)
'@types/babel__core': 7.20.5
react-refresh: 0.14.2
vite: 5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0)
vite: 5.3.3(@types/node@20.14.10)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0)
transitivePeerDependencies:
- supports-color
@@ -7836,9 +7780,9 @@ snapshots:
dependencies:
is-what: 3.14.1
cosmiconfig-typescript-loader@5.0.0(@types/node@20.14.9)(cosmiconfig@9.0.0(typescript@5.5.3))(typescript@5.5.3):
cosmiconfig-typescript-loader@5.0.0(@types/node@20.14.10)(cosmiconfig@9.0.0(typescript@5.5.3))(typescript@5.5.3):
dependencies:
'@types/node': 20.14.9
'@types/node': 20.14.10
cosmiconfig: 9.0.0(typescript@5.5.3)
jiti: 1.21.0
typescript: 5.5.3
@@ -8829,9 +8773,9 @@ snapshots:
functions-have-names@1.2.3: {}
generouted@1.19.5(vite@5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0)):
generouted@1.19.5(vite@5.3.3(@types/node@20.14.10)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0)):
dependencies:
vite: 5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0)
vite: 5.3.3(@types/node@20.14.10)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0)
gensync@1.0.0-beta.2: {}
@@ -11345,43 +11289,43 @@ snapshots:
esbuild: 0.19.12
monaco-editor: 0.50.0
vite-plugin-sass-dts@1.3.22(postcss@8.4.39)(prettier@3.3.2)(sass@1.77.6)(vite@5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0)):
vite-plugin-sass-dts@1.3.22(postcss@8.4.39)(prettier@3.3.2)(sass@1.77.6)(vite@5.3.3(@types/node@20.14.10)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0)):
dependencies:
postcss: 8.4.39
postcss-js: 4.0.1(postcss@8.4.39)
prettier: 3.3.2
sass: 1.77.6
vite: 5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0)
vite: 5.3.3(@types/node@20.14.10)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0)
vite-plugin-svgr@4.2.0(rollup@4.17.2)(typescript@5.5.3)(vite@5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0)):
vite-plugin-svgr@4.2.0(rollup@4.17.2)(typescript@5.5.3)(vite@5.3.3(@types/node@20.14.10)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0)):
dependencies:
'@rollup/pluginutils': 5.1.0(rollup@4.17.2)
'@svgr/core': 8.1.0(typescript@5.5.3)
'@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.5.3))
vite: 5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0)
vite: 5.3.3(@types/node@20.14.10)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0)
transitivePeerDependencies:
- rollup
- supports-color
- typescript
vite-tsconfig-paths@4.3.2(typescript@5.5.3)(vite@5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0)):
vite-tsconfig-paths@4.3.2(typescript@5.5.3)(vite@5.3.3(@types/node@20.14.10)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0)):
dependencies:
debug: 4.3.4
globrex: 0.1.2
tsconfck: 3.0.3(typescript@5.5.3)
optionalDependencies:
vite: 5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0)
vite: 5.3.3(@types/node@20.14.10)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0)
transitivePeerDependencies:
- supports-color
- typescript
vite@5.3.3(@types/node@20.14.9)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0):
vite@5.3.3(@types/node@20.14.10)(less@4.2.0)(sass@1.77.6)(stylus@0.62.0):
dependencies:
esbuild: 0.21.5
postcss: 8.4.39
rollup: 4.17.2
optionalDependencies:
'@types/node': 20.14.9
'@types/node': 20.14.10
fsevents: 2.3.3
less: 4.2.0
sass: 1.77.6
+15
View File
@@ -1,3 +1,18 @@
## v1.7.3
### Features
- 支持可视化编辑订阅代理组
- 支持可视化编辑订阅节点
- 支持可视化编辑订阅规则
- 扩展脚本支持订阅名称参数 `function main(config, profileName)`
### Bugs Fixes
- 代理绕过格式检查错误
---
## v1.7.2
### Break Changes
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "clash-verge",
"version": "1.7.2",
"version": "1.7.3",
"license": "GPL-3.0-only",
"scripts": {
"dev": "tauri dev",
@@ -24,10 +24,10 @@
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@juggle/resize-observer": "^3.4.0",
"@mui/icons-material": "^5.15.21",
"@mui/icons-material": "^5.16.0",
"@mui/lab": "5.0.0-alpha.149",
"@mui/material": "^5.15.21",
"@mui/x-data-grid": "^7.8.0",
"@mui/material": "^5.16.0",
"@mui/x-data-grid": "^7.9.0",
"@tauri-apps/api": "^1.6.0",
"@types/json-schema": "^7.0.15",
"ahooks": "^3.8.0",
+87 -69
View File
@@ -26,17 +26,17 @@ importers:
specifier: ^3.4.0
version: 3.4.0
"@mui/icons-material":
specifier: ^5.15.21
version: 5.15.21(@mui/material@5.15.21(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)
specifier: ^5.16.0
version: 5.16.0(@mui/material@5.16.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)
"@mui/lab":
specifier: 5.0.0-alpha.149
version: 5.0.0-alpha.149(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@mui/material@5.15.21(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
version: 5.0.0-alpha.149(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@mui/material@5.16.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
"@mui/material":
specifier: ^5.15.21
version: 5.15.21(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
specifier: ^5.16.0
version: 5.16.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
"@mui/x-data-grid":
specifier: ^7.8.0
version: 7.8.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@mui/material@5.15.21(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
specifier: ^7.9.0
version: 7.9.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@mui/material@5.16.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
"@tauri-apps/api":
specifier: ^1.6.0
version: 1.6.0
@@ -154,10 +154,10 @@ importers:
version: 4.4.10
"@vitejs/plugin-legacy":
specifier: ^5.4.1
version: 5.4.1(terser@5.31.1)(vite@5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))
version: 5.4.1(terser@5.31.1)(vite@5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1))
"@vitejs/plugin-react":
specifier: ^4.3.1
version: 4.3.1(vite@5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))
version: 4.3.1(vite@5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1))
adm-zip:
specifier: ^0.5.14
version: 0.5.14
@@ -193,13 +193,13 @@ importers:
version: 5.5.3
vite:
specifier: ^5.3.3
version: 5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
version: 5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1)
vite-plugin-monaco-editor:
specifier: ^1.1.0
version: 1.1.0(monaco-editor@0.49.0)
vite-plugin-svgr:
specifier: ^4.2.0
version: 4.2.0(rollup@4.18.0)(typescript@5.5.3)(vite@5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))
version: 4.2.0(rollup@4.18.0)(typescript@5.5.3)(vite@5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1))
packages:
"@actions/github@5.1.1":
@@ -1580,16 +1580,16 @@ packages:
"@types/react":
optional: true
"@mui/core-downloads-tracker@5.15.21":
"@mui/core-downloads-tracker@5.16.0":
resolution:
{
integrity: sha512-dp9lXBaJZzJYeJfQY3Ow4Rb49QaCEdkl2KKYscdQHQm6bMJ+l4XPY3Cd9PCeeJTsHPIDJ60lzXbeRgs6sx/rpw==,
integrity: sha512-8SLffXYPRVpcZx5QzxNE8fytTqzp+IuU3deZbQWg/vSaTlDpR5YVrQ4qQtXTi5cRdhOufV5INylmwlKK+//nPw==,
}
"@mui/icons-material@5.15.21":
"@mui/icons-material@5.16.0":
resolution:
{
integrity: sha512-yqkq1MbdkmX5ZHyvZTBuAaA6RkvoqkoAgwBSx9Oh0L0jAfj9T/Ih/NhMNjkl8PWVSonjfDUkKroBnjRyo/1M9Q==,
integrity: sha512-6ISoOhkp9w5gD0PEW9JklrcbyARDkFWNTBdwXZ1Oy5IGlyu9B0zG0hnUIe4H17IaF1Vgj6C8VI+v4tkSdK0veg==,
}
engines: { node: ">=12.0.0" }
peerDependencies:
@@ -1621,10 +1621,10 @@ packages:
"@types/react":
optional: true
"@mui/material@5.15.21":
"@mui/material@5.16.0":
resolution:
{
integrity: sha512-nTyCcgduKwHqiuQ/B03EQUa+utSMzn2sQp0QAibsnYe4tvc3zkMbO0amKpl48vhABIY3IvT6w9615BFIgMt0YA==,
integrity: sha512-DbR1NckTLpjt9Zut9EGQ70th86HfN0BYQgyYro6aXQrNfjzSwe3BJS1AyBQ5mJ7TdL6YVRqohfukxj9JlqZZUg==,
}
engines: { node: ">=12.0.0" }
peerDependencies:
@@ -1641,10 +1641,10 @@ packages:
"@types/react":
optional: true
"@mui/private-theming@5.15.20":
"@mui/private-theming@5.16.0":
resolution:
{
integrity: sha512-BK8F94AIqSrnaPYXf2KAOjGZJgWfvqAVQ2gVR3EryvQFtuBnG6RwodxrCvd3B48VuMy6Wsk897+lQMUxJyk+6g==,
integrity: sha512-sYpubkO1MZOnxNyVOClrPNOTs0MfuRVVnAvCeMaOaXt6GimgQbnUcshYv2pSr6PFj+Mqzdff/FYOBceK8u5QgA==,
}
engines: { node: ">=12.0.0" }
peerDependencies:
@@ -1670,10 +1670,10 @@ packages:
"@emotion/styled":
optional: true
"@mui/system@5.15.20":
"@mui/system@5.16.0":
resolution:
{
integrity: sha512-LoMq4IlAAhxzL2VNUDBTQxAb4chnBe8JvRINVNDiMtHE2PiPOoHlhOPutSxEbaL5mkECPVWSv6p8JEV+uykwIA==,
integrity: sha512-9YbkC2m3+pNumAvubYv+ijLtog6puJ0fJ6rYfzfLCM47pWrw3m+30nXNM8zMgDaKL6vpfWJcCXm+LPaWBpy7sw==,
}
engines: { node: ">=12.0.0" }
peerDependencies:
@@ -1700,10 +1700,10 @@ packages:
"@types/react":
optional: true
"@mui/utils@5.15.20":
"@mui/utils@5.16.0":
resolution:
{
integrity: sha512-mAbYx0sovrnpAu1zHc3MDIhPqL8RPVC5W5xcO1b7PiSCJPtckIZmBkp8hefamAvUiAV8gpfMOM6Zb+eSisbI2A==,
integrity: sha512-kLLi5J1xY+mwtUlMb8Ubdxf4qFAA1+U7WPBvjM/qQ4CIwLCohNb0sHo1oYPufjSIH/Z9+dhVxD7dJlfGjd1AVA==,
}
engines: { node: ">=12.0.0" }
peerDependencies:
@@ -1713,10 +1713,10 @@ packages:
"@types/react":
optional: true
"@mui/x-data-grid@7.8.0":
"@mui/x-data-grid@7.9.0":
resolution:
{
integrity: sha512-X3t6EVSZ28vVKY9NfqKcClchw2o/KmHsywybp1tNFevIJiwjZSp7NDJ091GyTqMgyDt1Dy5z2hGxoTDUYYfeGg==,
integrity: sha512-RkrVD+tfcR/h3j2p2uqohxA00C5tCJIV5gb5+2ap8XdM0Y8XMF81bB8UADWenU5W83UTErWvtU7n4gCl7hJO9g==,
}
engines: { node: ">=14.0.0" }
peerDependencies:
@@ -1724,6 +1724,15 @@ packages:
react: ^17.0.0 || ^18.0.0
react-dom: ^17.0.0 || ^18.0.0
"@mui/x-internals@7.9.0":
resolution:
{
integrity: sha512-RJRrM6moaDZ8S11gDt8OKVclKm2v9khpIyLkpenNze+tT4dQYoU3liW5P2t31hA4Na/T6JQKNosB4qmB2TYfZw==,
}
engines: { node: ">=14.0.0" }
peerDependencies:
react: ^17.0.0 || ^18.0.0
"@mui/x-tree-view@6.0.0-alpha.1":
resolution:
{
@@ -2268,10 +2277,10 @@ packages:
integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==,
}
"@types/node@20.14.9":
"@types/node@20.14.10":
resolution:
{
integrity: sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==,
integrity: sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==,
}
"@types/parse-json@4.0.2":
@@ -2740,10 +2749,10 @@ packages:
integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==,
}
electron-to-chromium@1.4.816:
electron-to-chromium@1.4.818:
resolution:
{
integrity: sha512-EKH5X5oqC6hLmiS7/vYtZHZFTNdhsYG5NVPRN6Yn0kQHNBlT59+xSM8HBy66P5fxWpKgZbPqb+diC64ng295Jw==,
integrity: sha512-eGvIk2V0dGImV9gWLq8fDfTTsCAeMDwZqEPMr+jMInxZdnp9Us8UpovYpRCf9NQ7VOFgrN2doNSgvISbsbNpxA==,
}
end-of-stream@1.4.4:
@@ -5587,7 +5596,7 @@ snapshots:
"@babel/runtime": 7.24.7
"@floating-ui/react-dom": 2.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
"@mui/types": 7.2.14(@types/react@18.3.3)
"@mui/utils": 5.15.20(@types/react@18.3.3)(react@18.3.1)
"@mui/utils": 5.16.0(@types/react@18.3.3)(react@18.3.1)
"@popperjs/core": 2.11.8
clsx: 2.1.1
prop-types: 15.8.1
@@ -5601,7 +5610,7 @@ snapshots:
"@babel/runtime": 7.24.7
"@floating-ui/react-dom": 2.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
"@mui/types": 7.2.14(@types/react@18.3.3)
"@mui/utils": 5.15.20(@types/react@18.3.3)(react@18.3.1)
"@mui/utils": 5.16.0(@types/react@18.3.3)(react@18.3.1)
"@popperjs/core": 2.11.8
clsx: 2.1.1
prop-types: 15.8.1
@@ -5610,25 +5619,25 @@ snapshots:
optionalDependencies:
"@types/react": 18.3.3
"@mui/core-downloads-tracker@5.15.21": {}
"@mui/core-downloads-tracker@5.16.0": {}
"@mui/icons-material@5.15.21(@mui/material@5.15.21(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)":
"@mui/icons-material@5.16.0(@mui/material@5.16.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)":
dependencies:
"@babel/runtime": 7.24.7
"@mui/material": 5.15.21(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
"@mui/material": 5.16.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 18.3.1
optionalDependencies:
"@types/react": 18.3.3
"@mui/lab@5.0.0-alpha.149(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@mui/material@5.15.21(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)":
"@mui/lab@5.0.0-alpha.149(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@mui/material@5.16.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)":
dependencies:
"@babel/runtime": 7.24.7
"@mui/base": 5.0.0-beta.20(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
"@mui/material": 5.15.21(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
"@mui/system": 5.15.20(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)
"@mui/material": 5.16.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
"@mui/system": 5.16.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)
"@mui/types": 7.2.14(@types/react@18.3.3)
"@mui/utils": 5.15.20(@types/react@18.3.3)(react@18.3.1)
"@mui/x-tree-view": 6.0.0-alpha.1(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@mui/base@5.0.0-beta.20(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/material@5.15.21(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@5.15.20(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
"@mui/utils": 5.16.0(@types/react@18.3.3)(react@18.3.1)
"@mui/x-tree-view": 6.0.0-alpha.1(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@mui/base@5.0.0-beta.20(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/material@5.16.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@5.16.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
clsx: 2.1.1
prop-types: 15.8.1
react: 18.3.1
@@ -5638,14 +5647,14 @@ snapshots:
"@emotion/styled": 11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)
"@types/react": 18.3.3
"@mui/material@5.15.21(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)":
"@mui/material@5.16.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)":
dependencies:
"@babel/runtime": 7.24.7
"@mui/base": 5.0.0-beta.40(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
"@mui/core-downloads-tracker": 5.15.21
"@mui/system": 5.15.20(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)
"@mui/core-downloads-tracker": 5.16.0
"@mui/system": 5.16.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)
"@mui/types": 7.2.14(@types/react@18.3.3)
"@mui/utils": 5.15.20(@types/react@18.3.3)(react@18.3.1)
"@mui/utils": 5.16.0(@types/react@18.3.3)(react@18.3.1)
"@types/react-transition-group": 4.4.10
clsx: 2.1.1
csstype: 3.1.3
@@ -5659,10 +5668,10 @@ snapshots:
"@emotion/styled": 11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)
"@types/react": 18.3.3
"@mui/private-theming@5.15.20(@types/react@18.3.3)(react@18.3.1)":
"@mui/private-theming@5.16.0(@types/react@18.3.3)(react@18.3.1)":
dependencies:
"@babel/runtime": 7.24.7
"@mui/utils": 5.15.20(@types/react@18.3.3)(react@18.3.1)
"@mui/utils": 5.16.0(@types/react@18.3.3)(react@18.3.1)
prop-types: 15.8.1
react: 18.3.1
optionalDependencies:
@@ -5679,13 +5688,13 @@ snapshots:
"@emotion/react": 11.11.4(@types/react@18.3.3)(react@18.3.1)
"@emotion/styled": 11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)
"@mui/system@5.15.20(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)":
"@mui/system@5.16.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)":
dependencies:
"@babel/runtime": 7.24.7
"@mui/private-theming": 5.15.20(@types/react@18.3.3)(react@18.3.1)
"@mui/private-theming": 5.16.0(@types/react@18.3.3)(react@18.3.1)
"@mui/styled-engine": 5.15.14(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1)
"@mui/types": 7.2.14(@types/react@18.3.3)
"@mui/utils": 5.15.20(@types/react@18.3.3)(react@18.3.1)
"@mui/utils": 5.16.0(@types/react@18.3.3)(react@18.3.1)
clsx: 2.1.1
csstype: 3.1.3
prop-types: 15.8.1
@@ -5699,7 +5708,7 @@ snapshots:
optionalDependencies:
"@types/react": 18.3.3
"@mui/utils@5.15.20(@types/react@18.3.3)(react@18.3.1)":
"@mui/utils@5.16.0(@types/react@18.3.3)(react@18.3.1)":
dependencies:
"@babel/runtime": 7.24.7
"@types/prop-types": 15.7.12
@@ -5709,12 +5718,13 @@ snapshots:
optionalDependencies:
"@types/react": 18.3.3
"@mui/x-data-grid@7.8.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@mui/material@5.15.21(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)":
"@mui/x-data-grid@7.9.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@mui/material@5.16.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)":
dependencies:
"@babel/runtime": 7.24.7
"@mui/material": 5.15.21(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
"@mui/system": 5.15.20(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)
"@mui/utils": 5.15.20(@types/react@18.3.3)(react@18.3.1)
"@mui/material": 5.16.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
"@mui/system": 5.16.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)
"@mui/utils": 5.16.0(@types/react@18.3.3)(react@18.3.1)
"@mui/x-internals": 7.9.0(@types/react@18.3.3)(react@18.3.1)
clsx: 2.1.1
prop-types: 15.8.1
react: 18.3.1
@@ -5725,15 +5735,23 @@ snapshots:
- "@emotion/styled"
- "@types/react"
"@mui/x-tree-view@6.0.0-alpha.1(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@mui/base@5.0.0-beta.20(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/material@5.15.21(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@5.15.20(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)":
"@mui/x-internals@7.9.0(@types/react@18.3.3)(react@18.3.1)":
dependencies:
"@babel/runtime": 7.24.7
"@mui/utils": 5.16.0(@types/react@18.3.3)(react@18.3.1)
react: 18.3.1
transitivePeerDependencies:
- "@types/react"
"@mui/x-tree-view@6.0.0-alpha.1(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@mui/base@5.0.0-beta.20(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/material@5.16.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@5.16.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)":
dependencies:
"@babel/runtime": 7.24.7
"@emotion/react": 11.11.4(@types/react@18.3.3)(react@18.3.1)
"@emotion/styled": 11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)
"@mui/base": 5.0.0-beta.20(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
"@mui/material": 5.15.21(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
"@mui/system": 5.15.20(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)
"@mui/utils": 5.15.20(@types/react@18.3.3)(react@18.3.1)
"@mui/material": 5.16.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
"@mui/system": 5.16.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)
"@mui/utils": 5.16.0(@types/react@18.3.3)(react@18.3.1)
"@types/react-transition-group": 4.4.10
clsx: 2.1.1
prop-types: 15.8.1
@@ -6019,7 +6037,7 @@ snapshots:
"@types/fs-extra@9.0.13":
dependencies:
"@types/node": 20.14.9
"@types/node": 20.14.10
"@types/hast@3.0.4":
dependencies:
@@ -6043,7 +6061,7 @@ snapshots:
"@types/ms@0.7.34": {}
"@types/node@20.14.9":
"@types/node@20.14.10":
dependencies:
undici-types: 5.26.5
@@ -6070,7 +6088,7 @@ snapshots:
"@ungap/structured-clone@1.2.0": {}
"@vitejs/plugin-legacy@5.4.1(terser@5.31.1)(vite@5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))":
"@vitejs/plugin-legacy@5.4.1(terser@5.31.1)(vite@5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1))":
dependencies:
"@babel/core": 7.24.7
"@babel/preset-env": 7.24.7(@babel/core@7.24.7)
@@ -6081,18 +6099,18 @@ snapshots:
regenerator-runtime: 0.14.1
systemjs: 6.15.1
terser: 5.31.1
vite: 5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
vite: 5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1)
transitivePeerDependencies:
- supports-color
"@vitejs/plugin-react@4.3.1(vite@5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))":
"@vitejs/plugin-react@4.3.1(vite@5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1))":
dependencies:
"@babel/core": 7.24.7
"@babel/plugin-transform-react-jsx-self": 7.24.7(@babel/core@7.24.7)
"@babel/plugin-transform-react-jsx-source": 7.24.7(@babel/core@7.24.7)
"@types/babel__core": 7.20.5
react-refresh: 0.14.2
vite: 5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
vite: 5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1)
transitivePeerDependencies:
- supports-color
@@ -6188,7 +6206,7 @@ snapshots:
browserslist@4.23.1:
dependencies:
caniuse-lite: 1.0.30001640
electron-to-chromium: 1.4.816
electron-to-chromium: 1.4.818
node-releases: 2.0.14
update-browserslist-db: 1.1.0(browserslist@4.23.1)
@@ -6321,7 +6339,7 @@ snapshots:
no-case: 3.0.4
tslib: 2.6.3
electron-to-chromium@1.4.816: {}
electron-to-chromium@1.4.818: {}
end-of-stream@1.4.4:
dependencies:
@@ -7393,24 +7411,24 @@ snapshots:
dependencies:
monaco-editor: 0.49.0
vite-plugin-svgr@4.2.0(rollup@4.18.0)(typescript@5.5.3)(vite@5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)):
vite-plugin-svgr@4.2.0(rollup@4.18.0)(typescript@5.5.3)(vite@5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1)):
dependencies:
"@rollup/pluginutils": 5.1.0(rollup@4.18.0)
"@svgr/core": 8.1.0(typescript@5.5.3)
"@svgr/plugin-jsx": 8.1.0(@svgr/core@8.1.0(typescript@5.5.3))
vite: 5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
vite: 5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1)
transitivePeerDependencies:
- rollup
- supports-color
- typescript
vite@5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1):
vite@5.3.3(@types/node@20.14.10)(sass@1.77.6)(terser@5.31.1):
dependencies:
esbuild: 0.21.5
postcss: 8.4.39
rollup: 4.18.0
optionalDependencies:
"@types/node": 20.14.9
"@types/node": 20.14.10
fsevents: 2.3.3
sass: 1.77.6
terser: 5.31.1
+259 -230
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "clash-verge"
version = "1.7.2"
version = "1.7.3"
description = "clash verge"
authors = ["zzzgydi", "wonfen", "MystiPanda"]
license = "GPL-3.0-only"
+1 -1
View File
@@ -2,7 +2,7 @@
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"package": {
"productName": "Clash Verge",
"version": "1.7.2"
"version": "1.7.3"
},
"build": {
"distDir": "../dist",
@@ -137,7 +137,7 @@ export const GroupItem = (props: Props) => {
);
};
const StyledPrimary = styled("span")`
const StyledPrimary = styled("div")`
font-size: 15px;
font-weight: 700;
line-height: 1.5;
@@ -78,10 +78,18 @@ export const GroupsEditorViewer = (props: Props) => {
const [appendSeq, setAppendSeq] = useState<IProxyGroupConfig[]>([]);
const [deleteSeq, setDeleteSeq] = useState<string[]>([]);
const filteredPrependSeq = useMemo(
() => prependSeq.filter((group) => match(group.name)),
[prependSeq, match]
);
const filteredGroupList = useMemo(
() => groupList.filter((group) => match(group.name)),
[groupList, match]
);
const filteredAppendSeq = useMemo(
() => appendSeq.filter((group) => match(group.name)),
[appendSeq, match]
);
const sensors = useSensors(
useSensor(PointerSensor),
@@ -376,6 +384,7 @@ export const GroupsEditorViewer = (props: Props) => {
}}
multiple
options={proxyPolicyList}
disableCloseOnSelect
onChange={(_, value) => value && field.onChange(value)}
renderInput={(params) => <TextField {...params} />}
/>
@@ -393,6 +402,7 @@ export const GroupsEditorViewer = (props: Props) => {
sx={{ width: "calc(100% - 150px)" }}
multiple
options={proxyProviderList}
disableCloseOnSelect
onChange={(_, value) => value && field.onChange(value)}
renderInput={(params) => <TextField {...params} />}
/>
@@ -541,23 +551,33 @@ export const GroupsEditorViewer = (props: Props) => {
<Autocomplete
multiple
options={[
"ss",
"ssr",
"direct",
"dns",
"snell",
"http",
"trojan",
"hysteria",
"hysteria2",
"tuic",
"wireguard",
"ssh",
"socks5",
"vmess",
"vless",
"Direct",
"Reject",
"RejectDrop",
"Compatible",
"Pass",
"Dns",
"Shadowsocks",
"ShadowsocksR",
"Snell",
"Socks5",
"Http",
"Vmess",
"Vless",
"Trojan",
"Hysteria",
"Hysteria2",
"WireGuard",
"Tuic",
"Relay",
"Selector",
"Fallback",
"URLTest",
"LoadBalance",
"Ssh",
]}
size="small"
disableCloseOnSelect
sx={{ width: "calc(100% - 150px)" }}
value={field.value?.split("|")}
onChange={(_, value) => {
@@ -576,7 +596,6 @@ export const GroupsEditorViewer = (props: Props) => {
<ListItemText primary={t("Expected Status")} />
<TextField
autoComplete="off"
type="number"
size="small"
sx={{ width: "calc(100% - 150px)" }}
onChange={(e) => {
@@ -705,13 +724,13 @@ export const GroupsEditorViewer = (props: Props) => {
style={{ height: "calc(100% - 24px)", marginTop: "8px" }}
totalCount={
filteredGroupList.length +
(prependSeq.length > 0 ? 1 : 0) +
(appendSeq.length > 0 ? 1 : 0)
(filteredPrependSeq.length > 0 ? 1 : 0) +
(filteredAppendSeq.length > 0 ? 1 : 0)
}
increaseViewportBy={256}
itemContent={(index) => {
let shift = prependSeq.length > 0 ? 1 : 0;
if (prependSeq.length > 0 && index === 0) {
let shift = filteredPrependSeq.length > 0 ? 1 : 0;
if (filteredPrependSeq.length > 0 && index === 0) {
return (
<DndContext
sensors={sensors}
@@ -719,11 +738,11 @@ export const GroupsEditorViewer = (props: Props) => {
onDragEnd={onPrependDragEnd}
>
<SortableContext
items={prependSeq.map((x) => {
items={filteredPrependSeq.map((x) => {
return x.name;
})}
>
{prependSeq.map((item, index) => {
{filteredPrependSeq.map((item, index) => {
return (
<GroupItem
key={`${item.name}-${index}`}
@@ -779,11 +798,11 @@ export const GroupsEditorViewer = (props: Props) => {
onDragEnd={onAppendDragEnd}
>
<SortableContext
items={appendSeq.map((x) => {
items={filteredAppendSeq.map((x) => {
return x.name;
})}
>
{appendSeq.map((item, index) => {
{filteredAppendSeq.map((item, index) => {
return (
<GroupItem
key={`${item.name}-${index}`}
@@ -61,10 +61,18 @@ export const ProxiesEditorViewer = (props: Props) => {
const [appendSeq, setAppendSeq] = useState<IProxyConfig[]>([]);
const [deleteSeq, setDeleteSeq] = useState<string[]>([]);
const filteredPrependSeq = useMemo(
() => prependSeq.filter((proxy) => match(proxy.name)),
[prependSeq, match]
);
const filteredProxyList = useMemo(
() => proxyList.filter((proxy) => match(proxy.name)),
[proxyList, match]
);
const filteredAppendSeq = useMemo(
() => appendSeq.filter((proxy) => match(proxy.name)),
[appendSeq, match]
);
const sensors = useSensors(
useSensor(PointerSensor),
@@ -119,7 +127,31 @@ export const ProxiesEditorViewer = (props: Props) => {
}
}
};
const handleParse = () => {
let proxies = [] as IProxyConfig[];
let names: string[] = [];
let uris = "";
try {
uris = atob(proxyUri);
} catch {
uris = proxyUri;
}
uris
.trim()
.split("\n")
.forEach((uri) => {
try {
let proxy = parseUri(uri.trim());
if (!names.includes(proxy.name)) {
proxies.push(proxy);
names.push(proxy.name);
}
} catch (err: any) {
Notice.error(err.message || err.toString());
}
});
return proxies;
};
const fetchProfile = async () => {
let data = await readProfileFile(profileUid);
@@ -228,7 +260,6 @@ export const ProxiesEditorViewer = (props: Props) => {
placeholder={t("Use newlines for multiple uri")}
fullWidth
rows={9}
sx={{ height: "100px" }}
multiline
size="small"
onChange={(e) => setProxyUri(e.target.value)}
@@ -240,18 +271,7 @@ export const ProxiesEditorViewer = (props: Props) => {
fullWidth
variant="contained"
onClick={() => {
let proxies = [] as IProxyConfig[];
proxyUri
.trim()
.split("\n")
.forEach((uri) => {
try {
let proxy = parseUri(uri.trim());
proxies.push(proxy);
} catch (err: any) {
Notice.error(err.message || err.toString());
}
});
let proxies = handleParse();
setPrependSeq([...prependSeq, ...proxies]);
}}
>
@@ -263,18 +283,7 @@ export const ProxiesEditorViewer = (props: Props) => {
fullWidth
variant="contained"
onClick={() => {
let proxies = [] as IProxyConfig[];
proxyUri
.trim()
.split("\n")
.forEach((uri) => {
try {
let proxy = parseUri(uri.trim());
proxies.push(proxy);
} catch (err: any) {
Notice.error(err.message || err.toString());
}
});
let proxies = handleParse();
setAppendSeq([...appendSeq, ...proxies]);
}}
>
@@ -297,13 +306,13 @@ export const ProxiesEditorViewer = (props: Props) => {
style={{ height: "calc(100% - 24px)", marginTop: "8px" }}
totalCount={
filteredProxyList.length +
(prependSeq.length > 0 ? 1 : 0) +
(appendSeq.length > 0 ? 1 : 0)
(filteredPrependSeq.length > 0 ? 1 : 0) +
(filteredAppendSeq.length > 0 ? 1 : 0)
}
increaseViewportBy={256}
itemContent={(index) => {
let shift = prependSeq.length > 0 ? 1 : 0;
if (prependSeq.length > 0 && index === 0) {
let shift = filteredPrependSeq.length > 0 ? 1 : 0;
if (filteredPrependSeq.length > 0 && index === 0) {
return (
<DndContext
sensors={sensors}
@@ -311,11 +320,11 @@ export const ProxiesEditorViewer = (props: Props) => {
onDragEnd={onPrependDragEnd}
>
<SortableContext
items={prependSeq.map((x) => {
items={filteredPrependSeq.map((x) => {
return x.name;
})}
>
{prependSeq.map((item, index) => {
{filteredPrependSeq.map((item, index) => {
return (
<ProxyItem
key={`${item.name}-${index}`}
@@ -371,11 +380,11 @@ export const ProxiesEditorViewer = (props: Props) => {
onDragEnd={onAppendDragEnd}
>
<SortableContext
items={appendSeq.map((x) => {
items={filteredAppendSeq.map((x) => {
return x.name;
})}
>
{appendSeq.map((item, index) => {
{filteredAppendSeq.map((item, index) => {
return (
<ProxyItem
key={`${item.name}-${index}`}
@@ -90,7 +90,7 @@ export const ProxyItem = (props: Props) => {
);
};
const StyledPrimary = styled("span")`
const StyledPrimary = styled("div")`
font-size: 15px;
font-weight: 700;
line-height: 1.5;
@@ -18,7 +18,12 @@ interface Props {
export const RuleItem = (props: Props) => {
let { type, ruleRaw, onDelete } = props;
const sortable = type === "prepend" || type === "append";
const rule = ruleRaw.replace(",no-resolve", "").split(",");
const rule = ruleRaw.replace(",no-resolve", "");
const ruleType = rule.match(/^[^,]+/)?.[0] ?? "";
const proxyPolicy = rule.match(/[^,]+$/)?.[0] ?? "";
const ruleContent = rule.slice(ruleType.length + 1, -proxyPolicy.length - 1);
const { attributes, listeners, setNodeRef, transform, transition } = sortable
? useSortable({ id: ruleRaw })
: {
@@ -56,7 +61,7 @@ export const RuleItem = (props: Props) => {
<StyledPrimary
sx={{ textDecoration: type === "delete" ? "line-through" : "" }}
>
{rule.length === 3 ? rule[1] : "-"}
{ruleContent || "-"}
</StyledPrimary>
}
secondary={
@@ -70,10 +75,10 @@ export const RuleItem = (props: Props) => {
}}
>
<Box sx={{ marginTop: "2px" }}>
<StyledTypeBox>{rule[0]}</StyledTypeBox>
<StyledTypeBox>{ruleType}</StyledTypeBox>
</Box>
<StyledSubtitle sx={{ color: "text.secondary" }}>
{rule.length === 3 ? rule[2] : rule[1]}
{proxyPolicy}
</StyledSubtitle>
</ListItemTextChild>
}
@@ -92,7 +97,7 @@ export const RuleItem = (props: Props) => {
);
};
const StyledPrimary = styled("span")`
const StyledPrimary = styled("div")`
font-size: 15px;
font-weight: 700;
line-height: 1.5;
@@ -254,10 +254,18 @@ export const RulesEditorViewer = (props: Props) => {
const [appendSeq, setAppendSeq] = useState<string[]>([]);
const [deleteSeq, setDeleteSeq] = useState<string[]>([]);
const filteredPrependSeq = useMemo(
() => prependSeq.filter((rule) => match(rule)),
[prependSeq, match]
);
const filteredRuleList = useMemo(
() => ruleList.filter((rule) => match(rule)),
[ruleList, match]
);
const filteredAppendSeq = useMemo(
() => appendSeq.filter((rule) => match(rule)),
[appendSeq, match]
);
const sensors = useSensors(
useSensor(PointerSensor),
@@ -573,13 +581,13 @@ export const RulesEditorViewer = (props: Props) => {
style={{ height: "calc(100% - 24px)", marginTop: "8px" }}
totalCount={
filteredRuleList.length +
(prependSeq.length > 0 ? 1 : 0) +
(appendSeq.length > 0 ? 1 : 0)
(filteredPrependSeq.length > 0 ? 1 : 0) +
(filteredAppendSeq.length > 0 ? 1 : 0)
}
increaseViewportBy={256}
itemContent={(index) => {
let shift = prependSeq.length > 0 ? 1 : 0;
if (prependSeq.length > 0 && index === 0) {
let shift = filteredPrependSeq.length > 0 ? 1 : 0;
if (filteredPrependSeq.length > 0 && index === 0) {
return (
<DndContext
sensors={sensors}
@@ -587,11 +595,11 @@ export const RulesEditorViewer = (props: Props) => {
onDragEnd={onPrependDragEnd}
>
<SortableContext
items={prependSeq.map((x) => {
items={filteredPrependSeq.map((x) => {
return x;
})}
>
{prependSeq.map((item, index) => {
{filteredPrependSeq.map((item, index) => {
return (
<RuleItem
key={`${item}-${index}`}
@@ -643,11 +651,11 @@ export const RulesEditorViewer = (props: Props) => {
onDragEnd={onAppendDragEnd}
>
<SortableContext
items={appendSeq.map((x) => {
items={filteredAppendSeq.map((x) => {
return x;
})}
>
{appendSeq.map((item, index) => {
{filteredAppendSeq.map((item, index) => {
return (
<RuleItem
key={`${item}-${index}`}
@@ -28,10 +28,10 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
let validReg;
if (getSystem() === "windows") {
validReg =
/^((\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}|(\d{1,3}\.){1,3}\d{1,3}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\*|\d{1,3}\.\d{1,3}\.\*|\d{1,3}\.\*|([a-fA-F0-9:]+:+)+[a-fA-F0-9]+|localhost|<local>)(;((\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}|(\d{1,3}\.){1,3}\d{1,3}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\*|\d{1,3}\.\d{1,3}\.\*|\d{1,3}\.\*|([a-fA-F0-9:]+:+)+[a-fA-F0-9]+|localhost|<local>))*;?$/;
/^((\*\.)?([a-zA-Z0-9-]+\.?)+(local|test|example|invalid|localhost|onion|([a-zA-Z]{2,}))|(\d{1,3}\.){1,3}\d{1,3}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\*|\d{1,3}\.\d{1,3}\.\*|\d{1,3}\.\*|([a-fA-F0-9:]+:+)+[a-fA-F0-9]+|localhost|<local>)(;((\*\.)?([a-zA-Z0-9-]+\.?)+(local|test|example|invalid|localhost|onion|([a-zA-Z]{2,}))|(\d{1,3}\.){1,3}\d{1,3}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\*|\d{1,3}\.\d{1,3}\.\*|\d{1,3}\.\*|([a-fA-F0-9:]+:+)+[a-fA-F0-9]+|localhost|<local>))*;?$/;
} else {
validReg =
/^((\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}|(\d{1,3}\.){1,3}\d{1,3}(\/\d{1,2}|\/3[0-2])?|\d{1,3}\.\d{1,3}\.\d{1,3}\.\*(\/\d{1,2}|\/3[0-2])?|\d{1,3}\.\d{1,3}\.\*(\/\d{1,2}|\/3[0-2])?|\d{1,3}\.\*(\/\d{1,2}|\/3[0-2])?|([a-fA-F0-9:]+:+)+[a-fA-F0-9]+(\/\d{1,3})?|localhost|<local>)(,((\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}|(\d{1,3}\.){1,3}\d{1,3}(\/\d{1,2}|\/3[0-2])?|\d{1,3}\.\d{1,3}\.\d{1,3}\.\*(\/\d{1,2}|\/3[0-2])?|\d{1,3}\.\d{1,3}\.\*(\/\d{1,2}|\/3[0-2])?|\d{1,3}\.\*(\/\d{1,2}|\/3[0-2])?|([a-fA-F0-9:]+:+)+[a-fA-F0-9]+(\/\d{1,3})?|localhost|<local>))*,?$/;
/^((\*\.)?([a-zA-Z0-9-]+\.?)+(local|test|example|invalid|localhost|onion|([a-zA-Z]{2,}))|(\d{1,3}\.){1,3}\d{1,3}(\/\d{1,2}|\/3[0-2])?|\d{1,3}\.\d{1,3}\.\d{1,3}\.\*(\/\d{1,2}|\/3[0-2])?|\d{1,3}\.\d{1,3}\.\*(\/\d{1,2}|\/3[0-2])?|\d{1,3}\.\*(\/\d{1,2}|\/3[0-2])?|([a-fA-F0-9:]+:+)+[a-fA-F0-9]+(\/\d{1,3})?|localhost|<local>)(,((\*\.)?([a-zA-Z0-9-]+\.?)+(local|test|example|invalid|localhost|onion|([a-zA-Z]{2,}))|(\d{1,3}\.){1,3}\d{1,3}(\/\d{1,2}|\/3[0-2])?|\d{1,3}\.\d{1,3}\.\d{1,3}\.\*(\/\d{1,2}|\/3[0-2])?|\d{1,3}\.\d{1,3}\.\*(\/\d{1,2}|\/3[0-2])?|\d{1,3}\.\*(\/3[0-2])?|([a-fA-F0-9:]+:+)+[a-fA-F0-9]+(\/\d{1,3})?|localhost|<local>))*,?$/;
}
const [open, setOpen] = useState(false);
+46 -9
View File
@@ -223,7 +223,7 @@ interface IProxyGroupConfig {
filter?: string;
"exclude-filter"?: string;
"exclude-type"?: string;
"expected-status"?: number;
"expected-status"?: string;
hidden?: boolean;
icon?: string;
}
@@ -243,7 +243,7 @@ interface HttpOptions {
method?: string;
path?: string[];
headers?: {
[key: string]: string;
[key: string]: string[];
};
}
@@ -262,7 +262,38 @@ interface RealityOptions {
}
type NetworkType = "ws" | "http" | "h2" | "grpc";
type CipherType =
| "none"
| "auto"
| "dummy"
| "aes-128-gcm"
| "aes-192-gcm"
| "aes-256-gcm"
| "lea-128-gcm"
| "lea-192-gcm"
| "lea-256-gcm"
| "aes-128-gcm-siv"
| "aes-256-gcm-siv"
| "2022-blake3-aes-128-gcm"
| "2022-blake3-aes-256-gcm"
| "aes-128-cfb"
| "aes-192-cfb"
| "aes-256-cfb"
| "aes-128-ctr"
| "aes-192-ctr"
| "aes-256-ctr"
| "chacha20"
| "chacha20-ietf"
| "chacha20-ietf-poly1305"
| "2022-blake3-chacha20-poly1305"
| "rabbit128-poly1305"
| "xchacha20-ietf-poly1305"
| "xchacha20"
| "aegis-128l"
| "aegis-256"
| "aez-384"
| "deoxys-ii-256-128"
| "rc4-md5";
// base
interface IProxyBaseConfig {
tfo?: boolean;
@@ -294,7 +325,9 @@ interface IProxyHttpConfig extends IProxyBaseConfig {
sni?: string;
"skip-cert-verify"?: boolean;
fingerprint?: string;
headers?: {};
headers?: {
[key: string]: string;
};
}
// socks5
interface IProxySocks5Config extends IProxyBaseConfig {
@@ -399,7 +432,9 @@ interface IProxyVlessConfig extends IProxyBaseConfig {
"grpc-opts"?: GrpcOptions;
"ws-opts"?: WsOptions;
"ws-path"?: string;
"ws-headers"?: {};
"ws-headers"?: {
[key: string]: string;
};
"skip-cert-verify"?: boolean;
fingerprint?: string;
servername?: string;
@@ -413,7 +448,7 @@ interface IProxyVmessConfig extends IProxyBaseConfig {
port?: number;
uuid?: string;
alterId?: number;
cipher?: string;
cipher?: CipherType;
udp?: boolean;
network?: NetworkType;
tls?: boolean;
@@ -516,7 +551,7 @@ interface IProxyShadowsocksConfig extends IProxyBaseConfig {
server?: string;
port?: number;
password?: string;
cipher?: string;
cipher?: CipherType;
udp?: boolean;
plugin?: "obfs" | "v2ray-plugin" | "shadow-tls" | "restls";
"plugin-opts"?: {
@@ -526,7 +561,9 @@ interface IProxyShadowsocksConfig extends IProxyBaseConfig {
path?: string;
tls?: string;
fingerprint?: string;
headers?: {};
headers?: {
[key: string]: string;
};
"skip-cert-verify"?: boolean;
version?: number;
mux?: boolean;
@@ -546,7 +583,7 @@ interface IProxyshadowsocksRConfig extends IProxyBaseConfig {
server?: string;
port?: number;
password?: string;
cipher?: string;
cipher?: CipherType;
obfs?: string;
"obfs-param"?: string;
protocol?: string;
+80 -13
View File
@@ -80,10 +80,83 @@ function decodeBase64OrOriginal(str: string): string {
}
}
function getCipher(str: string | undefined) {
switch (str) {
case "none":
return "none";
case "auto":
return "auto";
case "dummy":
return "dummy";
case "aes-128-gcm":
return "aes-128-gcm";
case "aes-192-gcm":
return "aes-192-gcm";
case "aes-256-gcm":
return "aes-256-gcm";
case "lea-128-gcm":
return "lea-128-gcm";
case "lea-192-gcm":
return "lea-192-gcm";
case "lea-256-gcm":
return "lea-256-gcm";
case "aes-128-gcm-siv":
return "aes-128-gcm-siv";
case "aes-256-gcm-siv":
return "aes-256-gcm-siv";
case "2022-blake3-aes-128-gcm":
return "2022-blake3-aes-128-gcm";
case "2022-blake3-aes-256-gcm":
return "2022-blake3-aes-256-gcm";
case "aes-128-cfb":
return "aes-128-cfb";
case "aes-192-cfb":
return "aes-192-cfb";
case "aes-256-cfb":
return "aes-256-cfb";
case "aes-128-ctr":
return "aes-128-ctr";
case "aes-192-ctr":
return "aes-192-ctr";
case "aes-256-ctr":
return "aes-256-ctr";
case "chacha20":
return "chacha20";
case "chacha20-poly1305":
return "chacha20-ietf-poly1305";
case "chacha20-ietf":
return "chacha20-ietf";
case "chacha20-ietf-poly1305":
return "chacha20-ietf-poly1305";
case "2022-blake3-chacha20-poly1305":
return "2022-blake3-chacha20-poly1305";
case "rabbit128-poly1305":
return "rabbit128-poly1305";
case "xchacha20-ietf-poly1305":
return "xchacha20-ietf-poly1305";
case "xchacha20":
return "xchacha20";
case "aegis-128l":
return "aegis-128l";
case "aegis-256":
return "aegis-256";
case "aez-384":
return "aez-384";
case "deoxys-ii-256-128":
return "deoxys-ii-256-128";
case "rc4-md5":
return "rc4-md5";
case undefined:
return "none";
default:
return "auto";
}
}
function URI_SS(line: string): IProxyShadowsocksConfig {
// parse url
let content = line.split("ss://")[1];
content = decodeBase64OrOriginal(content);
const proxy: IProxyShadowsocksConfig = {
name: decodeURIComponent(line.split("#")[1]).trim(),
type: "ss",
@@ -125,7 +198,7 @@ function URI_SS(line: string): IProxyShadowsocksConfig {
`${serverAndPort?.substring(portIdx + 1)}`.match(/\d+/)?.[0] ?? ""
);
const userInfo = userInfoStr.match(/(^.*?):(.*$)/);
proxy.cipher = userInfo?.[1];
proxy.cipher = getCipher(userInfo?.[1]);
proxy.password = userInfo?.[2];
// handle obfs
@@ -172,7 +245,7 @@ function URI_SS(line: string): IProxyShadowsocksConfig {
function URI_SSR(line: string): IProxyshadowsocksRConfig {
line = decodeBase64OrOriginal(line.split("ssr://")[1]);
line = decodeBase64OrOriginal(line);
// handle IPV6 & IPV4 format
let splitIdx = line.indexOf(":origin");
if (splitIdx === -1) {
@@ -194,7 +267,7 @@ function URI_SSR(line: string): IProxyshadowsocksRConfig {
server,
port,
protocol: params[0],
cipher: params[1],
cipher: getCipher(params[1]),
obfs: params[2],
password: decodeBase64OrOriginal(params[3]),
};
@@ -243,7 +316,7 @@ function URI_VMESS(line: string): IProxyVmessConfig {
type: "vmess",
server: partitions[1],
port: parseInt(partitions[2], 10),
cipher: getIfNotBlank(partitions[3], "auto"),
cipher: getCipher(getIfNotBlank(partitions[3], "auto")),
uuid: partitions[4].match(/^"(.*)"$/)?.[1] || "",
tls: params.obfs === "wss",
udp: getIfPresent(params["udp-relay"]),
@@ -320,7 +393,7 @@ function URI_VMESS(line: string): IProxyVmessConfig {
type: "vmess",
server,
port,
cipher: getIfPresent(params.scy, "auto"),
cipher: getCipher(getIfPresent(params.scy, "auto")),
uuid: params.id,
tls: ["tls", true, 1, "1"].includes(params.tls),
"skip-cert-verify": isPresent(params.verify_cert)
@@ -411,7 +484,6 @@ function URI_VMESS(line: string): IProxyVmessConfig {
function URI_VLESS(line: string): IProxyVlessConfig {
line = line.split("vless://")[1];
line = decodeBase64OrOriginal(line);
let isShadowrocket;
let parsed = /^(.*?)@(.*?):(\d+)\/?(\?(.*?))?(?:#(.*?))?$/.exec(line)!;
if (!parsed) {
@@ -578,7 +650,6 @@ function URI_Trojan(line: string): IProxyTrojanConfig {
function URI_Hysteria2(line: string): IProxyHysteria2Config {
line = line.split(/(hysteria2|hy2):\/\//)[2];
line = decodeBase64OrOriginal(line);
// eslint-disable-next-line no-unused-vars
let [__, password, server, ___, port, ____, addons = "", name] =
/^(.*?)@(.*?)(:(\d+))?\/?(\?(.*?))?(?:#(.*?))?$/.exec(line) || [];
@@ -627,7 +698,6 @@ function URI_Hysteria2(line: string): IProxyHysteria2Config {
function URI_Hysteria(line: string): IProxyHysteriaConfig {
line = line.split(/(hysteria|hy):\/\//)[2];
line = decodeBase64OrOriginal(line);
let [__, server, ___, port, ____, addons = "", name] =
/^(.*?)(:(\d+))?\/?(\?(.*?))?(?:#(.*?))?$/.exec(line)!;
let portNum = parseInt(`${port}`, 10);
@@ -723,7 +793,7 @@ function URI_Hysteria(line: string): IProxyHysteriaConfig {
function URI_TUIC(line: string): IProxyTuicConfig {
line = line.split(/tuic:\/\//)[1];
line = decodeBase64OrOriginal(line);
let [__, uuid, password, server, ___, port, ____, addons = "", name] =
/^(.*?):(.*?)@(.*?)(:(\d+))?\/?(\?(.*?))?(?:#(.*?))?$/.exec(line) || [];
@@ -803,7 +873,6 @@ function URI_TUIC(line: string): IProxyTuicConfig {
function URI_Wireguard(line: string): IProxyWireguardConfig {
line = line.split(/(wireguard|wg):\/\//)[2];
line = decodeBase64OrOriginal(line);
let [__, ___, privateKey, server, ____, port, _____, addons = "", name] =
/^((.*?)@)?(.*?)(:(\d+))?\/?(\?(.*?))?(?:#(.*?))?$/.exec(line)!;
@@ -886,7 +955,6 @@ function URI_Wireguard(line: string): IProxyWireguardConfig {
function URI_HTTP(line: string): IProxyHttpConfig {
line = line.split(/(http|https):\/\//)[2];
line = decodeBase64OrOriginal(line);
let [__, ___, auth, server, ____, port, _____, addons = "", name] =
/^((.*?)@)?(.*?)(:(\d+))?\/?(\?(.*?))?(?:#(.*?))?$/.exec(line)!;
@@ -951,7 +1019,6 @@ function URI_HTTP(line: string): IProxyHttpConfig {
function URI_SOCKS(line: string): IProxySocks5Config {
line = line.split(/socks5:\/\//)[1];
line = decodeBase64OrOriginal(line);
let [__, ___, auth, server, ____, port, _____, addons = "", name] =
/^((.*?)@)?(.*?)(:(\d+))?\/?(\?(.*?))?(?:#(.*?))?$/.exec(line)!;
+21
View File
@@ -0,0 +1,21 @@
package lib
import (
"fmt"
"io"
"net/http"
)
func getRemoteURLContent(url string) ([]byte, error) {
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to get remote content -> %s: %s", url, resp.Status)
}
return io.ReadAll(resp.Body)
}
+183
View File
@@ -0,0 +1,183 @@
package lib
import (
"fmt"
"strings"
"go4.org/netipx"
)
type Container interface {
GetEntry(name string) (*Entry, bool)
Add(entry *Entry, opts ...IgnoreIPOption) error
Remove(entry *Entry, rCase CaseRemove, opts ...IgnoreIPOption) error
Loop() <-chan *Entry
}
type container struct {
entries map[string]*Entry
}
func NewContainer() Container {
return &container{
entries: make(map[string]*Entry),
}
}
func (c *container) isValid() bool {
return c.entries != nil
}
func (c *container) GetEntry(name string) (*Entry, bool) {
if !c.isValid() {
return nil, false
}
val, ok := c.entries[strings.ToUpper(strings.TrimSpace(name))]
if !ok {
return nil, false
}
return val, true
}
func (c *container) Loop() <-chan *Entry {
ch := make(chan *Entry, 300)
go func() {
for _, val := range c.entries {
ch <- val
}
close(ch)
}()
return ch
}
func (c *container) Add(entry *Entry, opts ...IgnoreIPOption) error {
var ignoreIPType IPType
for _, opt := range opts {
if opt != nil {
ignoreIPType = opt()
}
}
name := entry.GetName()
val, found := c.GetEntry(name)
switch found {
case true:
var ipv4set, ipv6set *netipx.IPSet
var err4, err6 error
if entry.hasIPv4Builder() {
ipv4set, err4 = entry.ipv4Builder.IPSet()
if err4 != nil {
return err4
}
}
if entry.hasIPv6Builder() {
ipv6set, err6 = entry.ipv6Builder.IPSet()
if err6 != nil {
return err6
}
}
switch ignoreIPType {
case IPv4:
if !val.hasIPv6Builder() {
val.ipv6Builder = new(netipx.IPSetBuilder)
}
val.ipv6Builder.AddSet(ipv6set)
case IPv6:
if !val.hasIPv4Builder() {
val.ipv4Builder = new(netipx.IPSetBuilder)
}
val.ipv4Builder.AddSet(ipv4set)
default:
if !val.hasIPv4Builder() {
val.ipv4Builder = new(netipx.IPSetBuilder)
}
if !val.hasIPv6Builder() {
val.ipv6Builder = new(netipx.IPSetBuilder)
}
val.ipv4Builder.AddSet(ipv4set)
val.ipv6Builder.AddSet(ipv6set)
}
case false:
switch ignoreIPType {
case IPv4:
entry.ipv4Builder = nil
case IPv6:
entry.ipv6Builder = nil
}
c.entries[name] = entry
}
return nil
}
func (c *container) Remove(entry *Entry, rCase CaseRemove, opts ...IgnoreIPOption) error {
name := entry.GetName()
val, found := c.GetEntry(name)
if !found {
return fmt.Errorf("entry %s not found", name)
}
var ignoreIPType IPType
for _, opt := range opts {
if opt != nil {
ignoreIPType = opt()
}
}
switch rCase {
case CaseRemovePrefix:
var ipv4set, ipv6set *netipx.IPSet
var err4, err6 error
if entry.hasIPv4Builder() {
ipv4set, err4 = entry.ipv4Builder.IPSet()
if err4 != nil {
return err4
}
}
if entry.hasIPv6Builder() {
ipv6set, err6 = entry.ipv6Builder.IPSet()
if err6 != nil {
return err6
}
}
switch ignoreIPType {
case IPv4:
if !val.hasIPv6Builder() {
val.ipv6Builder = new(netipx.IPSetBuilder)
}
val.ipv6Builder.RemoveSet(ipv6set)
case IPv6:
if !val.hasIPv4Builder() {
val.ipv4Builder = new(netipx.IPSetBuilder)
}
val.ipv4Builder.RemoveSet(ipv4set)
default:
if !val.hasIPv4Builder() {
val.ipv4Builder = new(netipx.IPSetBuilder)
}
if !val.hasIPv6Builder() {
val.ipv6Builder = new(netipx.IPSetBuilder)
}
val.ipv4Builder.RemoveSet(ipv4set)
val.ipv6Builder.RemoveSet(ipv6set)
}
case CaseRemoveEntry:
switch ignoreIPType {
case IPv4:
val.ipv6Builder = nil
case IPv6:
val.ipv4Builder = nil
default:
delete(c.entries, name)
}
default:
return fmt.Errorf("unknown remove case %d", rCase)
}
return nil
}
@@ -2,8 +2,6 @@ package lib
import (
"fmt"
"io"
"net/http"
"sort"
"strings"
)
@@ -54,17 +52,3 @@ func RegisterOutputConverter(name string, c OutputConverter) error {
outputConverterMap[name] = c
return nil
}
func getRemoteURLContent(url string) ([]byte, error) {
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to get remote content -> %s: %s", url, resp.Status)
}
return io.ReadAll(resp.Body)
}
+328
View File
@@ -0,0 +1,328 @@
package lib
import (
"fmt"
"net"
"net/netip"
"strings"
"sync"
"go4.org/netipx"
)
type Entry struct {
name string
mu *sync.Mutex
ipv4Builder *netipx.IPSetBuilder
ipv6Builder *netipx.IPSetBuilder
}
func NewEntry(name string) *Entry {
return &Entry{
name: strings.ToUpper(strings.TrimSpace(name)),
mu: new(sync.Mutex),
ipv4Builder: new(netipx.IPSetBuilder),
ipv6Builder: new(netipx.IPSetBuilder),
}
}
func (e *Entry) GetName() string {
return e.name
}
func (e *Entry) hasIPv4Builder() bool {
return e.ipv4Builder != nil
}
func (e *Entry) hasIPv6Builder() bool {
return e.ipv6Builder != nil
}
func (e *Entry) processPrefix(src any) (*netip.Prefix, IPType, error) {
switch src := src.(type) {
case net.IP:
ip, ok := netipx.FromStdIP(src)
if !ok {
return nil, "", ErrInvalidIP
}
ip = ip.Unmap()
switch {
case ip.Is4():
prefix := netip.PrefixFrom(ip, 32)
return &prefix, IPv4, nil
case ip.Is6():
prefix := netip.PrefixFrom(ip, 128)
return &prefix, IPv6, nil
default:
return nil, "", ErrInvalidIPLength
}
case *net.IPNet:
prefix, ok := netipx.FromStdIPNet(src)
if !ok {
return nil, "", ErrInvalidIPNet
}
ip := prefix.Addr().Unmap()
switch {
case ip.Is4():
return &prefix, IPv4, nil
case ip.Is6():
return &prefix, IPv6, nil
default:
return nil, "", ErrInvalidIPLength
}
case netip.Addr:
src = src.Unmap()
switch {
case src.Is4():
prefix := netip.PrefixFrom(src, 32)
return &prefix, IPv4, nil
case src.Is6():
prefix := netip.PrefixFrom(src, 128)
return &prefix, IPv6, nil
default:
return nil, "", ErrInvalidIPLength
}
case *netip.Addr:
*src = (*src).Unmap()
switch {
case src.Is4():
prefix := netip.PrefixFrom(*src, 32)
return &prefix, IPv4, nil
case src.Is6():
prefix := netip.PrefixFrom(*src, 128)
return &prefix, IPv6, nil
default:
return nil, "", ErrInvalidIPLength
}
case netip.Prefix:
ip := src.Addr()
switch {
case ip.Is4():
prefix, err := ip.Prefix(src.Bits())
if err != nil {
return nil, "", ErrInvalidPrefix
}
return &prefix, IPv4, nil
case ip.Is4In6():
ip = ip.Unmap()
bits := src.Bits()
if bits < 96 {
return nil, "", ErrInvalidPrefix
}
prefix, err := ip.Prefix(bits - 96)
if err != nil {
return nil, "", ErrInvalidPrefix
}
return &prefix, IPv4, nil
case ip.Is6():
prefix, err := ip.Prefix(src.Bits())
if err != nil {
return nil, "", ErrInvalidPrefix
}
return &prefix, IPv6, nil
default:
return nil, "", ErrInvalidIPLength
}
case *netip.Prefix:
ip := src.Addr()
switch {
case ip.Is4():
prefix, err := ip.Prefix(src.Bits())
if err != nil {
return nil, "", ErrInvalidPrefix
}
return &prefix, IPv4, nil
case ip.Is4In6():
ip = ip.Unmap()
bits := src.Bits()
if bits < 96 {
return nil, "", ErrInvalidPrefix
}
prefix, err := ip.Prefix(bits - 96)
if err != nil {
return nil, "", ErrInvalidPrefix
}
return &prefix, IPv4, nil
case ip.Is6():
prefix, err := ip.Prefix(src.Bits())
if err != nil {
return nil, "", ErrInvalidPrefix
}
return &prefix, IPv6, nil
default:
return nil, "", ErrInvalidIPLength
}
case string:
src, _, _ = strings.Cut(src, "#")
src, _, _ = strings.Cut(src, "//")
src, _, _ = strings.Cut(src, "/*")
src = strings.TrimSpace(src)
if src == "" {
return nil, "", ErrCommentLine
}
switch strings.Contains(src, "/") {
case true: // src is CIDR notation
ip, network, err := net.ParseCIDR(src)
if err != nil {
return nil, "", ErrInvalidCIDR
}
addr, ok := netipx.FromStdIP(ip)
if !ok {
return nil, "", ErrInvalidIP
}
if addr.Unmap().Is4() && strings.Contains(network.String(), "::") { // src is invalid IPv4-mapped IPv6 address
return nil, "", ErrInvalidCIDR
}
prefix, ok := netipx.FromStdIPNet(network)
if !ok {
return nil, "", ErrInvalidIPNet
}
addr = prefix.Addr().Unmap()
switch {
case addr.Is4():
return &prefix, IPv4, nil
case addr.Is6():
return &prefix, IPv6, nil
default:
return nil, "", ErrInvalidIPLength
}
case false: // src is IP address
ip, err := netip.ParseAddr(src)
if err != nil {
return nil, "", ErrInvalidIP
}
ip = ip.Unmap()
switch {
case ip.Is4():
prefix := netip.PrefixFrom(ip, 32)
return &prefix, IPv4, nil
case ip.Is6():
prefix := netip.PrefixFrom(ip, 128)
return &prefix, IPv6, nil
default:
return nil, "", ErrInvalidIPLength
}
}
}
return nil, "", ErrInvalidPrefixType
}
func (e *Entry) add(prefix *netip.Prefix, ipType IPType) error {
e.mu.Lock()
defer e.mu.Unlock()
switch ipType {
case IPv4:
if !e.hasIPv4Builder() {
e.ipv4Builder = new(netipx.IPSetBuilder)
}
e.ipv4Builder.AddPrefix(*prefix)
case IPv6:
if !e.hasIPv6Builder() {
e.ipv6Builder = new(netipx.IPSetBuilder)
}
e.ipv6Builder.AddPrefix(*prefix)
default:
return ErrInvalidIPType
}
return nil
}
func (e *Entry) remove(prefix *netip.Prefix, ipType IPType) error {
e.mu.Lock()
defer e.mu.Unlock()
switch ipType {
case IPv4:
if e.hasIPv4Builder() {
e.ipv4Builder.RemovePrefix(*prefix)
}
case IPv6:
if e.hasIPv6Builder() {
e.ipv6Builder.RemovePrefix(*prefix)
}
default:
return ErrInvalidIPType
}
return nil
}
func (e *Entry) AddPrefix(cidr any) error {
prefix, ipType, err := e.processPrefix(cidr)
if err != nil && err != ErrCommentLine {
return err
}
if err := e.add(prefix, ipType); err != nil {
return err
}
return nil
}
func (e *Entry) RemovePrefix(cidr string) error {
prefix, ipType, err := e.processPrefix(cidr)
if err != nil && err != ErrCommentLine {
return err
}
if err := e.remove(prefix, ipType); err != nil {
return err
}
return nil
}
func (e *Entry) MarshalText(opts ...IgnoreIPOption) ([]string, error) {
var ignoreIPType IPType
for _, opt := range opts {
if opt != nil {
ignoreIPType = opt()
}
}
disableIPv4, disableIPv6 := false, false
switch ignoreIPType {
case IPv4:
disableIPv4 = true
case IPv6:
disableIPv6 = true
}
prefixSet := make([]string, 0, 1024)
if !disableIPv4 && e.hasIPv4Builder() {
ipv4set, err := e.ipv4Builder.IPSet()
if err != nil {
return nil, err
}
prefixes := ipv4set.Prefixes()
for _, prefix := range prefixes {
prefixSet = append(prefixSet, prefix.String())
}
}
if !disableIPv6 && e.hasIPv6Builder() {
ipv6set, err := e.ipv6Builder.IPSet()
if err != nil {
return nil, err
}
prefixes := ipv6set.Prefixes()
for _, prefix := range prefixes {
prefixSet = append(prefixSet, prefix.String())
}
}
if len(prefixSet) > 0 {
return prefixSet, nil
}
return nil, fmt.Errorf("entry %s has no prefix", e.GetName())
}
+2
View File
@@ -10,6 +10,8 @@ var (
ErrInvalidIP = errors.New("invalid IP address")
ErrInvalidIPLength = errors.New("invalid IP address length")
ErrInvalidIPNet = errors.New("invalid IPNet address")
ErrInvalidCIDR = errors.New("invalid CIDR")
ErrInvalidPrefix = errors.New("invalid prefix")
ErrInvalidPrefixType = errors.New("invalid prefix type")
ErrCommentLine = errors.New("comment line")
)
+5 -420
View File
@@ -1,16 +1,5 @@
package lib
import (
"fmt"
"log"
"net"
"net/netip"
"strings"
"sync"
"go4.org/netipx"
)
const (
ActionAdd Action = "add"
ActionRemove Action = "remove"
@@ -18,6 +7,9 @@ const (
IPv4 IPType = "ipv4"
IPv6 IPType = "ipv6"
CaseRemovePrefix CaseRemove = 0
CaseRemoveEntry CaseRemove = 1
)
var ActionsRegistry = map[Action]bool{
@@ -30,6 +22,8 @@ type Action string
type IPType string
type CaseRemove int
type Typer interface {
GetType() string
}
@@ -56,280 +50,6 @@ type OutputConverter interface {
Output(Container) error
}
type Entry struct {
name string
mu *sync.Mutex
ipv4Builder *netipx.IPSetBuilder
ipv6Builder *netipx.IPSetBuilder
}
func NewEntry(name string) *Entry {
return &Entry{
name: strings.ToUpper(strings.TrimSpace(name)),
mu: new(sync.Mutex),
ipv4Builder: new(netipx.IPSetBuilder),
ipv6Builder: new(netipx.IPSetBuilder),
}
}
func (e *Entry) GetName() string {
return e.name
}
func (e *Entry) hasIPv4Builder() bool {
return e.ipv4Builder != nil
}
func (e *Entry) hasIPv6Builder() bool {
return e.ipv6Builder != nil
}
func (e *Entry) processPrefix(src any) (*netip.Prefix, IPType, error) {
switch src := src.(type) {
case net.IP:
ip, ok := netipx.FromStdIP(src)
if !ok {
return nil, "", ErrInvalidIP
}
switch {
case ip.Is4():
prefix := netip.PrefixFrom(ip, 32)
return &prefix, IPv4, nil
case ip.Is6():
prefix := netip.PrefixFrom(ip, 128)
return &prefix, IPv6, nil
default:
return nil, "", ErrInvalidIPLength
}
case *net.IPNet:
prefix, ok := netipx.FromStdIPNet(src)
if !ok {
return nil, "", ErrInvalidIPNet
}
ip := prefix.Addr()
switch {
case ip.Is4():
return &prefix, IPv4, nil
case ip.Is6():
return &prefix, IPv6, nil
default:
return nil, "", ErrInvalidIPLength
}
case netip.Addr:
switch {
case src.Is4():
prefix := netip.PrefixFrom(src, 32)
return &prefix, IPv4, nil
case src.Is6():
prefix := netip.PrefixFrom(src, 128)
return &prefix, IPv6, nil
default:
return nil, "", ErrInvalidIPLength
}
case *netip.Addr:
switch {
case src.Is4():
prefix := netip.PrefixFrom(*src, 32)
return &prefix, IPv4, nil
case src.Is6():
prefix := netip.PrefixFrom(*src, 128)
return &prefix, IPv6, nil
default:
return nil, "", ErrInvalidIPLength
}
case netip.Prefix:
ip := src.Addr()
switch {
case ip.Is4():
return &src, IPv4, nil
case ip.Is6():
return &src, IPv6, nil
default:
return nil, "", ErrInvalidIPLength
}
case *netip.Prefix:
ip := src.Addr()
switch {
case ip.Is4():
return src, IPv4, nil
case ip.Is6():
return src, IPv6, nil
default:
return nil, "", ErrInvalidIPLength
}
case string:
src, _, _ = strings.Cut(src, "#")
src, _, _ = strings.Cut(src, "//")
src, _, _ = strings.Cut(src, "/*")
src = strings.TrimSpace(src)
if src == "" {
return nil, "", ErrCommentLine
}
_, network, err := net.ParseCIDR(src)
switch err {
case nil:
prefix, err2 := netip.ParsePrefix(network.String())
if err2 != nil {
return nil, "", ErrInvalidIPNet
}
ip := prefix.Addr()
switch {
case ip.Is4():
return &prefix, IPv4, nil
case ip.Is6():
return &prefix, IPv6, nil
default:
return nil, "", ErrInvalidIPLength
}
default:
ip, err := netip.ParseAddr(src)
if err != nil {
return nil, "", err
}
switch {
case ip.Is4():
prefix := netip.PrefixFrom(ip, 32)
return &prefix, IPv4, nil
case ip.Is4In6():
_, network, err2 := net.ParseCIDR(src + "/128")
if err2 != nil {
return nil, "", ErrInvalidIPNet
}
prefix, err3 := netip.ParsePrefix(network.String())
if err3 != nil {
return nil, "", ErrInvalidIPNet
}
return &prefix, IPv4, nil
case ip.Is6():
prefix := netip.PrefixFrom(ip, 128)
return &prefix, IPv6, nil
default:
return nil, "", ErrInvalidIPLength
}
}
}
return nil, "", ErrInvalidPrefixType
}
func (e *Entry) add(prefix *netip.Prefix, ipType IPType) error {
e.mu.Lock()
defer e.mu.Unlock()
switch ipType {
case IPv4:
if !e.hasIPv4Builder() {
e.ipv4Builder = new(netipx.IPSetBuilder)
}
e.ipv4Builder.AddPrefix(*prefix)
case IPv6:
if !e.hasIPv6Builder() {
e.ipv6Builder = new(netipx.IPSetBuilder)
}
e.ipv6Builder.AddPrefix(*prefix)
default:
return ErrInvalidIPType
}
return nil
}
func (e *Entry) remove(prefix *netip.Prefix, ipType IPType) error {
e.mu.Lock()
defer e.mu.Unlock()
switch ipType {
case IPv4:
if e.hasIPv4Builder() {
e.ipv4Builder.RemovePrefix(*prefix)
}
case IPv6:
if e.hasIPv6Builder() {
e.ipv6Builder.RemovePrefix(*prefix)
}
default:
return ErrInvalidIPType
}
return nil
}
func (e *Entry) AddPrefix(cidr any) error {
prefix, ipType, err := e.processPrefix(cidr)
if err != nil && err != ErrCommentLine {
return err
}
if err := e.add(prefix, ipType); err != nil {
return err
}
return nil
}
func (e *Entry) RemovePrefix(cidr string) error {
prefix, ipType, err := e.processPrefix(cidr)
if err != nil && err != ErrCommentLine {
return err
}
if err := e.remove(prefix, ipType); err != nil {
return err
}
return nil
}
func (e *Entry) MarshalText(opts ...IgnoreIPOption) ([]string, error) {
var ignoreIPType IPType
for _, opt := range opts {
if opt != nil {
ignoreIPType = opt()
}
}
disableIPv4, disableIPv6 := false, false
switch ignoreIPType {
case IPv4:
disableIPv4 = true
case IPv6:
disableIPv6 = true
}
prefixSet := make([]string, 0, 1024)
if !disableIPv4 && e.hasIPv4Builder() {
ipv4set, err := e.ipv4Builder.IPSet()
if err != nil {
return nil, err
}
prefixes := ipv4set.Prefixes()
for _, prefix := range prefixes {
prefixSet = append(prefixSet, prefix.String())
}
}
if !disableIPv6 && e.hasIPv6Builder() {
ipv6set, err := e.ipv6Builder.IPSet()
if err != nil {
return nil, err
}
prefixes := ipv6set.Prefixes()
for _, prefix := range prefixes {
prefixSet = append(prefixSet, prefix.String())
}
}
if len(prefixSet) > 0 {
return prefixSet, nil
}
return nil, fmt.Errorf("entry %s has no prefix", e.GetName())
}
type IgnoreIPOption func() IPType
func IgnoreIPv4() IPType {
@@ -339,138 +59,3 @@ func IgnoreIPv4() IPType {
func IgnoreIPv6() IPType {
return IPv6
}
type Container interface {
GetEntry(name string) (*Entry, bool)
Add(entry *Entry, opts ...IgnoreIPOption) error
Remove(name string, opts ...IgnoreIPOption)
Loop() <-chan *Entry
}
type container struct {
entries *sync.Map // map[name]*Entry
}
func NewContainer() Container {
return &container{
entries: new(sync.Map),
}
}
func (c *container) isValid() bool {
if c == nil || c.entries == nil {
return false
}
return true
}
func (c *container) GetEntry(name string) (*Entry, bool) {
if !c.isValid() {
return nil, false
}
val, ok := c.entries.Load(strings.ToUpper(strings.TrimSpace(name)))
if !ok {
return nil, false
}
return val.(*Entry), true
}
func (c *container) Loop() <-chan *Entry {
ch := make(chan *Entry, 300)
go func() {
c.entries.Range(func(key, value any) bool {
ch <- value.(*Entry)
return true
})
close(ch)
}()
return ch
}
func (c *container) Add(entry *Entry, opts ...IgnoreIPOption) error {
var ignoreIPType IPType
for _, opt := range opts {
if opt != nil {
ignoreIPType = opt()
}
}
name := entry.GetName()
val, found := c.GetEntry(name)
switch found {
case true:
var ipv4set, ipv6set *netipx.IPSet
var err4, err6 error
if entry.hasIPv4Builder() {
ipv4set, err4 = entry.ipv4Builder.IPSet()
if err4 != nil {
return err4
}
}
if entry.hasIPv6Builder() {
ipv6set, err6 = entry.ipv6Builder.IPSet()
if err6 != nil {
return err6
}
}
switch ignoreIPType {
case IPv4:
if !val.hasIPv6Builder() {
val.ipv6Builder = new(netipx.IPSetBuilder)
}
val.ipv6Builder.AddSet(ipv6set)
case IPv6:
if !val.hasIPv4Builder() {
val.ipv4Builder = new(netipx.IPSetBuilder)
}
val.ipv4Builder.AddSet(ipv4set)
default:
if !val.hasIPv4Builder() {
val.ipv4Builder = new(netipx.IPSetBuilder)
}
if !val.hasIPv6Builder() {
val.ipv6Builder = new(netipx.IPSetBuilder)
}
val.ipv4Builder.AddSet(ipv4set)
val.ipv6Builder.AddSet(ipv6set)
}
c.entries.Store(name, val)
case false:
switch ignoreIPType {
case IPv4:
entry.ipv4Builder = nil
case IPv6:
entry.ipv6Builder = nil
}
c.entries.Store(name, entry)
}
return nil
}
func (c *container) Remove(name string, opts ...IgnoreIPOption) {
val, found := c.GetEntry(name)
if !found {
log.Printf("failed to remove non-existent entry %s", name)
return
}
var ignoreIPType IPType
for _, opt := range opts {
if opt != nil {
ignoreIPType = opt()
}
}
switch ignoreIPType {
case IPv4:
val.ipv6Builder = nil
c.entries.Store(name, val)
case IPv6:
val.ipv4Builder = nil
c.entries.Store(name, val)
default:
c.entries.Delete(name)
}
}
+11 -6
View File
@@ -4,6 +4,7 @@ import (
"encoding/csv"
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
@@ -113,6 +114,10 @@ func (g *geoLite2CountryCSV) Input(container lib.Container) (lib.Container, erro
}
}
if len(entries) == 0 {
return nil, fmt.Errorf("❌ [type %s | action %s] no entry is generated", typeCountryCSV, g.Action)
}
var ignoreIPType lib.IgnoreIPOption
switch g.OnlyIPType {
case lib.IPv4:
@@ -121,14 +126,16 @@ func (g *geoLite2CountryCSV) Input(container lib.Container) (lib.Container, erro
ignoreIPType = lib.IgnoreIPv4
}
for name, entry := range entries {
for _, entry := range entries {
switch g.Action {
case lib.ActionAdd:
if err := container.Add(entry, ignoreIPType); err != nil {
return nil, err
}
case lib.ActionRemove:
container.Remove(name, ignoreIPType)
if err := container.Remove(entry, lib.CaseRemovePrefix, ignoreIPType); err != nil {
return nil, err
}
default:
return nil, lib.ErrUnknownAction
}
@@ -193,10 +200,8 @@ func (g *geoLite2CountryCSV) process(file string, ccMap map[string]string, entri
for _, line := range lines[1:] {
ccID := strings.TrimSpace(line[1])
if countryCode, found := ccMap[ccID]; found {
if len(wantList) > 0 {
if _, found := wantList[countryCode]; !found {
continue
}
if len(wantList) > 0 && !wantList[countryCode] {
continue
}
cidrStr := strings.ToLower(strings.TrimSpace(line[0]))
entry, found := entries[countryCode]
+10 -15
View File
@@ -107,7 +107,7 @@ func (g *maxmindMMDBIn) Input(container lib.Container) (lib.Container, error) {
}
if len(entries) == 0 {
return nil, fmt.Errorf("❌ [type %s | action %s] no entry is newly generated", typeMaxmindMMDBIn, g.Action)
return nil, fmt.Errorf("❌ [type %s | action %s] no entry is generated", typeMaxmindMMDBIn, g.Action)
}
var ignoreIPType lib.IgnoreIPOption
@@ -138,7 +138,11 @@ func (g *maxmindMMDBIn) Input(container lib.Container) (lib.Container, error) {
return nil, err
}
case lib.ActionRemove:
container.Remove(name, ignoreIPType)
if err := container.Remove(entry, lib.CaseRemovePrefix, ignoreIPType); err != nil {
return nil, err
}
default:
return nil, lib.ErrUnknownAction
}
}
@@ -197,23 +201,14 @@ func (g *maxmindMMDBIn) generateEntries(entries map[string]*lib.Entry) error {
continue
}
var entry *lib.Entry
name := strings.ToUpper(record.Country.IsoCode)
if theEntry, found := entries[name]; found {
entry = theEntry
} else {
entry, found := entries[name]
if !found {
entry = lib.NewEntry(name)
}
switch g.Action {
case lib.ActionAdd:
if err := entry.AddPrefix(subnet); err != nil {
return err
}
case lib.ActionRemove:
if err := entry.RemovePrefix(subnet.String()); err != nil {
return err
}
if err := entry.AddPrefix(subnet); err != nil {
return err
}
entries[name] = entry
+6 -2
View File
@@ -106,7 +106,7 @@ func (t *textIn) Input(container lib.Container) (lib.Container, error) {
}
if len(entries) == 0 {
return nil, fmt.Errorf("type %s | action %s no entry are generated", t.Type, t.Action)
return nil, fmt.Errorf("type %s | action %s no entry is generated", t.Type, t.Action)
}
for _, entry := range entries {
@@ -116,7 +116,11 @@ func (t *textIn) Input(container lib.Container) (lib.Container, error) {
return nil, err
}
case lib.ActionRemove:
container.Remove(entry.GetName(), ignoreIPType)
if err := container.Remove(entry, lib.CaseRemovePrefix, ignoreIPType); err != nil {
return nil, err
}
default:
return nil, lib.ErrUnknownAction
}
}
+12 -17
View File
@@ -114,7 +114,7 @@ func (s *srsIn) Input(container lib.Container) (lib.Container, error) {
}
if len(entries) == 0 {
return nil, fmt.Errorf("type %s | action %s no entry are generated", s.Type, s.Action)
return nil, fmt.Errorf("type %s | action %s no entry is generated", s.Type, s.Action)
}
for _, entry := range entries {
@@ -124,7 +124,11 @@ func (s *srsIn) Input(container lib.Container) (lib.Container, error) {
return nil, err
}
case lib.ActionRemove:
container.Remove(entry.GetName(), ignoreIPType)
if err := container.Remove(entry, lib.CaseRemovePrefix, ignoreIPType); err != nil {
return nil, err
}
default:
return nil, lib.ErrUnknownAction
}
}
@@ -200,10 +204,9 @@ func (s *srsIn) walkRemoteFile(url, name string, entries map[string]*lib.Entry)
}
func (s *srsIn) generateEntries(name string, reader io.Reader, entries map[string]*lib.Entry) error {
entry := lib.NewEntry(name)
if theEntry, found := entries[entry.GetName()]; found {
fmt.Printf("⚠️ [type %s | action %s] found duplicated entry: %s. Process anyway\n", typeSRSIn, s.Action, name)
entry = theEntry
entry, found := entries[name]
if !found {
entry = lib.NewEntry(name)
}
plainRuleSet, err := srs.Read(reader, true)
@@ -213,20 +216,12 @@ func (s *srsIn) generateEntries(name string, reader io.Reader, entries map[strin
for _, rule := range plainRuleSet.Rules {
for _, cidrStr := range rule.DefaultOptions.IPCIDR {
switch s.Action {
case lib.ActionAdd:
if err := entry.AddPrefix(cidrStr); err != nil {
return err
}
case lib.ActionRemove:
if err := entry.RemovePrefix(cidrStr); err != nil {
return err
}
if err := entry.AddPrefix(cidrStr); err != nil {
return err
}
}
}
entries[entry.GetName()] = entry
entries[name] = entry
return nil
}
+3 -1
View File
@@ -89,7 +89,9 @@ func (c *cutter) Input(container lib.Container) (lib.Container, error) {
if len(wantList) > 0 && !wantList[name] {
continue
}
container.Remove(name, ignoreIPType)
if err := container.Remove(entry, lib.CaseRemoveEntry, ignoreIPType); err != nil {
return nil, err
}
}
return container, nil
+3 -1
View File
@@ -85,7 +85,9 @@ func (p *private) Input(container lib.Container) (lib.Container, error) {
return nil, err
}
case lib.ActionRemove:
container.Remove(entryNamePrivate)
if err := container.Remove(entry, lib.CaseRemovePrefix); err != nil {
return nil, err
}
default:
return nil, lib.ErrUnknownAction
}
+18 -11
View File
@@ -3,6 +3,7 @@ package special
import (
"bufio"
"encoding/json"
"fmt"
"os"
"strings"
@@ -35,6 +36,10 @@ func newStdin(action lib.Action, data json.RawMessage) (lib.InputConverter, erro
}
}
if tmp.Name == "" {
return nil, fmt.Errorf("type %s | action %s missing name", typeStdin, action)
}
return &stdin{
Type: typeStdin,
Action: action,
@@ -81,15 +86,8 @@ func (s *stdin) Input(container lib.Container) (lib.Container, error) {
continue
}
switch s.Action {
case lib.ActionAdd:
if err := entry.AddPrefix(line); err != nil {
continue
}
case lib.ActionRemove:
if err := entry.RemovePrefix(line); err != nil {
continue
}
if err := entry.AddPrefix(line); err != nil {
continue
}
}
@@ -105,8 +103,17 @@ func (s *stdin) Input(container lib.Container) (lib.Container, error) {
ignoreIPType = lib.IgnoreIPv4
}
if err := container.Add(entry, ignoreIPType); err != nil {
return nil, err
switch s.Action {
case lib.ActionAdd:
if err := container.Add(entry, ignoreIPType); err != nil {
return nil, err
}
case lib.ActionRemove:
if err := container.Remove(entry, lib.CaseRemovePrefix, ignoreIPType); err != nil {
return nil, err
}
default:
return nil, lib.ErrUnknownAction
}
return container, nil
+9 -23
View File
@@ -74,31 +74,17 @@ func (s *stdout) Output(container lib.Container) error {
}
}
switch len(wantList) {
case 0:
for entry := range container.Loop() {
cidrList, err := s.generateCIDRList(entry)
if err != nil {
continue
}
for _, cidr := range cidrList {
io.WriteString(os.Stdout, cidr+"\n")
}
for entry := range container.Loop() {
if len(wantList) > 0 && !wantList[entry.GetName()] {
continue
}
default:
for name := range wantList {
entry, found := container.GetEntry(name)
if !found {
continue
}
cidrList, err := s.generateCIDRList(entry)
if err != nil {
continue
}
for _, cidr := range cidrList {
io.WriteString(os.Stdout, cidr+"\n")
}
cidrList, err := s.generateCIDRList(entry)
if err != nil {
continue
}
for _, cidr := range cidrList {
io.WriteString(os.Stdout, cidr+"\n")
}
}
+3 -1
View File
@@ -65,7 +65,9 @@ func (t *test) Input(container lib.Container) (lib.Container, error) {
return nil, err
}
case lib.ActionRemove:
container.Remove(entryNameTest)
if err := container.Remove(entry, lib.CaseRemovePrefix); err != nil {
return nil, err
}
default:
return nil, lib.ErrUnknownAction
}
+10 -16
View File
@@ -92,7 +92,7 @@ func (g *geoIPDatIn) Input(container lib.Container) (lib.Container, error) {
}
if len(entries) == 0 {
return nil, fmt.Errorf("❌ [type %s | action %s] no entry is newly generated", typeGeoIPdatIn, g.Action)
return nil, fmt.Errorf("❌ [type %s | action %s] no entry is generated", typeGeoIPdatIn, g.Action)
}
var ignoreIPType lib.IgnoreIPOption
@@ -123,7 +123,11 @@ func (g *geoIPDatIn) Input(container lib.Container) (lib.Container, error) {
return nil, err
}
case lib.ActionRemove:
container.Remove(name, ignoreIPType)
if err := container.Remove(entry, lib.CaseRemovePrefix, ignoreIPType); err != nil {
return nil, err
}
default:
return nil, lib.ErrUnknownAction
}
}
@@ -174,26 +178,16 @@ func (g *geoIPDatIn) generateEntries(reader io.Reader, entries map[string]*lib.E
}
for _, geoip := range geoipList.Entry {
var entry *lib.Entry
name := geoip.CountryCode
if theEntry, found := entries[name]; found {
fmt.Printf("⚠️ [type %s | action %s] found duplicated entry: %s. Process anyway\n", typeGeoIPdatIn, g.Action, name)
entry = theEntry
} else {
entry, found := entries[name]
if !found {
entry = lib.NewEntry(name)
}
for _, v2rayCIDR := range geoip.Cidr {
ipStr := net.IP(v2rayCIDR.GetIp()).String() + "/" + fmt.Sprint(v2rayCIDR.GetPrefix())
switch g.Action {
case lib.ActionAdd:
if err := entry.AddPrefix(ipStr); err != nil {
return err
}
case lib.ActionRemove:
if err := entry.RemovePrefix(ipStr); err != nil {
return err
}
if err := entry.AddPrefix(ipStr); err != nil {
return err
}
}
+3 -3
View File
@@ -155,10 +155,10 @@ func (g *geoIPDatOut) Output(container lib.Container) error {
}
}
// Sort to make reproducible builds
g.sort(geoIPList)
if !g.OneFilePerList && updated {
// Sort to make reproducible builds
g.sort(geoIPList)
geoIPBytes, err := proto.Marshal(geoIPList)
if err != nil {
return err
@@ -305,10 +305,10 @@ generate_static_system() {
set system.ntp='timeserver'
set system.ntp.enabled='1'
set system.ntp.enable_server='1'
add_list system.ntp.server='ntp.aliyun.com'
add_list system.ntp.server='time1.cloud.tencent.com'
add_list system.ntp.server='time.ustc.edu.cn'
add_list system.ntp.server='cn.pool.ntp.org'
add_list system.ntp.server='time.apple.com'
add_list system.ntp.server='time.google.com'
add_list system.ntp.server='time.windows.com'
add_list system.ntp.server='time.cloudflare.com'
EOF
if json_is_a system object; then
@@ -3,8 +3,16 @@
uci set luci.main.lang=zh_cn
uci commit luci
uci set system.@system[0].timezone=CST-8
uci set system.@system[0].zonename=Asia/Shanghai
uci -q batch <<-EOF
set system.@system[0].timezone='CST-8'
set system.@system[0].zonename='Asia/Shanghai'
delete system.ntp.server
add_list system.ntp.server='ntp1.aliyun.com'
add_list system.ntp.server='ntp.tencent.com'
add_list system.ntp.server='ntp.ntsc.ac.cn'
add_list system.ntp.server='time.ustc.edu.cn'
EOF
uci commit system
uci set fstab.@global[0].anon_mount=1
@@ -28,9 +36,7 @@ sed -i 's/\"services\"/\"nas\"/g' /usr/lib/lua/luci/controller/usb_printer.lua
sed -i 's/\"services\"/\"nas\"/g' /usr/lib/lua/luci/controller/xunlei.lua
sed -i 's/services/nas/g' /usr/lib/lua/luci/view/minidlna_status.htm
#ln -sf /sbin/ip /usr/bin/ip
sed -i 's#downloads.openwrt.org#mirrors.cloud.tencent.com/lede#g' /etc/opkg/distfeeds.conf
sed -i 's#downloads.openwrt.org#mirrors.tencent.com/lede#g' /etc/opkg/distfeeds.conf
sed -i 's/root::0:0:99999:7:::/root:$1$V4UetPzk$CYXluq4wUazHjmCDBCqXF.:0:0:99999:7:::/g' /etc/shadow
sed -i 's/root:::0:99999:7:::/root:$1$V4UetPzk$CYXluq4wUazHjmCDBCqXF.:0:0:99999:7:::/g' /etc/shadow
@@ -40,12 +46,6 @@ sed -i '/openwrt_luci/ { s/snapshots/releases\/18.06.9/g; }' /etc/opkg/distfeed
sed -i '/check_signature/d' /etc/opkg.conf
sed -i '/REDIRECT --to-ports 53/d' /etc/firewall.user
#echo 'iptables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 53' >> /etc/firewall.user
#echo 'iptables -t nat -A PREROUTING -p tcp --dport 53 -j REDIRECT --to-ports 53' >> /etc/firewall.user
#echo '[ -n "$(command -v ip6tables)" ] && ip6tables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 53' >> /etc/firewall.user
#echo '[ -n "$(command -v ip6tables)" ] && ip6tables -t nat -A PREROUTING -p tcp --dport 53 -j REDIRECT --to-ports 53' >> /etc/firewall.user
#echo 'iptables -A OUTPUT -m string --string "api.installer.xiaomi.cn" --algo bm --to 65535 -j DROP' >> /etc/firewall.user
sed -i '/option disabled/d' /etc/config/wireless
sed -i '/set wireless.radio${devidx}.disabled/d' /lib/wifi/mac80211.sh
@@ -58,11 +58,7 @@ echo "DISTRIB_DESCRIPTION='OpenWrt '" >> /etc/openwrt_release
sed -i '/log-facility/d' /etc/dnsmasq.conf
echo "log-facility=/dev/null" >> /etc/dnsmasq.conf
if [ -f /www/luci-static/resources/luci.js ]; then
sed -i 's/ifname/device/g' /etc/config/network
else
sed -i 's/device/ifname/g' /etc/config/network
fi
#ln -sf /sbin/ip /usr/bin/ip
rm -rf /tmp/luci-modulecache/
rm -f /tmp/luci-indexcache
@@ -184,25 +184,25 @@
&dp1 {
status = "okay";
phy-handle = <&qca8075_0>;
label = "wan";
label = "lan1";
};
&dp2 {
status = "okay";
phy-handle = <&qca8075_1>;
label = "lan1";
label = "lan2";
};
&dp3 {
status = "okay";
phy-handle = <&qca8075_2>;
label = "lan2";
label = "lan3";
};
&dp4 {
status = "okay";
phy-handle = <&qca8075_3>;
label = "lan3";
label = "wan";
};
&wifi {
@@ -271,6 +271,5 @@
&wifi {
status = "okay";
qcom,ath11k-calibration-variant = "JDC-AX1800-Pro";
qcom,ath11k-fw-memory-mode = <1>;
qcom,ath11k-calibration-variant = "JDC-AX6600";
};
+2 -5
View File
@@ -12,6 +12,7 @@ PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/AdguardTeam/AdGuardHome/tar.gz/v$(PKG_VERSION)?
PKG_HASH:=de6d99c4420d131b76e5d22b58ac91159e74e5783f02ada8b2f993f353e254f9
PKG_BUILD_DIR:=$(BUILD_DIR)/AdGuardHome-$(PKG_VERSION)
PKG_LICENSE:=GPL-3.0-only
PKG_LICENSE_FILES:=LICENSE.txt
@@ -57,17 +58,13 @@ define Download/adguardhome-frontend
URL:=https://github.com/AdguardTeam/AdGuardHome/releases/download/v$(PKG_VERSION)/
URL_FILE:=AdGuardHome_frontend.tar.gz
FILE:=$(FRONTEND_FILE)
HASH:=af9ae57b55a09a0aaf7c9a69a46734827443f98135d4c4b176874de3f9a449d8
HASH:=af9ae57b55a09a0aaf7c9a69a46734827443f98135d4c4b176874de3f9a449d8
endef
define Build/Prepare
$(call Build/Prepare/Default)
if [ -d "$(BUILD_DIR)/AdGuardHome-$(PKG_VERSION)" ]; then \
mv "$(BUILD_DIR)/AdGuardHome-$(PKG_VERSION)/"* "$(BUILD_DIR)/adguardhome-$(PKG_VERSION)/"; \
fi
gzip -dc $(DL_DIR)/$(FRONTEND_FILE) | $(HOST_TAR) -C $(PKG_BUILD_DIR)/ $(TAR_OPTIONS)
( cd "$(BUILD_DIR)/adguardhome-$(PKG_VERSION)"; go mod tidy )
endef
define Package/adguardhome/install
@@ -1111,7 +1111,7 @@ add_firewall_rule() {
$ip6t_m -I OUTPUT $(comment "mangle-OUTPUT-PSW") -o lo -j RETURN
insert_rule_before "$ip6t_m" "OUTPUT" "mwan3" "$(comment mangle-OUTPUT-PSW) -m mark --mark 1 -j RETURN"
[ $(config_t_get global dns_redirect) == "1" ] && {
[ $(config_t_get global dns_redirect "0") = "1" ] && {
$ipt_m -A PSW -p udp --dport 53 -j RETURN
$ip6t_m -A PSW -p udp --dport 53 -j RETURN
$ipt_n -I PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 53 -m comment --comment "PSW_DNS_Hijack" 2>/dev/null
@@ -1152,7 +1152,7 @@ add_firewall_rule() {
nft "add rule inet fw4 mangle_output oif lo counter return comment \"PSW_OUTPUT_MANGLE\""
nft "add rule inet fw4 mangle_output meta mark 1 counter return comment \"PSW_OUTPUT_MANGLE\""
[ $(config_t_get global dns_redirect) == "1" ] && {
[ $(config_t_get global dns_redirect "0") = "1" ] && {
nft "add rule inet fw4 PSW_MANGLE ip protocol udp udp dport 53 counter return"
nft "add rule inet fw4 PSW_MANGLE_V6 meta l4proto udp udp dport 53 counter return"
nft insert rule inet fw4 dstnat position 0 tcp dport 53 counter redirect to :53 comment \"PSW_DNS_Hijack\" 2>/dev/null
@@ -1,6 +1,6 @@
//! AEAD packet I/O facilities
//!
//! AEAD protocol is defined in <https://shadowsocks.org/en/spec/AEAD.html>.
//! AEAD protocol is defined in <https://shadowsocks.org/doc/aead.html>.
//!
//! ```plain
//! TCP request (before encryption)
@@ -305,7 +305,7 @@ impl DecryptedReader {
};
if plen > MAX_PACKET_SIZE {
// https://shadowsocks.org/en/spec/AEAD-Ciphers.html
// https://shadowsocks.org/doc/aead.html
//
// AEAD TCP protocol have reserved the higher two bits for future use
return Err(ProtocolError::DataTooLong(plen));
+2 -2
View File
@@ -181,7 +181,7 @@ pub fn define_command_line_options(mut app: Command) -> Command {
.action(ArgAction::Set)
.value_hint(ValueHint::CommandName)
.requires("SERVER_ADDR")
.help("SIP003 (https://shadowsocks.org/guide/sip003.html) plugin"),
.help("SIP003 (https://shadowsocks.org/doc/sip003.html) plugin"),
)
.arg(
Arg::new("PLUGIN_MODE")
@@ -206,7 +206,7 @@ pub fn define_command_line_options(mut app: Command) -> Command {
.action(ArgAction::Set)
.value_hint(ValueHint::Url)
.value_parser(vparser::parse_server_url)
.help("Server address in SIP002 (https://shadowsocks.org/guide/sip002.html) URL"),
.help("Server address in SIP002 (https://shadowsocks.org/doc/sip002.html) URL"),
)
.group(ArgGroup::new("SERVER_CONFIG")
.arg("SERVER_ADDR").arg("SERVER_URL").multiple(true))
+1 -1
View File
@@ -93,7 +93,7 @@ pub fn define_command_line_options(mut app: Command) -> Command {
.num_args(1)
.action(ArgAction::Set)
.value_hint(ValueHint::CommandName)
.help("Default SIP003 (https://shadowsocks.org/guide/sip003.html) plugin"),
.help("Default SIP003 (https://shadowsocks.org/doc/sip003.html) plugin"),
)
.arg(
Arg::new("PLUGIN_MODE")
+2 -2
View File
@@ -121,7 +121,7 @@ pub fn define_command_line_options(mut app: Command) -> Command {
.action(ArgAction::Set)
.value_hint(ValueHint::CommandName)
.requires("SERVER_ADDR")
.help("SIP003 (https://shadowsocks.org/guide/sip003.html) plugin"),
.help("SIP003 (https://shadowsocks.org/doc/sip003.html) plugin"),
)
.arg(
Arg::new("PLUGIN_MODE")
@@ -494,7 +494,7 @@ pub fn create(matches: &ArgMatches) -> Result<(Runtime, impl Future<Output = Exi
eprintln!(
"missing proxy servers, consider specifying it by \
--server-addr, --encrypt-method, --password command line option, \
or configuration file, check more details in https://shadowsocks.org/guide/configs.html"
or configuration file, check more details in https://shadowsocks.org/doc/configs.html"
);
return Err(crate::EXIT_CODE_INSUFFICIENT_PARAMS.into());
}
+1 -1
View File
@@ -52,7 +52,7 @@ value_parser_type!(parse_cipher_kind, CipherKind, "invalid cipher");
pub fn parse_server_url(v: &str) -> Result<ServerConfig, String> {
match ServerConfig::from_url(v) {
Ok(t) => Ok(t),
Err(..) => Err("should be SIP002 (https://shadowsocks.org/guide/sip002.html) format".to_owned()),
Err(..) => Err("should be SIP002 (https://shadowsocks.org/doc/sip002.html) format".to_owned()),
}
}
@@ -1111,7 +1111,7 @@ add_firewall_rule() {
$ip6t_m -I OUTPUT $(comment "mangle-OUTPUT-PSW") -o lo -j RETURN
insert_rule_before "$ip6t_m" "OUTPUT" "mwan3" "$(comment mangle-OUTPUT-PSW) -m mark --mark 1 -j RETURN"
[ $(config_t_get global dns_redirect) == "1" ] && {
[ $(config_t_get global dns_redirect "0") = "1" ] && {
$ipt_m -A PSW -p udp --dport 53 -j RETURN
$ip6t_m -A PSW -p udp --dport 53 -j RETURN
$ipt_n -I PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 53 -m comment --comment "PSW_DNS_Hijack" 2>/dev/null
@@ -1152,7 +1152,7 @@ add_firewall_rule() {
nft "add rule inet fw4 mangle_output oif lo counter return comment \"PSW_OUTPUT_MANGLE\""
nft "add rule inet fw4 mangle_output meta mark 1 counter return comment \"PSW_OUTPUT_MANGLE\""
[ $(config_t_get global dns_redirect) == "1" ] && {
[ $(config_t_get global dns_redirect "0") = "1" ] && {
nft "add rule inet fw4 PSW_MANGLE ip protocol udp udp dport 53 counter return"
nft "add rule inet fw4 PSW_MANGLE_V6 meta l4proto udp udp dport 53 counter return"
nft insert rule inet fw4 dstnat position 0 tcp dport 53 counter redirect to :53 comment \"PSW_DNS_Hijack\" 2>/dev/null
-4
View File
@@ -35,8 +35,6 @@ do
echo "appdmg V2rayU-64.dmg"
rm -f V2rayU-64.dmg
appdmg appdmg.json "V2rayU-64.dmg"
#rm -fr release/V2rayU.app
./sign_update "V2rayU-64.dmg"
break
;;
@@ -48,8 +46,6 @@ do
echo "appdmg V2rayU-arm64.dmg"
rm -f V2rayU-arm64.dmg
appdmg appdmg.json "V2rayU-arm64.dmg"
#rm -fr release/V2rayU.app
./sign_update "V2rayU-64.dmg"
break
;;
+10 -10
View File
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>compileBitcode</key>
<false/>
<key>method</key>
<string>development</string>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<dict/>
<key>development</key>
<true/>
</dict>
</plist>
-13
View File
@@ -1,13 +0,0 @@
#!/bin/bash
# publish.sh
# V2rayU
#
# Created by yanue on 2019/7/18.
# Copyright © 2019 yanue. All rights reserved.
read -p "请输入版本描述: " release_note
#pushRelease ${release_note}
generateAppcast ${release_note}
echo "Done"
+38 -7
View File
@@ -13,26 +13,60 @@ APP_TITLE="${APP_NAME} - V${APP_Version}"
AppCastDir=$HOME/swift/appcast
function updatePlistVersion() {
echo "Updating plist version..."
buildString=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "${BASE_DIR}/V2rayU/${INFOPLIST_FILE}")
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildString" "${BASE_DIR}/V2rayU/${INFOPLIST_FILE}"
if [ $? -eq 0 ]; then
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildString" "${BASE_DIR}/V2rayU/${INFOPLIST_FILE}"
if [ $? -ne 0 ]; then
echo "Error: Failed to set CFBundleVersion"
exit 1
fi
else
echo "Error: Failed to read CFBundleShortVersionString"
exit 1
fi
}
function build() {
echo "Building V2rayU."${APP_Version}
echo "Building V2rayU version ${APP_Version}"
echo "Cleaning up old archive & app..."
rm -rf ${V2rayU_ARCHIVE} ${V2rayU_RELEASE}
if [ $? -ne 0 ]; then
echo "Error: Failed to clean up old archive & app"
exit 1
fi
echo "Building archive... please wait a minute"
xcodebuild -workspace ${BASE_DIR}/V2rayU.xcworkspace -config Release -scheme V2rayU -archivePath ${V2rayU_ARCHIVE} archive
if [ $? -ne 0 ]; then
echo "Error: Failed to build archive"
exit 1
fi
echo "Exporting archive..."
xcodebuild -archivePath ${V2rayU_ARCHIVE} -exportArchive -exportPath ${V2rayU_RELEASE} -exportOptionsPlist ./build.plist
if [ $? -ne 0 ]; then
echo "Error: Failed to export archive"
exit 1
fi
echo "Cleaning up archive..."
rm -rf ${V2rayU_ARCHIVE}
if [ $? -ne 0 ]; then
echo "Error: Failed to clean up archive"
exit 1
fi
echo "Setting permissions for resources..."
chmod -R 755 "${V2rayU_RELEASE}/${APP_NAME}.app/Contents/Resources/v2ray-core"
chmod -R 755 "${V2rayU_RELEASE}/${APP_NAME}.app/Contents/Resources/unzip.sh"
if [ $? -ne 0 ]; then
echo "Error: Failed to set permissions for resources"
exit 1
fi
echo "Build and export completed successfully."
}
function createDmg() {
@@ -209,9 +243,6 @@ function createDmgByAppdmg() {
echo ${BUILD_DIR}/appdmg.json
appdmg appdmg.json ${DMG_FINAL}
# appcast sign update
${AppCastDir}/bin/sign_update ${DMG_FINAL}
# umount "/Volumes/${APP_NAME}"
}
@@ -256,7 +287,7 @@ function makeDmg() {
}
#makeDmg
createDmgByAppdmg
echo 'done'
-23
View File
@@ -1,23 +0,0 @@
#!/bin/sh
# sign.sh
# V2rayU
#
# Created by yanue on 2023/8/1.
# Copyright © 2023 yanue. All rights reserved.
set -ex
TOKEN=$1
release_id=$2
echo "token $TOKEN, release ${RELEASE_ID}"
curl -X "PATCH" "https://api.appcenter.ms/v0.1/apps/yanue/V2rayU/releases/${release_id}" \
-H "X-API-Token: $TOKEN" \
-H 'Content-Type: application/json; charset=utf-8' \
-d '{
"release_notes": "test",
"metadata": {
"ed_signature": "PW8pDnr5VZkmC93gZjUDlHI8gkJSspPoDU3DdhsMkps"
}
}'
Binary file not shown.
+3 -4
View File
@@ -10,8 +10,7 @@ target 'V2rayU' do
# Pods for V2rayU
pod 'FirebaseAnalytics', '~> 10.24.0'
pod 'FirebaseCrashlytics'
pod 'Alamofire'
pod 'SwiftyJSON'
pod 'SwiftyJSON'/
# master branch
pod 'Preferences', :git => 'https://github.com/sindresorhus/Preferences.git'
pod 'QRCoder'
@@ -21,11 +20,11 @@ target 'V2rayU' do
end
# fix libarclite_macosx.a need min deploy target 10.14
# fix libarclite_macosx.a need min deploy target 11.0
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['MACOSX_DEPLOYMENT_TARGET'] = '10.14'
config.build_settings['MACOSX_DEPLOYMENT_TARGET'] = '11.0'
end
end
end
+1 -9
View File
@@ -1,5 +1,4 @@
PODS:
- Alamofire (4.8.2)
- FirebaseAnalytics (10.24.0):
- FirebaseAnalytics/AdIdSupport (= 10.24.0)
- FirebaseCore (~> 10.0)
@@ -112,26 +111,22 @@ PODS:
- PromisesSwift (2.4.0):
- PromisesObjC (= 2.4.0)
- QRCoder (1.1.0)
- Sparkle (2.6.2)
- Swifter (1.4.7)
- SwiftyJSON (5.0.0)
- Yams (5.0.6)
DEPENDENCIES:
- Alamofire
- FirebaseAnalytics (~> 10.24.0)
- FirebaseCrashlytics
- MASShortcut
- Preferences (from `https://github.com/sindresorhus/Preferences.git`)
- QRCoder
- Sparkle (~> 2.0)
- Swifter
- SwiftyJSON
- Yams
SPEC REPOS:
https://github.com/CocoaPods/Specs.git:
- Alamofire
- FirebaseAnalytics
- FirebaseCore
- FirebaseCoreExtension
@@ -148,7 +143,6 @@ SPEC REPOS:
- PromisesObjC
- PromisesSwift
- QRCoder
- Sparkle
- Swifter
- SwiftyJSON
- Yams
@@ -163,7 +157,6 @@ CHECKOUT OPTIONS:
:git: https://github.com/sindresorhus/Preferences.git
SPEC CHECKSUMS:
Alamofire: ae5c501addb7afdbb13687d7f2f722c78734c2d3
FirebaseAnalytics: b5efc493eb0f40ec560b04a472e3e1a15d39ca13
FirebaseCore: 11dc8a16dfb7c5e3c3f45ba0e191a33ac4f50894
FirebaseCoreExtension: af5fd85e817ea9d19f9a2659a376cf9cf99f03c0
@@ -181,11 +174,10 @@ SPEC CHECKSUMS:
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851
QRCoder: cbd2bee531cc86d286df7942334cfed94c803ae4
Sparkle: a62c7dc4f410ced73beb2169cf1d3cc3f028a295
Swifter: 2327ef5d872c638aebab79646ce494af508b0c8f
SwiftyJSON: 36413e04c44ee145039d332b4f4e2d3e8d6c4db7
Yams: e10dae147f517ed57ecae37c5e8681bdf8fcab65
PODFILE CHECKSUM: 071aabe40b11d56fb50a9b83f6ed47079838bdee
PODFILE CHECKSUM: d61ad825ad5d61b2b98237544e21946c9babe3a0
COCOAPODS: 1.15.2
+22 -28
View File
@@ -25,7 +25,6 @@
664EB377216C9A5F00B6AE0D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 664EB376216C9A5F00B6AE0D /* Assets.xcassets */; };
664EB392216CA9E800B6AE0D /* ConfigWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664EB390216CA9E800B6AE0D /* ConfigWindow.swift */; };
6662C20B240EA782000AF6CD /* PreferenceDns.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6662C209240EA782000AF6CD /* PreferenceDns.xib */; };
667029CA21AF8E7A0079EF41 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 667029C921AF8E7A0079EF41 /* Sparkle.framework */; };
667029D321AFB86E0079EF41 /* QrcodeWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 667029D121AFB86E0079EF41 /* QrcodeWindow.xib */; };
66784AFC2170486D00AD307F /* Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66784AFB2170486D00AD307F /* Util.swift */; };
667ECE722A9A05EC009B00EC /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 667ECE712A9A05EC009B00EC /* main.swift */; };
@@ -110,15 +109,11 @@
6608D9DD2182C92D00A0E0DD /* v2rayOutbound.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = v2rayOutbound.swift; sourceTree = "<group>"; };
6608D9E12182C9C100A0E0DD /* v2rayStream.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = v2rayStream.swift; sourceTree = "<group>"; };
660D0E59216E0158000C2922 /* V2rayServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = V2rayServer.swift; sourceTree = "<group>"; usesTabs = 0; wrapsLines = 0; };
66107B8722DEDBE4002FFB60 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
66107B8922DEE445002FFB60 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
6618372823E9BF1A000F7410 /* ToastWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToastWindow.swift; sourceTree = "<group>"; };
6618372D23E9BF73000F7410 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/ToastWindow.xib; sourceTree = "<group>"; };
66193A8523EE45B200289B6A /* PreferenceRouting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferenceRouting.swift; sourceTree = "<group>"; };
66193A8823EE46BC00289B6A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = V2rayU/Base.lproj/PreferenceRouting.xib; sourceTree = SOURCE_ROOT; };
66193A9323EF1EF600289B6A /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "../zh-Hans.lproj/PreferenceRouting.strings"; sourceTree = "<group>"; };
66193A9523EF1EFA00289B6A /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-HK"; path = "../zh-HK.lproj/PreferenceRouting.strings"; sourceTree = "<group>"; };
6625848D2AB745E700DFDC1E /* sign.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = sign.sh; sourceTree = "<group>"; };
6625848E2AB746D500DFDC1E /* appdmg.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = appdmg.sh; sourceTree = "<group>"; };
662F0ACE2AB720C700884C17 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = ../en.lproj/PreferenceGeneral.strings; sourceTree = "<group>"; };
663F040525ED4B2C00687600 /* V2rayLaunch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = V2rayLaunch.swift; sourceTree = "<group>"; };
@@ -126,19 +121,17 @@
664B95DD217062A500DBC941 /* Alamofire */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Alamofire; path = Pods/Alamofire; sourceTree = "<group>"; };
664BAC462C2DB0E100654FB7 /* V2rayRouting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = V2rayRouting.swift; sourceTree = "<group>"; };
664BAC482C314CD600654FB7 /* AppVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppVersion.swift; sourceTree = "<group>"; };
664BAC4F2C394AA000654FB7 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "../zh-Hans.lproj/PreferenceRouting.strings"; sourceTree = "<group>"; };
664EB371216C9A5E00B6AE0D /* V2rayU.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = V2rayU.app; sourceTree = BUILT_PRODUCTS_DIR; };
664EB374216C9A5E00B6AE0D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
664EB376216C9A5F00B6AE0D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
664EB379216C9A5F00B6AE0D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
664EB37B216C9A5F00B6AE0D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
664EB390216CA9E800B6AE0D /* ConfigWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigWindow.swift; sourceTree = "<group>"; };
664FC05321A70C4300048FE3 /* build.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = build.plist; sourceTree = "<group>"; };
6653C20422E0A2B700754D66 /* publish.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = publish.sh; sourceTree = "<group>"; };
665DC9BA22A542F90062337B /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
6662C20A240EA782000AF6CD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = V2rayU/Base.lproj/PreferenceDns.xib; sourceTree = SOURCE_ROOT; };
6662C20C240EA794000AF6CD /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "../zh-Hans.lproj/PreferenceDns.strings"; sourceTree = "<group>"; };
6662C20D240EA797000AF6CD /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-HK"; path = "../zh-HK.lproj/PreferenceDns.strings"; sourceTree = "<group>"; };
667029C921AF8E7A0079EF41 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = "../../Downloads/Sparkle-1.21.0/Sparkle.framework"; sourceTree = "<group>"; };
667029D221AFB86E0079EF41 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/QrcodeWindow.xib; sourceTree = "<group>"; };
66784AFB2170486D00AD307F /* Util.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Util.swift; sourceTree = "<group>"; };
667ECE6F2A9A05EB009B00EC /* V2rayUTool */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = V2rayUTool; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -154,7 +147,9 @@
669A73AC233776B900807CF9 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-HK"; path = "../zh-HK.lproj/PreferenceGeneral.strings"; sourceTree = "<group>"; };
669A73AD233776B900807CF9 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-HK"; path = "../zh-HK.lproj/PreferencePac.strings"; sourceTree = "<group>"; };
669A73AE233776B900807CF9 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-HK"; path = "../zh-HK.lproj/PreferenceSubscription.strings"; sourceTree = "<group>"; };
669A73AF233776B900807CF9 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-HK"; path = "zh-HK.lproj/Localizable.strings"; sourceTree = "<group>"; };
66A358662C39517B00914A25 /* zh-HK */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-HK"; path = "zh-HK.lproj/Localizable.strings"; sourceTree = "<group>"; };
66A358672C39517F00914A25 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
66A358682C3959AE00914A25 /* build.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = build.plist; sourceTree = "<group>"; };
66A5CE4521706B5A009B08B2 /* Pods_V2rayU.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Pods_V2rayU.framework; sourceTree = BUILT_PRODUCTS_DIR; };
66A77BE2268225790097A126 /* v2ray-core */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "v2ray-core"; path = "Build/v2ray-core"; sourceTree = "<group>"; };
66ACB19F21757D5B005B5881 /* MainMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenu.swift; sourceTree = "<group>"; };
@@ -205,7 +200,6 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
667029CA21AF8E7A0079EF41 /* Sparkle.framework in Frameworks */,
66973EB721797719001FEA1E /* ServiceManagement.framework in Frameworks */,
F260898E336CFA5EAB07ADC9 /* Pods_V2rayU.framework in Frameworks */,
);
@@ -313,12 +307,10 @@
66FEAD44217D75D7009DECF9 /* Build */ = {
isa = PBXGroup;
children = (
66A358682C3959AE00914A25 /* build.plist */,
6625848E2AB746D500DFDC1E /* appdmg.sh */,
6625848D2AB745E700DFDC1E /* sign.sh */,
664FC05321A70C4300048FE3 /* build.plist */,
66FEAD45217D75FC009DECF9 /* release.sh */,
6D6DFD93CE866018306A090E /* pac */,
6653C20422E0A2B700754D66 /* publish.sh */,
6D6DF927AC79EEF7378C192A /* appdmg.json */,
);
path = Build;
@@ -360,7 +352,6 @@
children = (
665DC9BA22A542F90062337B /* AppKit.framework */,
664666A021CBD6C60094F0B7 /* libPods-V2rayUTool.a */,
667029C921AF8E7A0079EF41 /* Sparkle.framework */,
66973EB621797719001FEA1E /* ServiceManagement.framework */,
66A5CE4521706B5A009B08B2 /* Pods_V2rayU.framework */,
664B95DD217062A500DBC941 /* Alamofire */,
@@ -595,9 +586,9 @@
66107B8822DEDBE4002FFB60 /* Localizable.strings */ = {
isa = PBXVariantGroup;
children = (
66107B8722DEDBE4002FFB60 /* zh-Hans */,
66107B8922DEE445002FFB60 /* en */,
669A73AF233776B900807CF9 /* zh-HK */,
66A358662C39517B00914A25 /* zh-HK */,
66A358672C39517F00914A25 /* zh-Hans */,
);
name = Localizable.strings;
sourceTree = "<group>";
@@ -614,8 +605,7 @@
isa = PBXVariantGroup;
children = (
66193A8823EE46BC00289B6A /* Base */,
66193A9323EF1EF600289B6A /* zh-Hans */,
66193A9523EF1EFA00289B6A /* zh-HK */,
664BAC4F2C394AA000654FB7 /* zh-Hans */,
);
name = PreferenceRouting.xib;
sourceTree = "<group>";
@@ -844,14 +834,15 @@
buildSettings = {
ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 4.0.0;
CURRENT_PROJECT_VERSION = 4.2.0;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = RJYEH6TCJD;
ENABLE_ONLY_ACTIVE_RESOURCES = YES;
INFOPLIST_FILE = V2rayU/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = V2rayU;
@@ -860,7 +851,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 11.0;
MARKETING_VERSION = 4.0.0;
MARKETING_VERSION = 4.2.0;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = net.yanue.V2rayU;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -878,14 +869,15 @@
buildSettings = {
ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 4.0.0;
CURRENT_PROJECT_VERSION = 4.2.0;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = RJYEH6TCJD;
ENABLE_ONLY_ACTIVE_RESOURCES = YES;
INFOPLIST_FILE = V2rayU/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = V2rayU;
@@ -894,7 +886,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 11.0;
MARKETING_VERSION = 4.0.0;
MARKETING_VERSION = 4.2.0;
ONLY_ACTIVE_ARCH = NO;
PRODUCT_BUNDLE_IDENTIFIER = net.yanue.V2rayU;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -912,8 +904,9 @@
ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = RJYEH6TCJD;
ENABLE_HARDENED_RUNTIME = YES;
MACOSX_DEPLOYMENT_TARGET = 11.0;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -927,8 +920,9 @@
ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = RJYEH6TCJD;
ENABLE_HARDENED_RUNTIME = YES;
MACOSX_DEPLOYMENT_TARGET = 11.0;
PRODUCT_NAME = "$(TARGET_NAME)";
+105 -48
View File
@@ -45,6 +45,16 @@ struct GithubAsset: Codable {
}
}
struct GithubError: Codable {
let message: String
let documentationUrl: String
enum CodingKeys: String, CodingKey {
case message
case documentationUrl = "documentation_url"
}
}
let V2rayUpdater = AppCheckController()
// AppCheckController -
@@ -169,22 +179,46 @@ class AppCheckController: NSWindowController {
}
}
} catch {
print("Error decoding JSON: \(error)")
DispatchQueue.main.async {
// update progress text
self.bindData.progressText = "Check failed: \(error)"
var title = "Check failed!"
var toast = "\(error)"
if isMainland {
title = "检查失败"
toast = "\(error)";
//
do {
let decoder = JSONDecoder()
// try decode data
let data: GithubError = try decoder.decode(GithubError.self, from: data)
DispatchQueue.main.async {
// update progress text
self.bindData.progressText = "Check failed: \(error)"
var title = "Check failed!"
if isMainland {
title = "检查失败"
}
var toast = "\(data.message)\n\(data.documentationUrl)";
// open dialog
alertDialog(title: title, message: toast)
// sleep 2s
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
// close window
self.closeWindow()
}
}
// open dialog
alertDialog(title: title, message: toast)
// sleep 2s
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
// close window
self.closeWindow()
} catch {
print("Error decoding JSON: \(error)")
DispatchQueue.main.async {
// update progress text
self.bindData.progressText = "Check failed: \(error)"
var title = "Check failed!"
var toast = "\(error)"
if isMainland {
title = "检查失败"
toast = "\(error)";
}
// open dialog
alertDialog(title: title, message: toast)
// sleep 2s
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
// close window
self.closeWindow()
}
}
}
}
@@ -258,7 +292,7 @@ class AppVersionController: NSWindowController {
let window = NSWindow(contentRect: NSRect(x: 0, y: 0, width: 500, height: 300),
styleMask: [.titled, .closable, .resizable],
backing: .buffered, defer: false)
window.title = "Software Update"
window.title = "V2rayU Update"
window.contentView = contentView
super.init(window: window)
@@ -272,19 +306,37 @@ class AppVersionController: NSWindowController {
}
func show(release: GithubRelease) {
bindData.title = "A new version of V2rayU is available!"
bindData.description = "V2rayU \(appVersion) is now available—you have \(release.tagName). Would you like to download it now?"
bindData.releaseNotes = release.name + "\n" + release.body
self.release = release
print("bindData.releaseNotes", bindData.releaseNotes)
// bring window to front
window?.orderFrontRegardless()
// center position
window?.center()
// make window key
window?.makeKeyAndOrderFront(nil)
// activate app
NSApp.activate(ignoringOtherApps: true)
DispatchQueue.main.async {
self.release = release
if isMainland {
self.bindData.title = "A new version of V2rayU is available!"
if release.prerelease{
self.bindData.description = "V2rayU \(release.tagName) preview is now available, you have \(appVersion). Would you like to download it now?"
} else {
self.bindData.description = "V2rayU \(release.tagName) is now available, you have \(appVersion). Would you like to download it now?"
}
self.bindData.releaseNotes = release.name + "\n" + release.body
} else {
self.bindData.title = "V2rayU 有新版本上线了!"
if release.prerelease {
self.bindData.description = "V2rayU 已上线 \(release.tagName) 预览版,您有的版本 \(appVersion) —,需要立即下载吗?"
} else {
self.bindData.description = "V2rayU 已上线 \(release.tagName),您有的版本 \(appVersion) —,需要立即下载吗?"
}
self.bindData.releaseNotes = release.name + "\n" + release.body
self.bindData.releaseNodesTitle = "更新日志"
self.bindData.skipVersion = "跳过此版本"
self.bindData.installUpdate = "安装此版本"
}
// bring window to front
self.window?.orderFrontRegardless()
// center position
self.window?.center()
// make window key
self.window?.makeKeyAndOrderFront(nil)
// activate app
NSApp.activate(ignoringOtherApps: true)
}
}
required init?(coder: NSCoder) {
@@ -309,10 +361,10 @@ class AppVersionController: NSWindowController {
func skipAction() {
print("Skip action")
// UserDefaults
UserDefaults.standard.set(release.tagName, forKey: "skipAppVersion")
//
DispatchQueue.main.async {
// UserDefaults
UserDefaults.standard.set(self.release.tagName, forKey: "skipAppVersion")
//
self.window?.close()
}
}
@@ -320,8 +372,10 @@ class AppVersionController: NSWindowController {
class BindData: ObservableObject {
@Published var title = "A new version of V2rayU App is available!"
@Published var description = ""
@Published var releaseNotes = """
"""
@Published var releaseNotes = ""
@Published var releaseNodesTitle = "Release Notes:"
@Published var skipVersion = "Skip This Version!"
@Published var installUpdate = "Install Update!"
}
struct ContentView: View {
@@ -347,7 +401,7 @@ class AppVersionController: NSWindowController {
Text(bindData.description)
.padding(.trailing, 20)
Text("Release Notes:")
Text(bindData.releaseNodesTitle)
.font(.headline)
.bold()
.padding(.top, 20)
@@ -364,22 +418,25 @@ class AppVersionController: NSWindowController {
Spacer(minLength: 20) // margin 40
}
HStack {
Button(bindData.skipVersion) {
skipAction()
}
Spacer()
Button(bindData.installUpdate) {
installAction()
}
.padding(.trailing, 20)
.keyboardShortcut(.defaultAction)
}
.padding(.top,20)
.padding(.bottom,20)
}
}
HStack {
Button("Skip This Version") {
skipAction()
}
Spacer()
Button("Install Update") {
installAction()
}
.keyboardShortcut(.defaultAction)
}
.padding(20)
}
.frame(width: 500, height: 300)
}
+5 -5
View File
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21225" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21225"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22690"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@@ -51,7 +51,7 @@
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LeF-Sd-TXL">
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LeF-Sd-TXL">
<rect key="frame" x="18" y="319" width="295" height="21"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" title="V2ray Dns Json Text:" id="W0g-B7-neT">
@@ -82,7 +82,7 @@
<action selector="save:" target="-2" id="ec3-n0-TCm"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="f4X-IX-Vz3" userLabel="Set the rules line by line: domain or ip">
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="f4X-IX-Vz3" userLabel="Set the rules line by line: domain or ip">
<rect key="frame" x="18" y="22" width="251" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="* Please input v2ray DnsObject json text" id="yfy-JP-THq" userLabel="Set the rules line by line: domain or ip">
@@ -91,7 +91,7 @@
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="HDw-wR-poM">
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="HDw-wR-poM">
<rect key="frame" x="18" y="343" width="424" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" id="ztP-ok-6QX">
+6 -6
View File
@@ -4,14 +4,14 @@
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIconFile</key>
<string>AppIcon</string>
<key>CFBundleGetInfoString</key>
<string></string>
<key>CFBundleDisplayName</key>
<true/>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleGetInfoString</key>
<string></string>
<key>CFBundleIconFile</key>
<string>AppIcon</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
-2
View File
@@ -6,8 +6,6 @@
// Copyright © 2019 yanue. All rights reserved.
//
import SwiftyJSON
// ping and choose fastest v2ray
var inPing = false
var inPingCurrent = false
@@ -8,7 +8,6 @@
import Cocoa
import Preferences
import SwiftyJSON
final class PreferenceSubscribeViewController: NSViewController, PreferencePane, NSTabViewDelegate {
let preferencePaneIdentifier = PreferencePane.Identifier.subscribeTab
-1
View File
@@ -9,7 +9,6 @@
import Cocoa
import SystemConfiguration
import Swifter
import Alamofire
let LAUNCH_AGENT_NAME = "yanue.v2rayu.v2ray-core"
let AppResourcesPath = Bundle.main.bundlePath + "/Contents/Resources"
-1
View File
@@ -7,7 +7,6 @@
//
import Cocoa
import SwiftyJSON
// ----- v2ray server manager -----
class V2rayServer: NSObject {
-1
View File
@@ -7,7 +7,6 @@
//
import Cocoa
import SwiftyJSON
import Yams
// ----- v2ray subscribe manager -----
@@ -5,4 +5,3 @@
Created by yanue on 2019/7/17.
Copyright © 2019 yanue. All rights reserved.
*/
// Localizable App Name是App在英语环境环境下显示的名称
@@ -5,4 +5,3 @@
Created by yanue on 2019/7/17.
Copyright © 2019 yanue. All rights reserved.
*/
// Localizable App Name是App在英语环境环境下显示的名称
@@ -1,45 +1,45 @@
/* Class = "NSTextFieldCell"; title = "Domain Strategy:"; ObjectID = "22k-l7-G7a"; */
"22k-l7-G7a.title" = "域名策略:";
/* Class = "NSTextFieldCell"; title = "Block:"; ObjectID = "0Cd-bg-07K"; */
"0Cd-bg-07K.title" = "Block:";
/* Class = "NSMenuItem"; title = "Bypassing LAN and mainland address"; ObjectID = "3qq-dt-TS9"; */
"3qq-dt-TS9.title" = "绕过局域网和大陆地址";
/* Class = "NSMenuItem"; title = "IPOnDemand"; ObjectID = "2ST-s0-RrI"; */
"2ST-s0-RrI.title" = "IPOnDemand";
/* Class = "NSTextFieldCell"; title = "Routing Rule:"; ObjectID = "4rB-9k-291"; */
"4rB-9k-291.title" = "路由模式:";
/* Class = "NSTextFieldCell"; title = "Direct:"; ObjectID = "3hZ-qS-PUG"; */
"3hZ-qS-PUG.title" = "Direct:";
/* Class = "NSButtonCell"; title = "save"; ObjectID = "8g2-Nj-m07"; */
"8g2-Nj-m07.title" = "保存";
"8g2-Nj-m07.title" = "save";
/* Class = "NSMenuItem"; title = "IPIfNonMatch"; ObjectID = "92h-JU-wqn"; */
"92h-JU-wqn.title" = "IPIfNonMatch";
/* Class = "NSTextFieldCell"; title = "Rouing Rules"; ObjectID = "E4W-rN-0eq"; */
"E4W-rN-0eq.title" = "Rouing Rules";
/* Class = "NSMenuItem"; title = "Bypassing the LAN Address"; ObjectID = "CoE-cp-7fn"; */
"CoE-cp-7fn.title" = "绕过局域网地址";
/* Class = "NSTextFieldCell"; title = "Custom Rule Name"; ObjectID = "Ecy-wl-v5i"; */
"Ecy-wl-v5i.title" = "Custom Rule Name";
/* Class = "NSMenuItem"; title = "Bypassing mainland address"; ObjectID = "EaD-tR-ldB"; */
"EaD-tR-ldB.title" = "绕过大陆地址";
/* Class = "NSTextFieldCell"; title = "Routing Rule:"; ObjectID = "J2T-Pf-s5D"; */
"J2T-Pf-s5D.title" = "Routing Rule:";
/* Class = "NSMenuItem"; title = "IPOnDemand"; ObjectID = "Js7-cb-bWa"; */
"Js7-cb-bWa.title" = "IPOnDemand";
/* Class = "NSMenuItem"; title = "AsIs"; ObjectID = "Kl2-gr-bsh"; */
"Kl2-gr-bsh.title" = "AsIs";
/* Class = "NSMenuItem"; title = "AsIs"; ObjectID = "LX1-ye-51i"; */
"LX1-ye-51i.title" = "AsIs";
/* Class = "NSTextFieldCell"; title = "Custom Rule JSON Text"; ObjectID = "Q0v-Ic-TRt"; */
"Q0v-Ic-TRt.title" = "Custom Rule JSON Text";
/* Class = "NSButtonCell"; title = "load default"; ObjectID = "RSN-g5-vmP"; */
"RSN-g5-vmP.title" = "load default";
/* Class = "NSTextFieldCell"; title = "Domain Strategy:"; ObjectID = "Qp3-K0-xA9"; */
"Qp3-K0-xA9.title" = "Domain Strategy:";
/* Class = "NSTextFieldCell"; title = "* Set the rules line by line: domain or ip"; ObjectID = "ccq-gn-C6Z"; */
"ccq-gn-C6Z.title" = "*按行设置域名或ip";
/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "YQ1-7i-YOl"; */
"YQ1-7i-YOl.title" = "Text Cell";
/* Class = "NSTabViewItem"; label = "Block"; ObjectID = "nsT-BW-R2Z"; */
"nsT-BW-R2Z.label" = "阻止ip或域名";
/* Class = "NSTextFieldCell"; title = "Proxy:"; ObjectID = "cfS-8I-8Au"; */
"cfS-8I-8Au.title" = "Proxy:";
/* Class = "NSTabViewItem"; label = "Proxy"; ObjectID = "pnL-Z8-fIK"; */
"pnL-Z8-fIK.label" = "代理ip或域名";
/* Class = "NSMenuItem"; title = "IPIfNonMatch"; ObjectID = "dSb-GT-n2n"; */
"dSb-GT-n2n.title" = "IPIfNonMatch";
/* Class = "NSTabViewItem"; label = "Direct"; ObjectID = "yc0-Po-cG1"; */
"yc0-Po-cG1.label" = "直连ip或域名";
/* Class = "NSTextFieldCell"; title = "like: { \"domainStrategy\": \"IPOnDemand\", \"rules\": []}"; ObjectID = "ejH-X7-8Fb"; */
"ejH-X7-8Fb.title" = "like: { \"domainStrategy\": \"IPOnDemand\", \"rules\": []}";
/* Class = "NSMenuItem"; title = "Global"; ObjectID = "zLf-pT-xlm"; */
"zLf-pT-xlm.title" = "全局";
/* Class = "NSTextFieldCell"; title = "* Set the rules line by line: domain or ip"; ObjectID = "ymo-ds-No9"; */
"ymo-ds-No9.title" = "* Set the rules line by line: domain or ip";
@@ -1,45 +1,45 @@
/* Class = "NSTextFieldCell"; title = "Domain Strategy:"; ObjectID = "22k-l7-G7a"; */
"22k-l7-G7a.title" = "域名策略:";
/* Class = "NSTextFieldCell"; title = "Block:"; ObjectID = "0Cd-bg-07K"; */
"0Cd-bg-07K.title" = "阻止ip或域名:";
/* Class = "NSMenuItem"; title = "Bypassing LAN and mainland address"; ObjectID = "3qq-dt-TS9"; */
"3qq-dt-TS9.title" = "绕过局域网和大陆地址";
/* Class = "NSMenuItem"; title = "IPOnDemand"; ObjectID = "2ST-s0-RrI"; */
"2ST-s0-RrI.title" = "IPOnDemand";
/* Class = "NSTextFieldCell"; title = "Routing Rule:"; ObjectID = "4rB-9k-291"; */
"4rB-9k-291.title" = "路由模式:";
/* Class = "NSTextFieldCell"; title = "Direct:"; ObjectID = "3hZ-qS-PUG"; */
"3hZ-qS-PUG.title" = "直连ip或域名:";
/* Class = "NSButtonCell"; title = "save"; ObjectID = "8g2-Nj-m07"; */
"8g2-Nj-m07.title" = "保存";
/* Class = "NSMenuItem"; title = "IPIfNonMatch"; ObjectID = "92h-JU-wqn"; */
"92h-JU-wqn.title" = "IPIfNonMatch";
/* Class = "NSTextFieldCell"; title = "Rouing Rules"; ObjectID = "E4W-rN-0eq"; */
"E4W-rN-0eq.title" = "路由规则列表";
/* Class = "NSMenuItem"; title = "Bypassing the LAN Address"; ObjectID = "CoE-cp-7fn"; */
"CoE-cp-7fn.title" = "绕过局域网地址";
/* Class = "NSTextFieldCell"; title = "Custom Rule Name"; ObjectID = "Ecy-wl-v5i"; */
"Ecy-wl-v5i.title" = "自定义路由名称";
/* Class = "NSMenuItem"; title = "Bypassing mainland address"; ObjectID = "EaD-tR-ldB"; */
"EaD-tR-ldB.title" = "绕过大陆地址";
/* Class = "NSTextFieldCell"; title = "Routing Rule:"; ObjectID = "J2T-Pf-s5D"; */
"J2T-Pf-s5D.title" = "路由规则:";
/* Class = "NSMenuItem"; title = "IPOnDemand"; ObjectID = "Js7-cb-bWa"; */
"Js7-cb-bWa.title" = "IPOnDemand";
/* Class = "NSMenuItem"; title = "AsIs"; ObjectID = "Kl2-gr-bsh"; */
"Kl2-gr-bsh.title" = "AsIs";
/* Class = "NSMenuItem"; title = "AsIs"; ObjectID = "LX1-ye-51i"; */
"LX1-ye-51i.title" = "AsIs";
/* Class = "NSTextFieldCell"; title = "Custom Rule JSON Text"; ObjectID = "Q0v-Ic-TRt"; */
"Q0v-Ic-TRt.title" = "自定义路由 json 内容";
/* Class = "NSButtonCell"; title = "load default"; ObjectID = "RSN-g5-vmP"; */
"RSN-g5-vmP.title" = "load default";
/* Class = "NSTextFieldCell"; title = "Domain Strategy:"; ObjectID = "Qp3-K0-xA9"; */
"Qp3-K0-xA9.title" = "域名策略:";
/* Class = "NSTextFieldCell"; title = "* Set the rules line by line: domain or ip"; ObjectID = "ccq-gn-C6Z"; */
"ccq-gn-C6Z.title" = "*按行设置域名或ip";
/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "YQ1-7i-YOl"; */
"YQ1-7i-YOl.title" = "";
/* Class = "NSTabViewItem"; label = "Block"; ObjectID = "nsT-BW-R2Z"; */
"nsT-BW-R2Z.label" = "阻止ip或域名";
/* Class = "NSTextFieldCell"; title = "Proxy:"; ObjectID = "cfS-8I-8Au"; */
"cfS-8I-8Au.title" = "代理ip或域名:";
/* Class = "NSTabViewItem"; label = "Proxy"; ObjectID = "pnL-Z8-fIK"; */
"pnL-Z8-fIK.label" = "代理ip或域名";
/* Class = "NSMenuItem"; title = "IPIfNonMatch"; ObjectID = "dSb-GT-n2n"; */
"dSb-GT-n2n.title" = "IPIfNonMatch";
/* Class = "NSTabViewItem"; label = "Direct"; ObjectID = "yc0-Po-cG1"; */
"yc0-Po-cG1.label" = "直连ip或域名";
/* Class = "NSTextFieldCell"; title = "like: { \"domainStrategy\": \"IPOnDemand\", \"rules\": []}"; ObjectID = "ejH-X7-8Fb"; */
"ejH-X7-8Fb.title" = "例如: { \"domainStrategy\": \"IPOnDemand\", \"rules\": []}";
/* Class = "NSMenuItem"; title = "Global"; ObjectID = "zLf-pT-xlm"; */
"zLf-pT-xlm.title" = "全局";
/* Class = "NSTextFieldCell"; title = "* Set the rules line by line: domain or ip"; ObjectID = "ymo-ds-No9"; */
"ymo-ds-No9.title" = "* 按行设置域名或ip";
+1 -1
View File
@@ -25,7 +25,7 @@ require (
golang.org/x/crypto v0.24.0
golang.org/x/net v0.26.0
golang.org/x/sync v0.7.0
golang.org/x/sys v0.21.0
golang.org/x/sys v0.22.0
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
google.golang.org/grpc v1.65.0
google.golang.org/protobuf v1.34.2
+2 -2
View File
@@ -226,8 +226,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -120,6 +120,7 @@ class YandexMusicTrackIE(YandexMusicBaseIE):
download_data = self._download_json(
'https://music.yandex.ru/api/v2.1/handlers/track/%s:%s/web-album_track-track-track-main/download/m' % (track_id, album_id),
track_id, 'Downloading track location url JSON',
query={'hq': 1},
headers={'X-Retpath-Y': url})
fd_data = self._download_json(
+1
View File
@@ -2324,6 +2324,7 @@ from .vidio import (
)
from .vidlii import VidLiiIE
from .vidly import VidlyIE
from .vidyard import VidyardIE
from .viewlift import (
ViewLiftEmbedIE,
ViewLiftIE,
+4
View File
@@ -368,6 +368,7 @@ class AbemaTVIE(AbemaTVBaseIE):
info['episode_number'] = epis if epis < 2000 else None
is_live, m3u8_url = False, None
availability = 'public'
if video_type == 'now-on-air':
is_live = True
channel_url = 'https://api.abema.io/v1/channels'
@@ -389,6 +390,7 @@ class AbemaTVIE(AbemaTVBaseIE):
if 3 not in ondemand_types:
# cannot acquire decryption key for these streams
self.report_warning('This is a premium-only stream')
availability = 'premium_only'
info.update(traverse_obj(api_response, {
'series': ('series', 'title'),
'season': ('season', 'name'),
@@ -408,6 +410,7 @@ class AbemaTVIE(AbemaTVBaseIE):
headers=headers)
if not traverse_obj(api_response, ('slot', 'flags', 'timeshiftFree'), default=False):
self.report_warning('This is a premium-only stream')
availability = 'premium_only'
m3u8_url = f'https://vod-abematv.akamaized.net/slot/{video_id}/playlist.m3u8'
else:
@@ -425,6 +428,7 @@ class AbemaTVIE(AbemaTVBaseIE):
'description': description,
'formats': formats,
'is_live': is_live,
'availability': availability,
})
return info
+28 -41
View File
@@ -1,63 +1,50 @@
from .common import InfoExtractor
from ..utils import traverse_obj
from .vidyard import VidyardBaseIE, VidyardIE
from ..utils import ExtractorError, make_archive_id, url_basename
class CellebriteIE(InfoExtractor):
class CellebriteIE(VidyardBaseIE):
_VALID_URL = r'https?://cellebrite\.com/(?:\w+)?/(?P<id>[\w-]+)'
_TESTS = [{
'url': 'https://cellebrite.com/en/collect-data-from-android-devices-with-cellebrite-ufed/',
'info_dict': {
'id': '16025876',
'id': 'ZqmUss3dQfEMGpauambPuH',
'display_id': '16025876',
'ext': 'mp4',
'description': 'md5:174571cb97083fd1d457d75c684f4e2b',
'thumbnail': 'https://cellebrite.com/wp-content/uploads/2021/05/Chat-Capture-1024x559.png',
'title': 'Ask the Expert: Chat Capture - Collect Data from Android Devices in Cellebrite UFED',
'duration': 455,
'tags': [],
'description': 'md5:dee48fe12bbae5c01fe6a053f7676da4',
'thumbnail': 'https://cellebrite.com/wp-content/uploads/2021/05/Chat-Capture-1024x559.png',
'duration': 455.979,
'_old_archive_ids': ['cellebrite 16025876'],
},
}, {
'url': 'https://cellebrite.com/en/how-to-lawfully-collect-the-maximum-amount-of-data-from-android-devices/',
'info_dict': {
'id': '29018255',
'id': 'QV1U8a2yzcxigw7VFnqKyg',
'display_id': '29018255',
'ext': 'mp4',
'duration': 134,
'tags': [],
'description': 'md5:e9a3d124c7287b0b07bad2547061cacf',
'title': 'How to Lawfully Collect the Maximum Amount of Data From Android Devices',
'description': 'md5:0e943a9ac14c374d5d74faed634d773c',
'thumbnail': 'https://cellebrite.com/wp-content/uploads/2022/07/How-to-Lawfully-Collect-the-Maximum-Amount-of-Data-From-Android-Devices.png',
'title': 'Android Extractions Explained',
'duration': 134.315,
'_old_archive_ids': ['cellebrite 29018255'],
},
}]
def _get_formats_and_subtitles(self, json_data, display_id):
formats = [{'url': url} for url in traverse_obj(json_data, ('mp4', ..., 'url')) or []]
subtitles = {}
for url in traverse_obj(json_data, ('hls', ..., 'url')) or []:
fmt, sub = self._extract_m3u8_formats_and_subtitles(
url, display_id, ext='mp4', headers={'Referer': 'https://play.vidyard.com/'})
formats.extend(fmt)
self._merge_subtitles(sub, target=subtitles)
return formats, subtitles
def _real_extract(self, url):
display_id = self._match_id(url)
webpage = self._download_webpage(url, display_id)
slug = self._match_id(url)
webpage = self._download_webpage(url, slug)
vidyard_url = next(VidyardIE._extract_embed_urls(url, webpage), None)
if not vidyard_url:
raise ExtractorError('No Vidyard video embeds found on page')
player_uuid = self._search_regex(
r'<img\s[^>]*\bdata-uuid\s*=\s*"([^"\?]+)', webpage, 'player UUID')
json_data = self._download_json(
f'https://play.vidyard.com/player/{player_uuid}.json', display_id)['payload']['chapters'][0]
video_id = url_basename(vidyard_url)
info = self._process_video_json(self._fetch_video_json(video_id)['chapters'][0], video_id)
if info.get('display_id'):
info['_old_archive_ids'] = [make_archive_id(self, info['display_id'])]
if thumbnail := self._og_search_thumbnail(webpage, default=None):
info.setdefault('thumbnails', []).append({'url': thumbnail})
formats, subtitles = self._get_formats_and_subtitles(json_data['sources'], display_id)
return {
'id': str(json_data['videoId']),
'title': json_data.get('name') or self._og_search_title(webpage),
'formats': formats,
'subtitles': subtitles,
'description': json_data.get('description') or self._og_search_description(webpage),
'duration': json_data.get('seconds'),
'tags': json_data.get('tags'),
'thumbnail': self._og_search_thumbnail(webpage),
'http_headers': {'Referer': 'https://play.vidyard.com/'},
'description': self._og_search_description(webpage, default=None),
**info,
}
+35 -2
View File
@@ -36,7 +36,7 @@ class CHZZKLiveIE(InfoExtractor):
def _real_extract(self, url):
channel_id = self._match_id(url)
live_detail = self._download_json(
f'https://api.chzzk.naver.com/service/v2/channels/{channel_id}/live-detail', channel_id,
f'https://api.chzzk.naver.com/service/v3/channels/{channel_id}/live-detail', channel_id,
note='Downloading channel info', errnote='Unable to download channel info')['content']
if live_detail.get('status') == 'CLOSE':
@@ -106,12 +106,45 @@ class CHZZKVideoIE(InfoExtractor):
'upload_date': '20231219',
'view_count': int,
},
'skip': 'Replay video is expired',
}, {
# Manually uploaded video
'url': 'https://chzzk.naver.com/video/1980',
'info_dict': {
'id': '1980',
'ext': 'mp4',
'title': '※시청주의※한번보면 잊기 힘든 영상',
'channel': '라디유radiyu',
'channel_id': '68f895c59a1043bc5019b5e08c83a5c5',
'channel_is_verified': False,
'thumbnail': r're:^https?://.*\.jpg$',
'duration': 95,
'timestamp': 1703102631.722,
'upload_date': '20231220',
'view_count': int,
},
}, {
# Partner channel replay video
'url': 'https://chzzk.naver.com/video/2458',
'info_dict': {
'id': '2458',
'ext': 'mp4',
'title': '첫 방송',
'channel': '강지',
'channel_id': 'b5ed5db484d04faf4d150aedd362f34b',
'channel_is_verified': True,
'thumbnail': r're:^https?://.*\.jpg$',
'duration': 4433,
'timestamp': 1703307460.214,
'upload_date': '20231223',
'view_count': int,
},
}]
def _real_extract(self, url):
video_id = self._match_id(url)
video_meta = self._download_json(
f'https://api.chzzk.naver.com/service/v2/videos/{video_id}', video_id,
f'https://api.chzzk.naver.com/service/v3/videos/{video_id}', video_id,
note='Downloading video info', errnote='Unable to download video info')['content']
formats, subtitles = self._extract_mpd_formats_and_subtitles(
f'https://apis.naver.com/neonplayer/vodplay/v1/playback/{video_meta["videoId"]}', video_id,
+15 -49
View File
@@ -1,55 +1,31 @@
from .common import InfoExtractor
from ..utils import ExtractorError, int_or_none, traverse_obj
from .vidyard import VidyardBaseIE
from ..utils import ExtractorError, int_or_none, make_archive_id
class SwearnetEpisodeIE(InfoExtractor):
class SwearnetEpisodeIE(VidyardBaseIE):
_VALID_URL = r'https?://www\.swearnet\.com/shows/(?P<id>[\w-]+)/seasons/(?P<season_num>\d+)/episodes/(?P<episode_num>\d+)'
_TESTS = [{
'url': 'https://www.swearnet.com/shows/gettin-learnt-with-ricky/seasons/1/episodes/1',
'info_dict': {
'id': '232819',
'id': 'wicK2EOzjOdxkUXGDIgcPw',
'display_id': '232819',
'ext': 'mp4',
'episode_number': 1,
'episode': 'Episode 1',
'duration': 719,
'description': 'md5:c48ef71440ce466284c07085cd7bd761',
'description': r're:Are you drunk and high and craving a grilled cheese sandwich.+',
'season': 'Season 1',
'title': 'Episode 1 - Grilled Cheese Sammich',
'season_number': 1,
'thumbnail': 'https://cdn.vidyard.com/thumbnails/232819/_RX04IKIq60a2V6rIRqq_Q_small.jpg',
'thumbnail': 'https://cdn.vidyard.com/thumbnails/custom/0dd74f9b-388a-452e-b570-b407fb64435b_small.jpg',
'tags': ['Getting Learnt with Ricky', 'drunk', 'grilled cheese', 'high'],
'_old_archive_ids': ['swearnetepisode 232819'],
},
}]
def _get_formats_and_subtitle(self, video_source, video_id):
video_source = video_source or {}
formats, subtitles = [], {}
for key, value in video_source.items():
if key == 'hls':
for video_hls in value:
fmts, subs = self._extract_m3u8_formats_and_subtitles(video_hls.get('url'), video_id)
formats.extend(fmts)
self._merge_subtitles(subs, target=subtitles)
else:
formats.extend({
'url': video_mp4.get('url'),
'ext': 'mp4',
} for video_mp4 in value)
return formats, subtitles
def _get_direct_subtitle(self, caption_json):
subs = {}
for caption in caption_json:
subs.setdefault(caption.get('language') or 'und', []).append({
'url': caption.get('vttUrl'),
'name': caption.get('name'),
})
return subs
def _real_extract(self, url):
display_id, season_number, episode_number = self._match_valid_url(url).group('id', 'season_num', 'episode_num')
webpage = self._download_webpage(url, display_id)
slug, season_number, episode_number = self._match_valid_url(url).group('id', 'season_num', 'episode_num')
webpage = self._download_webpage(url, slug)
try:
external_id = self._search_regex(r'externalid\s*=\s*"([^"]+)', webpage, 'externalid')
@@ -58,22 +34,12 @@ class SwearnetEpisodeIE(InfoExtractor):
self.raise_login_required()
raise
json_data = self._download_json(
f'https://play.vidyard.com/player/{external_id}.json', display_id)['payload']['chapters'][0]
formats, subtitles = self._get_formats_and_subtitle(json_data['sources'], display_id)
self._merge_subtitles(self._get_direct_subtitle(json_data.get('captions')), target=subtitles)
info = self._process_video_json(self._fetch_video_json(external_id)['chapters'][0], external_id)
if info.get('display_id'):
info['_old_archive_ids'] = [make_archive_id(self, info['display_id'])]
return {
'id': str(json_data['videoId']),
'title': json_data.get('name') or self._html_search_meta(['og:title', 'twitter:title'], webpage),
'description': (json_data.get('description')
or self._html_search_meta(['og:description', 'twitter:description'], webpage)),
'duration': int_or_none(json_data.get('seconds')),
'formats': formats,
'subtitles': subtitles,
**info,
'season_number': int_or_none(season_number),
'episode_number': int_or_none(episode_number),
'thumbnails': [{'url': thumbnail_url}
for thumbnail_url in traverse_obj(json_data, ('thumbnailUrls', ...))],
}
+426
View File
@@ -0,0 +1,426 @@
import functools
import re
from .common import InfoExtractor
from ..utils import (
extract_attributes,
float_or_none,
int_or_none,
join_nonempty,
mimetype2ext,
parse_resolution,
str_or_none,
unescapeHTML,
url_or_none,
)
from ..utils.traversal import traverse_obj
class VidyardBaseIE(InfoExtractor):
_HEADERS = {'Referer': 'https://play.vidyard.com/'}
def _get_formats_and_subtitles(self, sources, video_id):
formats, subtitles = [], {}
def add_hls_fmts_and_subs(m3u8_url):
fmts, subs = self._extract_m3u8_formats_and_subtitles(
m3u8_url, video_id, 'mp4', m3u8_id='hls', headers=self._HEADERS, fatal=False)
formats.extend(fmts)
self._merge_subtitles(subs, target=subtitles)
hls_list = isinstance(sources, dict) and sources.pop('hls', None)
if master_m3u8_url := traverse_obj(
hls_list, (lambda _, v: v['profile'] == 'auto', 'url', {url_or_none}, any)):
add_hls_fmts_and_subs(master_m3u8_url)
if not formats: # These are duplicate and unnecesary requests if we got 'auto' hls fmts
for variant_m3u8_url in traverse_obj(hls_list, (..., 'url', {url_or_none})):
add_hls_fmts_and_subs(variant_m3u8_url)
for source_type, source_list in traverse_obj(sources, ({dict.items}, ...)):
for source in traverse_obj(source_list, lambda _, v: url_or_none(v['url'])):
profile = source.get('profile')
formats.append({
'url': source['url'],
'ext': mimetype2ext(source.get('mimeType'), default=None),
'format_id': join_nonempty('http', source_type, profile),
**parse_resolution(profile),
})
self._remove_duplicate_formats(formats)
return formats, subtitles
def _get_direct_subtitles(self, caption_json):
subs = {}
for caption in traverse_obj(caption_json, lambda _, v: url_or_none(v['vttUrl'])):
subs.setdefault(caption.get('language') or 'und', []).append({
'url': caption['vttUrl'],
'name': caption.get('name'),
})
return subs
def _fetch_video_json(self, video_id):
return self._download_json(
f'https://play.vidyard.com/player/{video_id}.json', video_id)['payload']
def _process_video_json(self, json_data, video_id):
formats, subtitles = self._get_formats_and_subtitles(json_data['sources'], video_id)
self._merge_subtitles(self._get_direct_subtitles(json_data.get('captions')), target=subtitles)
return {
**traverse_obj(json_data, {
'id': ('facadeUuid', {str}),
'display_id': ('videoId', {int}, {str_or_none}),
'title': ('name', {str}),
'description': ('description', {str}, {unescapeHTML}, {lambda x: x or None}),
'duration': ((
('milliseconds', {functools.partial(float_or_none, scale=1000)}),
('seconds', {int_or_none})), any),
'thumbnails': ('thumbnailUrls', ('small', 'normal'), {'url': {url_or_none}}),
'tags': ('tags', ..., 'name', {str}),
}),
'formats': formats,
'subtitles': subtitles,
'http_headers': self._HEADERS,
}
class VidyardIE(VidyardBaseIE):
_VALID_URL = [
r'https?://[\w-]+(?:\.hubs)?\.vidyard\.com/watch/(?P<id>[\w-]+)',
r'https?://(?:embed|share)\.vidyard\.com/share/(?P<id>[\w-]+)',
r'https?://play\.vidyard\.com/(?:player/)?(?P<id>[\w-]+)',
]
_EMBED_REGEX = [r'<iframe[^>]* src=["\'](?P<url>(?:https?:)?//play\.vidyard\.com/[\w-]+)']
_TESTS = [{
'url': 'https://vyexample03.hubs.vidyard.com/watch/oTDMPlUv--51Th455G5u7Q',
'info_dict': {
'id': 'oTDMPlUv--51Th455G5u7Q',
'display_id': '50347',
'ext': 'mp4',
'title': 'Homepage Video',
'description': 'Look I changed the description.',
'thumbnail': 'https://cdn.vidyard.com/thumbnails/50347/OUPa5LTKV46849sLYngMqQ_small.jpg',
'duration': 99,
'tags': ['these', 'are', 'all', 'tags'],
},
}, {
'url': 'https://share.vidyard.com/watch/PaQzDAT1h8JqB8ivEu2j6Y?',
'info_dict': {
'id': 'PaQzDAT1h8JqB8ivEu2j6Y',
'display_id': '9281024',
'ext': 'mp4',
'title': 'Inline Embed',
'thumbnail': 'https://cdn.vidyard.com/thumbnails/spacer.gif',
'duration': 41.186,
},
}, {
'url': 'https://embed.vidyard.com/share/oTDMPlUv--51Th455G5u7Q',
'info_dict': {
'id': 'oTDMPlUv--51Th455G5u7Q',
'display_id': '50347',
'ext': 'mp4',
'title': 'Homepage Video',
'description': 'Look I changed the description.',
'thumbnail': 'https://cdn.vidyard.com/thumbnails/50347/OUPa5LTKV46849sLYngMqQ_small.jpg',
'duration': 99,
'tags': ['these', 'are', 'all', 'tags'],
},
}, {
# First video from playlist below
'url': 'https://embed.vidyard.com/share/SyStyHtYujcBHe5PkZc5DL',
'info_dict': {
'id': 'SyStyHtYujcBHe5PkZc5DL',
'display_id': '41974005',
'ext': 'mp4',
'title': 'Prepare the Frame and Track for Palm Beach Polysatin Shutters With BiFold Track',
'description': r're:In this video, you will learn how to prepare the frame.+',
'thumbnail': 'https://cdn.vidyard.com/thumbnails/41974005/IJw7oCaJcF1h7WWu3OVZ8A_small.png',
'duration': 258.666,
},
}, {
# Playlist
'url': 'https://thelink.hubs.vidyard.com/watch/pwu7pCYWSwAnPxs8nDoFrE',
'info_dict': {
'id': 'pwu7pCYWSwAnPxs8nDoFrE',
'title': 'PLAYLIST - Palm Beach Shutters- Bi-Fold Track System Installation',
'entries': [{
'id': 'SyStyHtYujcBHe5PkZc5DL',
'display_id': '41974005',
'ext': 'mp4',
'title': 'Prepare the Frame and Track for Palm Beach Polysatin Shutters With BiFold Track',
'thumbnail': 'https://cdn.vidyard.com/thumbnails/41974005/IJw7oCaJcF1h7WWu3OVZ8A_small.png',
'duration': 258.666,
}, {
'id': '1Fw4B84jZTXLXWqkE71RiM',
'display_id': '5861113',
'ext': 'mp4',
'title': 'Palm Beach - Bi-Fold Track System "Frame Installation"',
'thumbnail': 'https://cdn.vidyard.com/thumbnails/5861113/29CJ54s5g1_aP38zkKLHew_small.jpg',
'duration': 167.858,
}, {
'id': 'DqP3wBvLXSpxrcqpT5kEeo',
'display_id': '41976334',
'ext': 'mp4',
'title': 'Install the Track for Palm Beach Polysatin Shutters With BiFold Track',
'thumbnail': 'https://cdn.vidyard.com/thumbnails/5861090/RwG2VaTylUa6KhSTED1r1Q_small.png',
'duration': 94.229,
}, {
'id': 'opfybfxpzQArxqtQYB6oBU',
'display_id': '41976364',
'ext': 'mp4',
'title': 'Install the Panel for Palm Beach Polysatin Shutters With BiFold Track',
'thumbnail': 'https://cdn.vidyard.com/thumbnails/5860926/JIOaJR08dM4QgXi_iQ2zGA_small.png',
'duration': 191.467,
}, {
'id': 'rWrXvkbTNNaNqD6189HJya',
'display_id': '41976382',
'ext': 'mp4',
'title': 'Adjust the Panels for Palm Beach Polysatin Shutters With BiFold Track',
'thumbnail': 'https://cdn.vidyard.com/thumbnails/5860687/CwHxBv4UudAhOh43FVB4tw_small.png',
'duration': 138.155,
}, {
'id': 'eYPTB521MZ9TPEArSethQ5',
'display_id': '41976409',
'ext': 'mp4',
'title': 'Assemble and Install the Valance for Palm Beach Polysatin Shutters With BiFold Track',
'thumbnail': 'https://cdn.vidyard.com/thumbnails/5861425/0y68qlMU4O5VKU7bJ8i_AA_small.png',
'duration': 148.224,
}],
},
'playlist_count': 6,
}, {
# Non hubs.vidyard.com playlist
'url': 'https://salesforce.vidyard.com/watch/d4vqPjs7Q5EzVEis5QT3jd',
'info_dict': {
'id': 'd4vqPjs7Q5EzVEis5QT3jd',
'title': 'How To: Service Cloud: Import External Content in Lightning Knowledge',
'entries': [{
'id': 'mcjDpSZir2iSttbvFkx6Rv',
'display_id': '29479036',
'ext': 'mp4',
'title': 'Welcome to this Expert Coaching Series',
'thumbnail': 'https://cdn.vidyard.com/thumbnails/ouyQi9WuwyiOupChUWNmjQ/7170d3485ba602e012df05_small.jpg',
'duration': 38.205,
}, {
'id': '84bPYwpg243G6xYEfJdYw9',
'display_id': '21820704',
'ext': 'mp4',
'title': 'Chapter 1 - Title + Agenda',
'thumbnail': 'https://cdn.vidyard.com/thumbnails/HFPN0ZgQq4Ow8BghGcQSow/bfaa30123c8f6601e7d7f2_small.jpg',
'duration': 98.016,
}, {
'id': 'nP17fMuvA66buVHUrzqjTi',
'display_id': '21820707',
'ext': 'mp4',
'title': 'Chapter 2 - Import Options',
'thumbnail': 'https://cdn.vidyard.com/thumbnails/rGRIF5nFjPI9OOA2qJ_Dbg/86a8d02bfec9a566845dd4_small.jpg',
'duration': 199.136,
}, {
'id': 'm54EcwXdpA5gDBH5rgCYoV',
'display_id': '21820710',
'ext': 'mp4',
'title': 'Chapter 3 - Importing Article Translations',
'thumbnail': 'https://cdn.vidyard.com/thumbnails/IVX4XR8zpSsiNIHx45kz-A/1ccbf8a29a33856d06b3ed_small.jpg',
'duration': 184.352,
}, {
'id': 'j4nzS42oq4hE9oRV73w3eQ',
'display_id': '21820716',
'ext': 'mp4',
'title': 'Chapter 4 - Best Practices',
'thumbnail': 'https://cdn.vidyard.com/thumbnails/BtrRrQpRDLbA4AT95YQyog/1f1e6b8e7fdc3fa95ec8d3_small.jpg',
'duration': 296.960,
}, {
'id': 'y28PYfW5pftvers9PXzisC',
'display_id': '21820727',
'ext': 'mp4',
'title': 'Chapter 5 - Migration Steps',
'thumbnail': 'https://cdn.vidyard.com/thumbnails/K2CdQOXDfLcrVTF60r0bdw/a09239ada28b6ffce12b1f_small.jpg',
'duration': 620.640,
}, {
'id': 'YWU1eQxYvhj29SjYoPw5jH',
'display_id': '21820733',
'ext': 'mp4',
'title': 'Chapter 6 - Demo',
'thumbnail': 'https://cdn.vidyard.com/thumbnails/rsmhP-cO8dAa8ilvFGCX0g/7911ef415167cd14032068_small.jpg',
'duration': 631.456,
}, {
'id': 'nmEvVqpwdJUgb74zKsLGxn',
'display_id': '29479037',
'ext': 'mp4',
'title': 'Schedule Your Follow-Up',
'thumbnail': 'https://cdn.vidyard.com/thumbnails/Rtwc7X4PEkF4Ae5kHi-Jvw/174ebed3f34227b1ffa1d0_small.jpg',
'duration': 33.608,
}],
},
'playlist_count': 8,
}, {
# URL of iframe embed src
'url': 'https://play.vidyard.com/iDqTwWGrd36vaLuaCY3nTs.html',
'info_dict': {
'id': 'iDqTwWGrd36vaLuaCY3nTs',
'display_id': '9281009',
'ext': 'mp4',
'title': 'Lightbox Embed',
'thumbnail': 'https://cdn.vidyard.com/thumbnails/spacer.gif',
'duration': 39.035,
},
}, {
# Player JSON URL
'url': 'https://play.vidyard.com/player/7GAApnNNbcZZ46k6JqJQSh.json?disable_analytics=0',
'info_dict': {
'id': '7GAApnNNbcZZ46k6JqJQSh',
'display_id': '820026',
'ext': 'mp4',
'title': 'The Art of Storytelling: How to Deliver Your Brand Story with Content & Social',
'thumbnail': 'https://cdn.vidyard.com/thumbnails/MhbE-5sEFQu4x3fI6FkNlA/41eb5717c557cd19456910_small.jpg',
'duration': 2153.013,
'tags': ['Summit2017'],
},
}, {
'url': 'http://share.vidyard.com/share/diYeo6YR2yiGgL8odvS8Ri',
'only_matching': True,
}, {
'url': 'https://play.vidyard.com/FFlz3ZpxhIfKQ1fd9DAryA',
'only_matching': True,
}, {
'url': 'https://play.vidyard.com/qhMAu5A76GZVrFzOPgSf9A/type/standalone',
'only_matching': True,
}]
_WEBPAGE_TESTS = [{
# URL containing inline/lightbox embedded video
'url': 'https://resources.altium.com/p/2-the-extreme-importance-of-pc-board-stack-up',
'info_dict': {
'id': 'GDx1oXrFWj4XHbipfoXaMn',
'display_id': '3225198',
'ext': 'mp4',
'title': 'The Extreme Importance of PC Board Stack Up',
'thumbnail': 'https://cdn.vidyard.com/thumbnails/73_Q3_hBexWX7Og1sae6cg/9998fa4faec921439e2c04_small.jpg',
'duration': 3422.742,
},
}, {
# <script ... id="vidyard_embed_code_DXx2sW4WaLA6hTdGFz7ja8" src="//play.vidyard.com/DXx2sW4WaLA6hTdGFz7ja8.js?
'url': 'http://videos.vivint.com/watch/DXx2sW4WaLA6hTdGFz7ja8',
'info_dict': {
'id': 'DXx2sW4WaLA6hTdGFz7ja8',
'display_id': '2746529',
'ext': 'mp4',
'title': 'How To Powercycle the Smart Hub Panel',
'duration': 30.613,
'thumbnail': 'https://cdn.vidyard.com/thumbnails/_-6cw8xQUJ3qiCs_JENc_A/b21d7a5e47967f49399d30_small.jpg',
},
}, {
# <script id="vidyard_embed_code_MIBHhiLVTxga7wqLsuoDjQ" src="//embed.vidyard.com/embed/MIBHhiLVTxga7wqLsuoDjQ/inline?v=2.1">
'url': 'https://www.babypips.com/learn/forex/introduction-to-metatrader4',
'info_dict': {
'id': 'MIBHhiLVTxga7wqLsuoDjQ',
'display_id': '20291',
'ext': 'mp4',
'title': 'Lesson 1 - Opening an MT4 Account',
'description': 'Never heard of MetaTrader4? Here\'s the 411 on the popular trading platform!',
'duration': 168,
'thumbnail': 'https://cdn.vidyard.com/thumbnails/20291/IM-G2WXQR9VBLl2Cmzvftg_small.jpg',
},
}, {
# <iframe ... src="//play.vidyard.com/d61w8EQoZv1LDuPxDkQP2Q/type/background?preview=1"
'url': 'https://www.avaya.com/en/',
'info_dict': {
# These values come from the generic extractor and don't matter
'id': str,
'title': str,
'age_limit': 0,
'upload_date': str,
'description': str,
'thumbnail': str,
'timestamp': float,
},
'playlist': [{
'info_dict': {
'id': 'd61w8EQoZv1LDuPxDkQP2Q',
'display_id': '42456529',
'ext': 'mp4',
'title': 'GettyImages-1027',
'duration': 6.0,
'thumbnail': 'https://cdn.vidyard.com/thumbnails/42061563/p6bY08d2N4e4IDz-7J4_wkgsPq3-qgcx_small.jpg',
},
}, {
'info_dict': {
'id': 'VAsYDi7eiqZRbHodUA2meC',
'display_id': '42456569',
'ext': 'mp4',
'title': 'GettyImages-1325598833',
'duration': 6.083,
'thumbnail': 'https://cdn.vidyard.com/thumbnails/42052358/y3qrbDpn_2quWr_5XBi7yzS3UvEI__ZM_small.jpg',
},
}],
'playlist_count': 2,
}, {
# <div class="vidyard-player-embed" data-uuid="vpCWTVHw3qrciLtVY94YkS"
'url': 'https://www.gogoair.com/',
'info_dict': {
# These values come from the generic extractor and don't matter
'id': str,
'title': str,
'description': str,
'age_limit': 0,
},
'playlist': [{
'info_dict': {
'id': 'vpCWTVHw3qrciLtVY94YkS',
'display_id': '40780699',
'ext': 'mp4',
'title': 'Upgrade to AVANCE 100% worth it - Jason Talley, Owner and Pilot, Testimonial',
'description': 'md5:f609824839439a51990cef55ffc472aa',
'duration': 70.737,
'thumbnail': 'https://cdn.vidyard.com/thumbnails/40780699/KzjfYZz5MZl2gHF_e-4i2c6ib1cLDweQ_small.jpg',
},
}, {
'info_dict': {
'id': 'xAmV9AsLbnitCw35paLBD8',
'display_id': '31130867',
'ext': 'mp4',
'title': 'Brad Keselowski goes faster with Gogo AVANCE inflight Wi-Fi',
'duration': 132.565,
'thumbnail': 'https://cdn.vidyard.com/thumbnails/31130867/HknyDtLdm2Eih9JZ4A5XLjhfBX_6HRw5_small.jpg',
},
}, {
'info_dict': {
'id': 'RkkrFRNxfP79nwCQavecpF',
'display_id': '39009815',
'ext': 'mp4',
'title': 'Live Demo of Gogo Galileo',
'description': 'md5:e2df497236f4e12c3fef8b392b5f23e0',
'duration': 112.128,
'thumbnail': 'https://cdn.vidyard.com/thumbnails/38144873/CWLlxfUbJ4Gh0ThbUum89IsEM4yupzMb_small.jpg',
},
}],
'playlist_count': 3,
}]
@classmethod
def _extract_embed_urls(cls, url, webpage):
# Handle protocol-less embed URLs
for embed_url in super()._extract_embed_urls(url, webpage):
if embed_url.startswith('//'):
embed_url = f'https:{embed_url}'
yield embed_url
# Extract inline/lightbox embeds
for embed_element in re.findall(
r'(<(?:img|div)[^>]* class=(["\'])(?:[^>"\']* )?vidyard-player-embed(?: [^>"\']*)?\2[^>]+>)', webpage):
if video_id := extract_attributes(embed_element[0]).get('data-uuid'):
yield f'https://play.vidyard.com/{video_id}'
for embed_id in re.findall(r'<script[^>]* id=["\']vidyard_embed_code_([\w-]+)["\']', webpage):
yield f'https://play.vidyard.com/{embed_id}'
def _real_extract(self, url):
video_id = self._match_id(url)
video_json = self._fetch_video_json(video_id)
if len(video_json['chapters']) == 1:
return self._process_video_json(video_json['chapters'][0], video_id)
return self.playlist_result(
[self._process_video_json(chapter, video_id) for chapter in video_json['chapters']],
str(video_json['playerUuid']), video_json.get('name'))
+44 -43
View File
@@ -1,6 +1,7 @@
import base64
import functools
import itertools
import json
import re
import urllib.parse
@@ -14,6 +15,7 @@ from ..utils import (
determine_ext,
get_element_by_class,
int_or_none,
join_nonempty,
js_to_json,
merge_dicts,
parse_filesize,
@@ -84,29 +86,23 @@ class VimeoBaseInfoExtractor(InfoExtractor):
expected=True)
return password
def _verify_video_password(self, url, video_id, password, token, vuid):
if url.startswith('http://'):
# vimeo only supports https now, but the user can give an http url
url = url.replace('http://', 'https://')
self._set_vimeo_cookie('vuid', vuid)
return self._download_webpage(
url + '/password', video_id, 'Verifying the password',
'Wrong password', data=urlencode_postdata({
'password': password,
'token': token,
}), headers={
'Content-Type': 'application/x-www-form-urlencoded',
'Referer': url,
})
def _extract_xsrft_and_vuid(self, webpage):
xsrft = self._search_regex(
r'(?:(?P<q1>["\'])xsrft(?P=q1)\s*:|xsrft\s*[=:])\s*(?P<q>["\'])(?P<xsrft>.+?)(?P=q)',
webpage, 'login token', group='xsrft')
vuid = self._search_regex(
r'["\']vuid["\']\s*:\s*(["\'])(?P<vuid>.+?)\1',
webpage, 'vuid', group='vuid')
return xsrft, vuid
def _verify_video_password(self, video_id, password, token):
url = f'https://vimeo.com/{video_id}'
try:
return self._download_webpage(
f'{url}/password', video_id,
'Submitting video password', data=json.dumps({
'password': password,
'token': token,
}, separators=(',', ':')).encode(), headers={
'Accept': '*/*',
'Content-Type': 'application/json',
'Referer': url,
}, impersonate=True)
except ExtractorError as error:
if isinstance(error.cause, HTTPError) and error.cause.status == 418:
raise ExtractorError('Wrong password', expected=True)
raise
def _extract_vimeo_config(self, webpage, video_id, *args, **kwargs):
vimeo_config = self._search_regex(
@@ -745,21 +741,34 @@ class VimeoIE(VimeoBaseInfoExtractor):
raise ExtractorError('Wrong video password', expected=True)
return checked
def _extract_from_api(self, video_id, unlisted_hash=None):
token = self._download_json(
'https://vimeo.com/_rv/jwt', video_id, headers={
'X-Requested-With': 'XMLHttpRequest',
})['token']
api_url = 'https://api.vimeo.com/videos/' + video_id
if unlisted_hash:
api_url += ':' + unlisted_hash
video = self._download_json(
api_url, video_id, headers={
'Authorization': 'jwt ' + token,
def _call_videos_api(self, video_id, jwt_token, unlisted_hash=None):
return self._download_json(
join_nonempty(f'https://api.vimeo.com/videos/{video_id}', unlisted_hash, delim=':'),
video_id, 'Downloading API JSON', headers={
'Authorization': f'jwt {jwt_token}',
'Accept': 'application/json',
}, query={
'fields': 'config_url,created_time,description,license,metadata.connections.comments.total,metadata.connections.likes.total,release_time,stats.plays',
})
def _extract_from_api(self, video_id, unlisted_hash=None):
viewer = self._download_json(
'https://vimeo.com/_next/viewer', video_id, 'Downloading viewer info')
for retry in (False, True):
try:
video = self._call_videos_api(video_id, viewer['jwt'], unlisted_hash)
except ExtractorError as e:
if (not retry and isinstance(e.cause, HTTPError) and e.cause.status == 400
and 'password' in traverse_obj(
e.cause.response.read(),
({bytes.decode}, {json.loads}, 'invalid_parameters', ..., 'field'),
)):
self._verify_video_password(
video_id, self._get_video_password(), viewer['xsrft'])
continue
raise
info = self._parse_config(self._download_json(
video['config_url'], video_id), video_id)
get_timestamp = lambda x: parse_iso8601(video.get(x + '_time'))
@@ -865,12 +874,6 @@ class VimeoIE(VimeoBaseInfoExtractor):
redirect_url, video_id, headers)
return self._parse_config(config, video_id)
if re.search(r'<form[^>]+?id="pw_form"', webpage):
video_password = self._get_video_password()
token, vuid = self._extract_xsrft_and_vuid(webpage)
webpage = self._verify_video_password(
redirect_url, video_id, video_password, token, vuid)
vimeo_config = self._extract_vimeo_config(webpage, video_id, default=None)
if vimeo_config:
seed_status = vimeo_config.get('seed_status') or {}
@@ -1290,9 +1293,7 @@ class VimeoReviewIE(VimeoBaseInfoExtractor):
video_password = self._get_video_password()
viewer = self._download_json(
'https://vimeo.com/_rv/viewer', video_id)
webpage = self._verify_video_password(
'https://vimeo.com/' + video_id, video_id,
video_password, viewer['xsrft'], viewer['vuid'])
webpage = self._verify_video_password(video_id, video_password, viewer['xsrft'])
clip_page_config = self._parse_json(self._search_regex(
r'window\.vimeo\.clip_page_config\s*=\s*({.+?});',
webpage, 'clip page config'), video_id)
+34 -3
View File
@@ -5059,27 +5059,53 @@ class _UnsafeExtensionError(Exception):
# video
*MEDIA_EXTENSIONS.video,
'avif',
'asx',
'ismv',
'm2t',
'm2ts',
'm2v',
'm4s',
'mng',
'mp2v',
'mp4v',
'mpe',
'mpeg',
'mpeg1',
'mpeg2',
'mpeg4',
'mxf',
'ogm',
'qt',
'rm',
'swf',
'ts',
'vob',
'vp9',
'wvm',
# audio
*MEDIA_EXTENSIONS.audio,
'3ga',
'ac3',
'adts',
'aif',
'au',
'dts',
'isma',
'it',
'mid',
'mod',
'mpga',
'mp1',
'mp2',
'mp4a',
'mpa',
'ra',
'shn',
'xm',
# image
*MEDIA_EXTENSIONS.thumbnails,
'avif',
'bmp',
'gif',
'heic',
@@ -5089,6 +5115,7 @@ class _UnsafeExtensionError(Exception):
'jxl',
'svg',
'tif',
'tiff',
'wbmp',
# subtitle
@@ -5096,11 +5123,16 @@ class _UnsafeExtensionError(Exception):
'dfxp',
'fs',
'ismt',
'json3',
'sami',
'scc',
'srv1',
'srv2',
'srv3',
'ssa',
'tt',
'ttml',
'xml',
# others
*MEDIA_EXTENSIONS.manifests,
@@ -5111,7 +5143,6 @@ class _UnsafeExtensionError(Exception):
'sbv',
'url',
'webloc',
'xml',
])
def __init__(self, extension, /):