mirror of
https://github.com/bolucat/Archive.git
synced 2026-04-23 00:17:16 +08:00
Update On Sun Nov 24 19:33:47 CET 2024
This commit is contained in:
@@ -834,3 +834,4 @@ Update On Wed Nov 20 19:36:13 CET 2024
|
|||||||
Update On Thu Nov 21 19:38:10 CET 2024
|
Update On Thu Nov 21 19:38:10 CET 2024
|
||||||
Update On Fri Nov 22 19:38:14 CET 2024
|
Update On Fri Nov 22 19:38:14 CET 2024
|
||||||
Update On Sat Nov 23 19:33:03 CET 2024
|
Update On Sat Nov 23 19:33:03 CET 2024
|
||||||
|
Update On Sun Nov 24 19:33:36 CET 2024
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ static partial class BBDownMuxer
|
|||||||
return string.IsNullOrEmpty(str) ? str : str.Replace("\"", "'").Replace("\\", "\\\\");
|
return string.IsNullOrEmpty(str) ? str : str.Replace("\"", "'").Replace("\\", "\\\\");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int MuxByMp4box(string videoPath, string audioPath, string outPath, string desc, string title, string author, string episodeId, string pic, string lang, List<Subtitle>? subs, bool audioOnly, bool videoOnly, List<ViewPoint>? points)
|
private static int MuxByMp4box(string url, string videoPath, string audioPath, string outPath, string desc, string title, string author, string episodeId, string pic, string lang, List<Subtitle>? subs, bool audioOnly, bool videoOnly, List<ViewPoint>? points)
|
||||||
{
|
{
|
||||||
StringBuilder inputArg = new();
|
StringBuilder inputArg = new();
|
||||||
StringBuilder metaArg = new();
|
StringBuilder metaArg = new();
|
||||||
@@ -73,7 +73,8 @@ static partial class BBDownMuxer
|
|||||||
metaArg.Append($":album=\"{title}\":title=\"{episodeId}\"");
|
metaArg.Append($":album=\"{title}\":title=\"{episodeId}\"");
|
||||||
else
|
else
|
||||||
metaArg.Append($":title=\"{title}\"");
|
metaArg.Append($":title=\"{title}\"");
|
||||||
metaArg.Append($":comment=\"{desc}\"");
|
metaArg.Append($":sdesc=\"{desc}\"");
|
||||||
|
metaArg.Append($":comment=\"{url}\"");
|
||||||
metaArg.Append($":artist=\"{author}\"");
|
metaArg.Append($":artist=\"{author}\"");
|
||||||
|
|
||||||
if (subs != null)
|
if (subs != null)
|
||||||
@@ -90,12 +91,12 @@ static partial class BBDownMuxer
|
|||||||
}
|
}
|
||||||
|
|
||||||
//----分析完毕
|
//----分析完毕
|
||||||
var arguments = (Config.DEBUG_LOG ? " -verbose " : "") + inputArg.ToString() + (metaArg.ToString() == "" ? "" : " -itags tool=" + metaArg.ToString()) + $" -new -- \"{outPath}\"";
|
var arguments = (Config.DEBUG_LOG ? " -v " : "") + inputArg + (metaArg.ToString() == "" ? "" : " -itags tool=" + metaArg) + $" -new -- \"{outPath}\"";
|
||||||
LogDebug("mp4box命令: {0}", arguments);
|
LogDebug("mp4box命令: {0}", arguments);
|
||||||
return RunExe(MP4BOX, arguments, MP4BOX != "mp4box");
|
return RunExe(MP4BOX, arguments, MP4BOX != "mp4box");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int MuxAV(bool useMp4box, string videoPath, string audioPath, List<AudioMaterial> audioMaterial, string outPath, string desc = "", string title = "", string author = "", string episodeId = "", string pic = "", string lang = "", List<Subtitle>? subs = null, bool audioOnly = false, bool videoOnly = false, List<ViewPoint>? points = null, long pubTime = 0, bool simplyMux = false)
|
public static int MuxAV(bool useMp4box, string bvid, string videoPath, string audioPath, List<AudioMaterial> audioMaterial, string outPath, string desc = "", string title = "", string author = "", string episodeId = "", string pic = "", string lang = "", List<Subtitle>? subs = null, bool audioOnly = false, bool videoOnly = false, List<ViewPoint>? points = null, long pubTime = 0, bool simplyMux = false)
|
||||||
{
|
{
|
||||||
if (audioOnly && audioPath != "")
|
if (audioOnly && audioPath != "")
|
||||||
videoPath = "";
|
videoPath = "";
|
||||||
@@ -104,10 +105,11 @@ static partial class BBDownMuxer
|
|||||||
desc = EscapeString(desc);
|
desc = EscapeString(desc);
|
||||||
title = EscapeString(title);
|
title = EscapeString(title);
|
||||||
episodeId = EscapeString(episodeId);
|
episodeId = EscapeString(episodeId);
|
||||||
|
var url = $"https://www.bilibili.com/video/{bvid}/";
|
||||||
|
|
||||||
if (useMp4box)
|
if (useMp4box)
|
||||||
{
|
{
|
||||||
return MuxByMp4box(videoPath, audioPath, outPath, desc, title, author, episodeId, pic, lang, subs, audioOnly, videoOnly, points);
|
return MuxByMp4box(url, videoPath, audioPath, outPath, desc, title, author, episodeId, pic, lang, subs, audioOnly, videoOnly, points);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outPath.Contains('/') && ! Directory.Exists(Path.GetDirectoryName(outPath)))
|
if (outPath.Contains('/') && ! Directory.Exists(Path.GetDirectoryName(outPath)))
|
||||||
@@ -179,6 +181,7 @@ static partial class BBDownMuxer
|
|||||||
argsBuilder.Append(metaArg);
|
argsBuilder.Append(metaArg);
|
||||||
if (!simplyMux) {
|
if (!simplyMux) {
|
||||||
argsBuilder.Append($"-metadata title=\"{(episodeId == "" ? title : episodeId)}\" ");
|
argsBuilder.Append($"-metadata title=\"{(episodeId == "" ? title : episodeId)}\" ");
|
||||||
|
argsBuilder.Append($"-metadata comment=\"{url}\" ");
|
||||||
if (lang != "") argsBuilder.Append($"-metadata:s:a:0 language={lang} ");
|
if (lang != "") argsBuilder.Append($"-metadata:s:a:0 language={lang} ");
|
||||||
if (!string.IsNullOrWhiteSpace(desc)) argsBuilder.Append($"-metadata description=\"{desc}\" ");
|
if (!string.IsNullOrWhiteSpace(desc)) argsBuilder.Append($"-metadata description=\"{desc}\" ");
|
||||||
if (!string.IsNullOrEmpty(author)) argsBuilder.Append($"-metadata artist=\"{author}\" ");
|
if (!string.IsNullOrEmpty(author)) argsBuilder.Append($"-metadata artist=\"{author}\" ");
|
||||||
|
|||||||
@@ -244,7 +244,7 @@ partial class Program
|
|||||||
|
|
||||||
Log("获取aid...");
|
Log("获取aid...");
|
||||||
aidOri = await GetAvIdAsync(input);
|
aidOri = await GetAvIdAsync(input);
|
||||||
Log("获取aid结束: " + aidOri);
|
Log($"获取aid结束: {aidOri}");
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(aidOri))
|
if (string.IsNullOrEmpty(aidOri))
|
||||||
{
|
{
|
||||||
@@ -287,6 +287,11 @@ partial class Program
|
|||||||
{
|
{
|
||||||
Log("发布时间: " + FormatTimeStamp(pubTime, "yyyy-MM-dd HH:mm:ss zzz"));
|
Log("发布时间: " + FormatTimeStamp(pubTime, "yyyy-MM-dd HH:mm:ss zzz"));
|
||||||
}
|
}
|
||||||
|
var bvid = vInfo.PagesInfo.FirstOrDefault()?.bvid;
|
||||||
|
if (!string.IsNullOrEmpty(bvid))
|
||||||
|
{
|
||||||
|
Log($"视频URL: https://www.bilibili.com/video/{bvid}/");
|
||||||
|
}
|
||||||
var mid = vInfo.PagesInfo.FirstOrDefault(p => !string.IsNullOrEmpty(p.ownerMid))?.ownerMid;
|
var mid = vInfo.PagesInfo.FirstOrDefault(p => !string.IsNullOrEmpty(p.ownerMid))?.ownerMid;
|
||||||
if (!string.IsNullOrEmpty(mid))
|
if (!string.IsNullOrEmpty(mid))
|
||||||
{
|
{
|
||||||
@@ -664,7 +669,7 @@ partial class Program
|
|||||||
Log($"开始合并音视频{(subtitleInfo.Any() ? "和字幕" : "")}...");
|
Log($"开始合并音视频{(subtitleInfo.Any() ? "和字幕" : "")}...");
|
||||||
if (myOption.AudioOnly)
|
if (myOption.AudioOnly)
|
||||||
savePath = savePath[..^4] + ".m4a";
|
savePath = savePath[..^4] + ".m4a";
|
||||||
int code = BBDownMuxer.MuxAV(myOption.UseMP4box, videoPath, audioPath, audioMaterial, savePath,
|
int code = BBDownMuxer.MuxAV(myOption.UseMP4box, p.bvid, videoPath, audioPath, audioMaterial, savePath,
|
||||||
desc,
|
desc,
|
||||||
title,
|
title,
|
||||||
p.ownerName ?? "",
|
p.ownerName ?? "",
|
||||||
@@ -753,7 +758,7 @@ partial class Program
|
|||||||
Log($"开始混流视频{(subtitleInfo.Any() ? "和字幕" : "")}...");
|
Log($"开始混流视频{(subtitleInfo.Any() ? "和字幕" : "")}...");
|
||||||
if (myOption.AudioOnly)
|
if (myOption.AudioOnly)
|
||||||
savePath = savePath[..^4] + ".m4a";
|
savePath = savePath[..^4] + ".m4a";
|
||||||
int code = BBDownMuxer.MuxAV(false, videoPath, "", audioMaterial, savePath,
|
int code = BBDownMuxer.MuxAV(false, p.bvid, videoPath, "", audioMaterial, savePath,
|
||||||
desc,
|
desc,
|
||||||
title,
|
title,
|
||||||
p.ownerName ?? "",
|
p.ownerName ?? "",
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Generated
+207
-208
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,7 @@ license = "GPL-3.0"
|
|||||||
authors = ["zzzgydi", "keiko233"]
|
authors = ["zzzgydi", "keiko233"]
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
thiserror = "1"
|
thiserror = "2"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
boa_engine = { version = "0.19.1" }
|
boa_engine = { version = "0.19.1" }
|
||||||
|
|
||||||
|
|||||||
@@ -109,12 +109,12 @@ atomic_enum = "0.3.0"
|
|||||||
enumflags2 = "0.7"
|
enumflags2 = "0.7"
|
||||||
|
|
||||||
# System Utilities
|
# System Utilities
|
||||||
auto-launch = { git = "https://github.com/zzzgydi/auto-launch.git", version = "0.5" }
|
auto-launch = { git = "https://github.com/libnyanpasu/auto-launch.git", version = "0.5" }
|
||||||
delay_timer = { version = "0.11", git = "https://github.com/libnyanpasu/delay-timer.git" } # Task scheduler with timer
|
delay_timer = { version = "0.11", git = "https://github.com/libnyanpasu/delay-timer.git" } # Task scheduler with timer
|
||||||
dunce = "1.0.4" # for cross platform path normalization
|
dunce = "1.0.4" # for cross platform path normalization
|
||||||
runas = { git = "https://github.com/libnyanpasu/rust-runas.git" }
|
runas = { git = "https://github.com/libnyanpasu/rust-runas.git" }
|
||||||
single-instance = "0.3.3"
|
single-instance = "0.3.3"
|
||||||
which = "6"
|
which = "7"
|
||||||
dirs = "5.0.1"
|
dirs = "5.0.1"
|
||||||
open = "5.0.1"
|
open = "5.0.1"
|
||||||
sysinfo = "0.32"
|
sysinfo = "0.32"
|
||||||
|
|||||||
@@ -84,7 +84,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"permissions": {
|
"permissions": {
|
||||||
"description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"core:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ```",
|
"description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"core:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ] ```",
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/definitions/PermissionEntry"
|
"$ref": "#/definitions/PermissionEntry"
|
||||||
|
|||||||
@@ -84,7 +84,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"permissions": {
|
"permissions": {
|
||||||
"description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"core:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ```",
|
"description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"core:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ] ```",
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/definitions/PermissionEntry"
|
"$ref": "#/definitions/PermissionEntry"
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ enum Commands {
|
|||||||
#[arg(raw = true)]
|
#[arg(raw = true)]
|
||||||
args: Vec<String>,
|
args: Vec<String>,
|
||||||
},
|
},
|
||||||
|
/// Show a panic dialog while the application is enter panic handler.
|
||||||
|
PanicDialog { message: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DelayedExitGuard;
|
struct DelayedExitGuard;
|
||||||
@@ -87,6 +89,9 @@ pub fn parse() -> anyhow::Result<()> {
|
|||||||
let envs = crate::utils::collect::collect_envs().unwrap();
|
let envs = crate::utils::collect::collect_envs().unwrap();
|
||||||
println!("{:#?}", envs);
|
println!("{:#?}", envs);
|
||||||
}
|
}
|
||||||
|
Commands::PanicDialog { message } => {
|
||||||
|
crate::utils::dialog::panic_dialog(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
drop(guard);
|
drop(guard);
|
||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
|
|||||||
@@ -108,7 +108,6 @@ pub fn run() -> std::io::Result<()> {
|
|||||||
crate::log_err!(init::init_config());
|
crate::log_err!(init::init_config());
|
||||||
|
|
||||||
// Panic Hook to show a panic dialog and save logs
|
// Panic Hook to show a panic dialog and save logs
|
||||||
let default_panic = std::panic::take_hook();
|
|
||||||
std::panic::set_hook(Box::new(move |panic_info| {
|
std::panic::set_hook(Box::new(move |panic_info| {
|
||||||
use std::backtrace::{Backtrace, BacktraceStatus};
|
use std::backtrace::{Backtrace, BacktraceStatus};
|
||||||
let payload = panic_info.payload();
|
let payload = panic_info.payload();
|
||||||
@@ -124,7 +123,7 @@ pub fn run() -> std::io::Result<()> {
|
|||||||
|
|
||||||
let location = panic_info.location().map(|l| l.to_string());
|
let location = panic_info.location().map(|l| l.to_string());
|
||||||
let (backtrace, note) = {
|
let (backtrace, note) = {
|
||||||
let backtrace = Backtrace::capture();
|
let backtrace = Backtrace::force_capture();
|
||||||
let note = (backtrace.status() == BacktraceStatus::Disabled)
|
let note = (backtrace.status() == BacktraceStatus::Disabled)
|
||||||
.then_some("run with RUST_BACKTRACE=1 environment variable to display a backtrace");
|
.then_some("run with RUST_BACKTRACE=1 environment variable to display a backtrace");
|
||||||
(Some(backtrace), note)
|
(Some(backtrace), note)
|
||||||
@@ -145,20 +144,29 @@ pub fn run() -> std::io::Result<()> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
utils::dialog::panic_dialog(&format!(
|
// FIXME: maybe move this logic to a util function?
|
||||||
"payload: {:#?}\nlocation: {:?}\nbacktrace: {:#?}\n\nnote: {:?}",
|
let msg = format!(
|
||||||
payload, location, backtrace, note
|
"Oops, we encountered some issues and program will exit immediately.\n\npayload: {:#?}\nlocation: {:?}\nbacktrace: {:#?}\n\n",
|
||||||
));
|
payload, location, backtrace,
|
||||||
|
);
|
||||||
|
let child = std::process::Command::new(tauri::utils::platform::current_exe().unwrap())
|
||||||
|
.arg("panic-dialog")
|
||||||
|
.arg(msg.as_str())
|
||||||
|
.spawn();
|
||||||
|
// fallback to show a dialog directly
|
||||||
|
if child.is_err() {
|
||||||
|
utils::dialog::panic_dialog(msg.as_str());
|
||||||
|
}
|
||||||
|
|
||||||
// cleanup the core manager
|
match Handle::global().app_handle.lock().as_ref() {
|
||||||
let task = std::thread::spawn(move || {
|
Some(app_handle) => {
|
||||||
nyanpasu_utils::runtime::block_on(async {
|
app_handle.exit(1);
|
||||||
let _ = crate::core::CoreManager::global().stop_core().await;
|
}
|
||||||
});
|
None => {
|
||||||
});
|
log::error!("app handle is not initialized");
|
||||||
let _ = task.join();
|
std::process::exit(1);
|
||||||
default_panic(panic_info);
|
}
|
||||||
std::process::exit(1); // exit if default panic handler doesn't exit
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let verge = { Config::verge().latest().language.clone().unwrap() };
|
let verge = { Config::verge().latest().language.clone().unwrap() };
|
||||||
@@ -322,39 +330,35 @@ pub fn run() -> std::io::Result<()> {
|
|||||||
.build(tauri::generate_context!())
|
.build(tauri::generate_context!())
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
app.run(|app_handle, e| match e {
|
app.run(|app_handle, e| match e {
|
||||||
tauri::RunEvent::ExitRequested { api, .. } => {
|
tauri::RunEvent::ExitRequested { api, code, .. } if code.is_none() => {
|
||||||
api.prevent_exit();
|
api.prevent_exit();
|
||||||
}
|
}
|
||||||
tauri::RunEvent::Exit => {
|
tauri::RunEvent::ExitRequested { .. } => {
|
||||||
resolve::resolve_reset();
|
utils::help::cleanup_processes(app_handle);
|
||||||
}
|
}
|
||||||
tauri::RunEvent::WindowEvent { label, event, .. } => {
|
tauri::RunEvent::WindowEvent { label, event, .. } if label == "main" => match event {
|
||||||
if label == "main" {
|
tauri::WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
|
||||||
match event {
|
core::tray::on_scale_factor_changed(scale_factor);
|
||||||
tauri::WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
|
|
||||||
core::tray::on_scale_factor_changed(scale_factor);
|
|
||||||
}
|
|
||||||
tauri::WindowEvent::CloseRequested { .. } => {
|
|
||||||
log::debug!(target: "app", "window close requested");
|
|
||||||
let _ = resolve::save_window_state(app_handle, true);
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
log_err!(app_handle.run_on_main_thread(|| {
|
|
||||||
crate::utils::dock::macos::hide_dock_icon();
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
tauri::WindowEvent::Destroyed => {
|
|
||||||
log::debug!(target: "app", "window destroyed");
|
|
||||||
reset_window_open_counter();
|
|
||||||
}
|
|
||||||
tauri::WindowEvent::Moved(_) | tauri::WindowEvent::Resized(_) => {
|
|
||||||
log::debug!(target: "app", "window moved or resized");
|
|
||||||
std::thread::sleep(std::time::Duration::from_nanos(1));
|
|
||||||
let _ = resolve::save_window_state(app_handle, false);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
tauri::WindowEvent::CloseRequested { .. } => {
|
||||||
|
log::debug!(target: "app", "window close requested");
|
||||||
|
let _ = resolve::save_window_state(app_handle, true);
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
log_err!(app_handle.run_on_main_thread(|| {
|
||||||
|
crate::utils::dock::macos::hide_dock_icon();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
tauri::WindowEvent::Destroyed => {
|
||||||
|
log::debug!(target: "app", "window destroyed");
|
||||||
|
reset_window_open_counter();
|
||||||
|
}
|
||||||
|
tauri::WindowEvent::Moved(_) | tauri::WindowEvent::Resized(_) => {
|
||||||
|
log::debug!(target: "app", "window moved or resized");
|
||||||
|
std::thread::sleep(std::time::Duration::from_nanos(1));
|
||||||
|
let _ = resolve::save_window_state(app_handle, false);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -247,9 +247,7 @@ pub fn cleanup_processes(app_handle: &AppHandle) {
|
|||||||
|
|
||||||
#[instrument(skip(app_handle))]
|
#[instrument(skip(app_handle))]
|
||||||
pub fn quit_application(app_handle: &AppHandle) {
|
pub fn quit_application(app_handle: &AppHandle) {
|
||||||
cleanup_processes(app_handle);
|
|
||||||
app_handle.exit(0);
|
app_handle.exit(0);
|
||||||
std::process::exit(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(app_handle))]
|
#[instrument(skip(app_handle))]
|
||||||
|
|||||||
@@ -10,8 +10,8 @@
|
|||||||
"serve": "vite preview"
|
"serve": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dnd-kit/core": "6.1.0",
|
"@dnd-kit/core": "6.2.0",
|
||||||
"@dnd-kit/sortable": "8.0.0",
|
"@dnd-kit/sortable": "9.0.0",
|
||||||
"@dnd-kit/utilities": "3.2.2",
|
"@dnd-kit/utilities": "3.2.2",
|
||||||
"@emotion/styled": "11.13.5",
|
"@emotion/styled": "11.13.5",
|
||||||
"@juggle/resize-observer": "3.4.0",
|
"@juggle/resize-observer": "3.4.0",
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
"country-code-emoji": "2.3.0",
|
"country-code-emoji": "2.3.0",
|
||||||
"dayjs": "1.11.13",
|
"dayjs": "1.11.13",
|
||||||
"framer-motion": "12.0.0-alpha.2",
|
"framer-motion": "12.0.0-alpha.2",
|
||||||
"i18next": "23.16.8",
|
"i18next": "24.0.0",
|
||||||
"jotai": "2.10.3",
|
"jotai": "2.10.3",
|
||||||
"json-schema": "0.4.0",
|
"json-schema": "0.4.0",
|
||||||
"material-react-table": "3.0.1",
|
"material-react-table": "3.0.1",
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"mihomo_alpha": "alpha-eb985b0",
|
"mihomo_alpha": "alpha-eb985b0",
|
||||||
"clash_rs": "v0.7.1",
|
"clash_rs": "v0.7.1",
|
||||||
"clash_premium": "2023-09-05-gdcc8d87",
|
"clash_premium": "2023-09-05-gdcc8d87",
|
||||||
"clash_rs_alpha": "0.7.1-alpha+sha.92b5bfc"
|
"clash_rs_alpha": "0.7.1-alpha+sha.a4b59dc"
|
||||||
},
|
},
|
||||||
"arch_template": {
|
"arch_template": {
|
||||||
"mihomo": {
|
"mihomo": {
|
||||||
@@ -69,5 +69,5 @@
|
|||||||
"linux-armv7hf": "clash-armv7-unknown-linux-gnueabihf"
|
"linux-armv7hf": "clash-armv7-unknown-linux-gnueabihf"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"updated_at": "2024-11-21T22:20:51.459Z"
|
"updated_at": "2024-11-23T22:20:39.660Z"
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+32
-25
@@ -184,11 +184,11 @@ importers:
|
|||||||
frontend/nyanpasu:
|
frontend/nyanpasu:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@dnd-kit/core':
|
'@dnd-kit/core':
|
||||||
specifier: 6.1.0
|
specifier: 6.2.0
|
||||||
version: 6.1.0(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1)
|
version: 6.2.0(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1)
|
||||||
'@dnd-kit/sortable':
|
'@dnd-kit/sortable':
|
||||||
specifier: 8.0.0
|
specifier: 9.0.0
|
||||||
version: 8.0.0(@dnd-kit/core@6.1.0(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1))(react@19.0.0-rc.1)
|
version: 9.0.0(@dnd-kit/core@6.2.0(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1))(react@19.0.0-rc.1)
|
||||||
'@dnd-kit/utilities':
|
'@dnd-kit/utilities':
|
||||||
specifier: 3.2.2
|
specifier: 3.2.2
|
||||||
version: 3.2.2(react@19.0.0-rc.1)
|
version: 3.2.2(react@19.0.0-rc.1)
|
||||||
@@ -241,8 +241,8 @@ importers:
|
|||||||
specifier: 12.0.0-alpha.2
|
specifier: 12.0.0-alpha.2
|
||||||
version: 12.0.0-alpha.2(@emotion/is-prop-valid@1.3.0)(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1)
|
version: 12.0.0-alpha.2(@emotion/is-prop-valid@1.3.0)(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1)
|
||||||
i18next:
|
i18next:
|
||||||
specifier: 23.16.8
|
specifier: 24.0.0
|
||||||
version: 23.16.8
|
version: 24.0.0(typescript@5.7.2)
|
||||||
jotai:
|
jotai:
|
||||||
specifier: 2.10.3
|
specifier: 2.10.3
|
||||||
version: 2.10.3(react@19.0.0-rc.1)(types-react@19.0.0-rc.1)
|
version: 2.10.3(react@19.0.0-rc.1)(types-react@19.0.0-rc.1)
|
||||||
@@ -275,7 +275,7 @@ importers:
|
|||||||
version: 7.4.0(c6eqiv3v4ro6nnqx6e4soqhoku)
|
version: 7.4.0(c6eqiv3v4ro6nnqx6e4soqhoku)
|
||||||
react-i18next:
|
react-i18next:
|
||||||
specifier: 15.1.1
|
specifier: 15.1.1
|
||||||
version: 15.1.1(i18next@23.16.8)(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1)
|
version: 15.1.1(i18next@24.0.0(typescript@5.7.2))(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1)
|
||||||
react-markdown:
|
react-markdown:
|
||||||
specifier: 9.0.1
|
specifier: 9.0.1
|
||||||
version: 9.0.1(react@19.0.0-rc.1)(types-react@19.0.0-rc.1)
|
version: 9.0.1(react@19.0.0-rc.1)(types-react@19.0.0-rc.1)
|
||||||
@@ -468,7 +468,7 @@ importers:
|
|||||||
version: 4.1.2(react@19.0.0-rc.1)
|
version: 4.1.2(react@19.0.0-rc.1)
|
||||||
react-i18next:
|
react-i18next:
|
||||||
specifier: 15.1.1
|
specifier: 15.1.1
|
||||||
version: 15.1.1(i18next@23.16.8)(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1)
|
version: 15.1.1(i18next@24.0.0(typescript@5.7.2))(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1)
|
||||||
react-use:
|
react-use:
|
||||||
specifier: 17.5.1
|
specifier: 17.5.1
|
||||||
version: 17.5.1(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1)
|
version: 17.5.1(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1)
|
||||||
@@ -1294,21 +1294,21 @@ packages:
|
|||||||
resolution: {integrity: sha512-WyOx8cJQ+FQus4Mm4uPIZA64gbk3Wxh0so5Lcii0aJifqwoVOlfFtorjLE0Hen4OYyHZMXDWqMmaQemBhgxFRQ==}
|
resolution: {integrity: sha512-WyOx8cJQ+FQus4Mm4uPIZA64gbk3Wxh0so5Lcii0aJifqwoVOlfFtorjLE0Hen4OYyHZMXDWqMmaQemBhgxFRQ==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
|
|
||||||
'@dnd-kit/accessibility@3.1.0':
|
'@dnd-kit/accessibility@3.1.1':
|
||||||
resolution: {integrity: sha512-ea7IkhKvlJUv9iSHJOnxinBcoOI3ppGnnL+VDJ75O45Nss6HtZd8IdN8touXPDtASfeI2T2LImb8VOZcL47wjQ==}
|
resolution: {integrity: sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: '>=16.8.0'
|
react: '>=16.8.0'
|
||||||
|
|
||||||
'@dnd-kit/core@6.1.0':
|
'@dnd-kit/core@6.2.0':
|
||||||
resolution: {integrity: sha512-J3cQBClB4TVxwGo3KEjssGEXNJqGVWx17aRTZ1ob0FliR5IjYgTxl5YJbKTzA6IzrtelotH19v6y7uoIRUZPSg==}
|
resolution: {integrity: sha512-KVK/CJmaYGTxTPU6P0+Oy4itgffTUa80B8317sXzfOr1qUzSL29jE7Th11llXiu2haB7B9Glpzo2CDElin+geQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: '>=16.8.0'
|
react: '>=16.8.0'
|
||||||
react-dom: '>=16.8.0'
|
react-dom: '>=16.8.0'
|
||||||
|
|
||||||
'@dnd-kit/sortable@8.0.0':
|
'@dnd-kit/sortable@9.0.0':
|
||||||
resolution: {integrity: sha512-U3jk5ebVXe1Lr7c2wU7SBZjcWdQP+j7peHJfCspnA81enlu88Mgd7CC8Q+pub9ubP7eKVETzJW+IBAhsqbSu/g==}
|
resolution: {integrity: sha512-3/9r8Mmba0nKTbo8kPnVSFZKf/VSy94nXZ3aUwzPEh78j/LooQ/EFKRZENak4PHKBkN53mgTF/z+Sd8H+FcAnQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@dnd-kit/core': ^6.1.0
|
'@dnd-kit/core': ^6.2.0
|
||||||
react: '>=16.8.0'
|
react: '>=16.8.0'
|
||||||
|
|
||||||
'@dnd-kit/utilities@3.2.2':
|
'@dnd-kit/utilities@3.2.2':
|
||||||
@@ -4849,8 +4849,13 @@ packages:
|
|||||||
hyphenate-style-name@1.1.0:
|
hyphenate-style-name@1.1.0:
|
||||||
resolution: {integrity: sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==}
|
resolution: {integrity: sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==}
|
||||||
|
|
||||||
i18next@23.16.8:
|
i18next@24.0.0:
|
||||||
resolution: {integrity: sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg==}
|
resolution: {integrity: sha512-ORGCwMrXxpmB/AljFbGEe0UK/9Pz6umb9aZgLZ9qJGE+kjKhlnLj423WX2mt+N0MlEJ78pQXFMBmeMzrkLxriQ==}
|
||||||
|
peerDependencies:
|
||||||
|
typescript: ^5
|
||||||
|
peerDependenciesMeta:
|
||||||
|
typescript:
|
||||||
|
optional: true
|
||||||
|
|
||||||
iconv-lite@0.6.3:
|
iconv-lite@0.6.3:
|
||||||
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
|
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
|
||||||
@@ -8777,22 +8782,22 @@ snapshots:
|
|||||||
|
|
||||||
'@ctrl/tinycolor@4.1.0': {}
|
'@ctrl/tinycolor@4.1.0': {}
|
||||||
|
|
||||||
'@dnd-kit/accessibility@3.1.0(react@19.0.0-rc.1)':
|
'@dnd-kit/accessibility@3.1.1(react@19.0.0-rc.1)':
|
||||||
dependencies:
|
dependencies:
|
||||||
react: 19.0.0-rc.1
|
react: 19.0.0-rc.1
|
||||||
tslib: 2.7.0
|
tslib: 2.7.0
|
||||||
|
|
||||||
'@dnd-kit/core@6.1.0(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1)':
|
'@dnd-kit/core@6.2.0(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@dnd-kit/accessibility': 3.1.0(react@19.0.0-rc.1)
|
'@dnd-kit/accessibility': 3.1.1(react@19.0.0-rc.1)
|
||||||
'@dnd-kit/utilities': 3.2.2(react@19.0.0-rc.1)
|
'@dnd-kit/utilities': 3.2.2(react@19.0.0-rc.1)
|
||||||
react: 19.0.0-rc.1
|
react: 19.0.0-rc.1
|
||||||
react-dom: 19.0.0-rc.1(react@19.0.0-rc.1)
|
react-dom: 19.0.0-rc.1(react@19.0.0-rc.1)
|
||||||
tslib: 2.7.0
|
tslib: 2.7.0
|
||||||
|
|
||||||
'@dnd-kit/sortable@8.0.0(@dnd-kit/core@6.1.0(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1))(react@19.0.0-rc.1)':
|
'@dnd-kit/sortable@9.0.0(@dnd-kit/core@6.2.0(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1))(react@19.0.0-rc.1)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@dnd-kit/core': 6.1.0(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1)
|
'@dnd-kit/core': 6.2.0(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1)
|
||||||
'@dnd-kit/utilities': 3.2.2(react@19.0.0-rc.1)
|
'@dnd-kit/utilities': 3.2.2(react@19.0.0-rc.1)
|
||||||
react: 19.0.0-rc.1
|
react: 19.0.0-rc.1
|
||||||
tslib: 2.7.0
|
tslib: 2.7.0
|
||||||
@@ -12589,9 +12594,11 @@ snapshots:
|
|||||||
|
|
||||||
hyphenate-style-name@1.1.0: {}
|
hyphenate-style-name@1.1.0: {}
|
||||||
|
|
||||||
i18next@23.16.8:
|
i18next@24.0.0(typescript@5.7.2):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.26.0
|
'@babel/runtime': 7.26.0
|
||||||
|
optionalDependencies:
|
||||||
|
typescript: 5.7.2
|
||||||
|
|
||||||
iconv-lite@0.6.3:
|
iconv-lite@0.6.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -14113,11 +14120,11 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
react: 19.0.0-rc.1
|
react: 19.0.0-rc.1
|
||||||
|
|
||||||
react-i18next@15.1.1(i18next@23.16.8)(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1):
|
react-i18next@15.1.1(i18next@24.0.0(typescript@5.7.2))(react-dom@19.0.0-rc.1(react@19.0.0-rc.1))(react@19.0.0-rc.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.26.0
|
'@babel/runtime': 7.26.0
|
||||||
html-parse-stringify: 3.0.1
|
html-parse-stringify: 3.0.1
|
||||||
i18next: 23.16.8
|
i18next: 24.0.0(typescript@5.7.2)
|
||||||
react: 19.0.0-rc.1
|
react: 19.0.0-rc.1
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
react-dom: 19.0.0-rc.1(react@19.0.0-rc.1)
|
react-dom: 19.0.0-rc.1(react@19.0.0-rc.1)
|
||||||
|
|||||||
@@ -33,7 +33,9 @@ $(call KernelPackage/r8125)
|
|||||||
endef
|
endef
|
||||||
|
|
||||||
ifeq ($(BUILD_VARIANT),rss)
|
ifeq ($(BUILD_VARIANT),rss)
|
||||||
PKG_MAKE_FLAGS += ENABLE_RSS_SUPPORT=y
|
PKG_MAKE_FLAGS += \
|
||||||
|
ENABLE_RSS_SUPPORT=y \
|
||||||
|
ENABLE_MULTIPLE_TX_QUEUE=y
|
||||||
endif
|
endif
|
||||||
|
|
||||||
PKG_MAKE_FLAGS += CONFIG_ASPM=n
|
PKG_MAKE_FLAGS += CONFIG_ASPM=n
|
||||||
|
|||||||
@@ -33,7 +33,9 @@ $(call KernelPackage/r8126)
|
|||||||
endef
|
endef
|
||||||
|
|
||||||
ifeq ($(BUILD_VARIANT),rss)
|
ifeq ($(BUILD_VARIANT),rss)
|
||||||
PKG_MAKE_FLAGS += ENABLE_RSS_SUPPORT=y
|
PKG_MAKE_FLAGS += \
|
||||||
|
ENABLE_RSS_SUPPORT=y \
|
||||||
|
ENABLE_MULTIPLE_TX_QUEUE=y
|
||||||
endif
|
endif
|
||||||
|
|
||||||
PKG_MAKE_FLAGS += CONFIG_ASPM=n
|
PKG_MAKE_FLAGS += CONFIG_ASPM=n
|
||||||
|
|||||||
+1
-8
@@ -296,10 +296,6 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
* fspi is unavailable
|
|
||||||
* use i2c instead
|
|
||||||
*/
|
|
||||||
&i2c8 {
|
&i2c8 {
|
||||||
pinctrl-names = "default";
|
pinctrl-names = "default";
|
||||||
pinctrl-0 = <&i2c8m1_xfer>;
|
pinctrl-0 = <&i2c8m1_xfer>;
|
||||||
@@ -840,9 +836,6 @@
|
|||||||
status = "okay";
|
status = "okay";
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
* Disabled due to driver bug.
|
|
||||||
*/
|
|
||||||
&usb_host2_xhci {
|
&usb_host2_xhci {
|
||||||
status = "disabled";
|
status = "okay";
|
||||||
};
|
};
|
||||||
|
|||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
--- a/arch/arm64/boot/dts/rockchip/rk3568.dtsi
|
||||||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3568.dtsi
|
||||||
|
@@ -225,6 +225,7 @@
|
||||||
|
assigned-clocks = <&pmucru CLK_PCIEPHY0_REF>;
|
||||||
|
assigned-clock-rates = <100000000>;
|
||||||
|
resets = <&cru SRST_PIPEPHY0>;
|
||||||
|
+ reset-names = "phy";
|
||||||
|
rockchip,pipe-grf = <&pipegrf>;
|
||||||
|
rockchip,pipe-phy-grf = <&pipe_phy_grf0>;
|
||||||
|
#phy-cells = <1>;
|
||||||
|
--- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi
|
||||||
|
+++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
|
||||||
|
@@ -1686,6 +1686,7 @@
|
||||||
|
assigned-clocks = <&pmucru CLK_PCIEPHY1_REF>;
|
||||||
|
assigned-clock-rates = <100000000>;
|
||||||
|
resets = <&cru SRST_PIPEPHY1>;
|
||||||
|
+ reset-names = "phy";
|
||||||
|
rockchip,pipe-grf = <&pipegrf>;
|
||||||
|
rockchip,pipe-phy-grf = <&pipe_phy_grf1>;
|
||||||
|
#phy-cells = <1>;
|
||||||
|
@@ -1702,6 +1703,7 @@
|
||||||
|
assigned-clocks = <&pmucru CLK_PCIEPHY2_REF>;
|
||||||
|
assigned-clock-rates = <100000000>;
|
||||||
|
resets = <&cru SRST_PIPEPHY2>;
|
||||||
|
+ reset-names = "phy";
|
||||||
|
rockchip,pipe-grf = <&pipegrf>;
|
||||||
|
rockchip,pipe-phy-grf = <&pipe_phy_grf2>;
|
||||||
|
#phy-cells = <1>;
|
||||||
|
--- a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c
|
||||||
|
+++ b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c
|
||||||
|
@@ -299,7 +299,7 @@ static int rockchip_combphy_parse_dt(str
|
||||||
|
|
||||||
|
priv->ext_refclk = device_property_present(dev, "rockchip,ext-refclk");
|
||||||
|
|
||||||
|
- priv->phy_rst = devm_reset_control_array_get_exclusive(dev);
|
||||||
|
+ priv->phy_rst = devm_reset_control_get(dev, "phy");
|
||||||
|
if (IS_ERR(priv->phy_rst))
|
||||||
|
return dev_err_probe(dev, PTR_ERR(priv->phy_rst), "failed to get phy reset\n");
|
||||||
|
|
||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
--- a/arch/arm64/boot/dts/rockchip/rk3568.dtsi
|
||||||
|
+++ b/arch/arm64/boot/dts/rockchip/rk3568.dtsi
|
||||||
|
@@ -225,6 +225,7 @@
|
||||||
|
assigned-clocks = <&pmucru CLK_PCIEPHY0_REF>;
|
||||||
|
assigned-clock-rates = <100000000>;
|
||||||
|
resets = <&cru SRST_PIPEPHY0>;
|
||||||
|
+ reset-names = "phy";
|
||||||
|
rockchip,pipe-grf = <&pipegrf>;
|
||||||
|
rockchip,pipe-phy-grf = <&pipe_phy_grf0>;
|
||||||
|
#phy-cells = <1>;
|
||||||
|
--- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi
|
||||||
|
+++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
|
||||||
|
@@ -1719,6 +1719,7 @@
|
||||||
|
assigned-clocks = <&pmucru CLK_PCIEPHY1_REF>;
|
||||||
|
assigned-clock-rates = <100000000>;
|
||||||
|
resets = <&cru SRST_PIPEPHY1>;
|
||||||
|
+ reset-names = "phy";
|
||||||
|
rockchip,pipe-grf = <&pipegrf>;
|
||||||
|
rockchip,pipe-phy-grf = <&pipe_phy_grf1>;
|
||||||
|
#phy-cells = <1>;
|
||||||
|
@@ -1735,6 +1736,7 @@
|
||||||
|
assigned-clocks = <&pmucru CLK_PCIEPHY2_REF>;
|
||||||
|
assigned-clock-rates = <100000000>;
|
||||||
|
resets = <&cru SRST_PIPEPHY2>;
|
||||||
|
+ reset-names = "phy";
|
||||||
|
rockchip,pipe-grf = <&pipegrf>;
|
||||||
|
rockchip,pipe-phy-grf = <&pipe_phy_grf2>;
|
||||||
|
#phy-cells = <1>;
|
||||||
|
--- a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c
|
||||||
|
+++ b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c
|
||||||
|
@@ -324,7 +324,7 @@ static int rockchip_combphy_parse_dt(str
|
||||||
|
|
||||||
|
priv->ext_refclk = device_property_present(dev, "rockchip,ext-refclk");
|
||||||
|
|
||||||
|
- priv->phy_rst = devm_reset_control_array_get_exclusive(dev);
|
||||||
|
+ priv->phy_rst = devm_reset_control_get(dev, "phy");
|
||||||
|
if (IS_ERR(priv->phy_rst))
|
||||||
|
return dev_err_probe(dev, PTR_ERR(priv->phy_rst), "failed to get phy reset\n");
|
||||||
|
|
||||||
@@ -98,9 +98,50 @@ if not fs.access(CACHE_PATH) then
|
|||||||
fs.mkdir(CACHE_PATH)
|
fs.mkdir(CACHE_PATH)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local LOCAL_EXTEND_ARG = ""
|
||||||
if LOCAL_GROUP == "nil" then
|
if LOCAL_GROUP == "nil" then
|
||||||
LOCAL_GROUP = nil
|
LOCAL_GROUP = nil
|
||||||
log(" * 注意:国内分组名未设置,可能会导致 DNS 解析异常!")
|
log(" * 注意:国内分组名未设置,可能会导致 DNS 分流错误!")
|
||||||
|
else
|
||||||
|
--从smartdns配置中读取参数
|
||||||
|
local custom_conf_path = "/etc/smartdns/custom.conf"
|
||||||
|
local options = {
|
||||||
|
{key = "dualstack_ip_selection", config_key = "dualstack-ip-selection", yes_no = true, arg_yes = "-d yes", arg_no = "-d no", default = "yes"},
|
||||||
|
{key = "speed_check_mode", config_key = "speed-check-mode", prefix = "-c ", default = "ping,tcp:80,tcp:443"},
|
||||||
|
{key = "serve_expired", config_key = "serve-expired", yes_no = true, arg_yes = "", arg_no = "-no-serve-expired", default = "yes"},
|
||||||
|
{key = "response_mode", config_key = "response-mode", prefix = "-r ", default = "first-ping"},
|
||||||
|
{key = "rr_ttl", config_key = "rr-ttl", prefix = "-rr-ttl "},
|
||||||
|
{key = "rr_ttl_min", config_key = "rr-ttl-min", prefix = "-rr-ttl-min "},
|
||||||
|
{key = "rr_ttl_max", config_key = "rr-ttl-max", prefix = "-rr-ttl-max "}
|
||||||
|
}
|
||||||
|
-- 从 custom.conf 中读取值,以最后出现的值为准
|
||||||
|
local custom_config = {}
|
||||||
|
local f_in = io.open(custom_conf_path, "r")
|
||||||
|
if f_in then
|
||||||
|
for line in f_in:lines() do
|
||||||
|
line = line:match("^%s*(.-)%s*$")
|
||||||
|
if line ~= "" and not line:match("^#") then
|
||||||
|
local param, value = line:match("^(%S+)%s+(%S+)$")
|
||||||
|
if param and value then custom_config[param] = value end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
f_in:close()
|
||||||
|
end
|
||||||
|
-- 从 smartdns 配置中读取值,优先级以 custom.conf 为准
|
||||||
|
for _, opt in ipairs(options) do
|
||||||
|
local val = custom_config[opt.config_key] or uci:get("smartdns", "@smartdns[0]", opt.key) or opt.default
|
||||||
|
if val == "yes" then val = "1" elseif val == "no" then val = "0" end
|
||||||
|
if opt.yes_no then
|
||||||
|
local arg = (val == "1" and opt.arg_yes or opt.arg_no)
|
||||||
|
if arg and arg ~= "" then
|
||||||
|
LOCAL_EXTEND_ARG = LOCAL_EXTEND_ARG .. (LOCAL_EXTEND_ARG ~= "" and " " or "") .. arg
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if val and (not opt.value or (opt.invert and val ~= opt.value) or (not opt.invert and val == opt.value)) then
|
||||||
|
LOCAL_EXTEND_ARG = LOCAL_EXTEND_ARG .. (LOCAL_EXTEND_ARG ~= "" and " " or "") .. (opt.prefix or "") .. (opt.arg or val)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if not REMOTE_GROUP or REMOTE_GROUP == "nil" then
|
if not REMOTE_GROUP or REMOTE_GROUP == "nil" then
|
||||||
@@ -167,6 +208,8 @@ if DEFAULT_DNS_GROUP then
|
|||||||
if NO_PROXY_IPV6 == "1" and only_global == 1 and uci:get(appname, TCP_NODE, "protocol") ~= "_shunt" then
|
if NO_PROXY_IPV6 == "1" and only_global == 1 and uci:get(appname, TCP_NODE, "protocol") ~= "_shunt" then
|
||||||
domain_rules_str = domain_rules_str .. " -address #6"
|
domain_rules_str = domain_rules_str .. " -address #6"
|
||||||
end
|
end
|
||||||
|
elseif DEFAULT_DNS_GROUP == LOCAL_GROUP then
|
||||||
|
domain_rules_str = domain_rules_str .. (LOCAL_EXTEND_ARG ~= "" and " " .. LOCAL_EXTEND_ARG or "")
|
||||||
end
|
end
|
||||||
table.insert(config_lines, domain_rules_str)
|
table.insert(config_lines, domain_rules_str)
|
||||||
end
|
end
|
||||||
@@ -226,6 +269,7 @@ if is_file_nonzero(file_vpslist) then
|
|||||||
}
|
}
|
||||||
local domain_rules_str = string.format('domain-rules /domain-set:%s/ %s', domain_set_name, LOCAL_GROUP and "-nameserver " .. LOCAL_GROUP or "")
|
local domain_rules_str = string.format('domain-rules /domain-set:%s/ %s', domain_set_name, LOCAL_GROUP and "-nameserver " .. LOCAL_GROUP or "")
|
||||||
domain_rules_str = domain_rules_str .. " " .. set_type .. " #4:" .. setflag .. "passwall_vpslist,#6:" .. setflag .. "passwall_vpslist6"
|
domain_rules_str = domain_rules_str .. " " .. set_type .. " #4:" .. setflag .. "passwall_vpslist,#6:" .. setflag .. "passwall_vpslist6"
|
||||||
|
domain_rules_str = domain_rules_str .. (LOCAL_EXTEND_ARG ~= "" and " " .. LOCAL_EXTEND_ARG or "")
|
||||||
table.insert(tmp_lines, domain_rules_str)
|
table.insert(tmp_lines, domain_rules_str)
|
||||||
insert_array_after(config_lines, tmp_lines, "#--8")
|
insert_array_after(config_lines, tmp_lines, "#--8")
|
||||||
log(string.format(" - 节点列表中的域名(vpslist)使用分组:%s", LOCAL_GROUP or "默认"))
|
log(string.format(" - 节点列表中的域名(vpslist)使用分组:%s", LOCAL_GROUP or "默认"))
|
||||||
@@ -256,6 +300,7 @@ if USE_DIRECT_LIST == "1" and is_file_nonzero(file_direct_host) then
|
|||||||
}
|
}
|
||||||
local domain_rules_str = string.format('domain-rules /domain-set:%s/ %s', domain_set_name, LOCAL_GROUP and "-nameserver " .. LOCAL_GROUP or "")
|
local domain_rules_str = string.format('domain-rules /domain-set:%s/ %s', domain_set_name, LOCAL_GROUP and "-nameserver " .. LOCAL_GROUP or "")
|
||||||
domain_rules_str = domain_rules_str .. " " .. set_type .. " #4:" .. setflag .. "passwall_whitelist,#6:" .. setflag .. "passwall_whitelist6"
|
domain_rules_str = domain_rules_str .. " " .. set_type .. " #4:" .. setflag .. "passwall_whitelist,#6:" .. setflag .. "passwall_whitelist6"
|
||||||
|
domain_rules_str = domain_rules_str .. (LOCAL_EXTEND_ARG ~= "" and " " .. LOCAL_EXTEND_ARG or "")
|
||||||
table.insert(tmp_lines, domain_rules_str)
|
table.insert(tmp_lines, domain_rules_str)
|
||||||
insert_array_after(config_lines, tmp_lines, "#--6")
|
insert_array_after(config_lines, tmp_lines, "#--6")
|
||||||
log(string.format(" - 域名白名单(whitelist)使用分组:%s", LOCAL_GROUP or "默认"))
|
log(string.format(" - 域名白名单(whitelist)使用分组:%s", LOCAL_GROUP or "默认"))
|
||||||
@@ -328,6 +373,7 @@ if CHN_LIST ~= "0" and is_file_nonzero(RULES_PATH .. "/chnlist") then
|
|||||||
if CHN_LIST == "direct" then
|
if CHN_LIST == "direct" then
|
||||||
local domain_rules_str = string.format('domain-rules /domain-set:%s/ %s', domain_set_name, LOCAL_GROUP and "-nameserver " .. LOCAL_GROUP or "")
|
local domain_rules_str = string.format('domain-rules /domain-set:%s/ %s', domain_set_name, LOCAL_GROUP and "-nameserver " .. LOCAL_GROUP or "")
|
||||||
domain_rules_str = domain_rules_str .. " " .. set_type .. " #4:" .. setflag .. "passwall_chnroute,#6:" .. setflag .. "passwall_chnroute6"
|
domain_rules_str = domain_rules_str .. " " .. set_type .. " #4:" .. setflag .. "passwall_chnroute,#6:" .. setflag .. "passwall_chnroute6"
|
||||||
|
domain_rules_str = domain_rules_str .. (LOCAL_EXTEND_ARG ~= "" and " " .. LOCAL_EXTEND_ARG or "")
|
||||||
table.insert(tmp_lines, domain_rules_str)
|
table.insert(tmp_lines, domain_rules_str)
|
||||||
insert_array_after(config_lines, tmp_lines, "#--2")
|
insert_array_after(config_lines, tmp_lines, "#--2")
|
||||||
log(string.format(" - 中国域名表(chnroute)使用分组:%s", LOCAL_GROUP or "默认"))
|
log(string.format(" - 中国域名表(chnroute)使用分组:%s", LOCAL_GROUP or "默认"))
|
||||||
@@ -419,6 +465,7 @@ if uci:get(appname, TCP_NODE, "protocol") == "_shunt" then
|
|||||||
}
|
}
|
||||||
local domain_rules_str = string.format('domain-rules /domain-set:%s/ %s', domain_set_name, LOCAL_GROUP and "-nameserver " .. LOCAL_GROUP or "")
|
local domain_rules_str = string.format('domain-rules /domain-set:%s/ %s', domain_set_name, LOCAL_GROUP and "-nameserver " .. LOCAL_GROUP or "")
|
||||||
domain_rules_str = domain_rules_str .. " " .. set_type .. " #4:" .. setflag .. "passwall_whitelist,#6:" .. setflag .. "passwall_whitelist6"
|
domain_rules_str = domain_rules_str .. " " .. set_type .. " #4:" .. setflag .. "passwall_whitelist,#6:" .. setflag .. "passwall_whitelist6"
|
||||||
|
domain_rules_str = domain_rules_str .. (LOCAL_EXTEND_ARG ~= "" and " " .. LOCAL_EXTEND_ARG or "")
|
||||||
table.insert(tmp_lines, domain_rules_str)
|
table.insert(tmp_lines, domain_rules_str)
|
||||||
insert_array_after(config_lines, tmp_lines, "#--3")
|
insert_array_after(config_lines, tmp_lines, "#--3")
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -70,10 +70,12 @@ type InboundContext struct {
|
|||||||
InboundOptions option.InboundOptions
|
InboundOptions option.InboundOptions
|
||||||
UDPDisableDomainUnmapping bool
|
UDPDisableDomainUnmapping bool
|
||||||
UDPConnect bool
|
UDPConnect bool
|
||||||
NetworkStrategy C.NetworkStrategy
|
UDPTimeout time.Duration
|
||||||
NetworkType []C.InterfaceType
|
|
||||||
FallbackNetworkType []C.InterfaceType
|
NetworkStrategy C.NetworkStrategy
|
||||||
FallbackDelay time.Duration
|
NetworkType []C.InterfaceType
|
||||||
|
FallbackNetworkType []C.InterfaceType
|
||||||
|
FallbackDelay time.Duration
|
||||||
|
|
||||||
DNSServer string
|
DNSServer string
|
||||||
|
|
||||||
|
|||||||
@@ -1,157 +0,0 @@
|
|||||||
package outbound
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net"
|
|
||||||
"net/netip"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
|
||||||
"github.com/sagernet/sing-box/common/dialer"
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
|
||||||
"github.com/sagernet/sing/common"
|
|
||||||
"github.com/sagernet/sing/common/buf"
|
|
||||||
"github.com/sagernet/sing/common/bufio"
|
|
||||||
"github.com/sagernet/sing/common/canceler"
|
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
|
||||||
N "github.com/sagernet/sing/common/network"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewConnection(ctx context.Context, this N.Dialer, conn net.Conn, metadata adapter.InboundContext) error {
|
|
||||||
defer conn.Close()
|
|
||||||
ctx = adapter.WithContext(ctx, &metadata)
|
|
||||||
var outConn net.Conn
|
|
||||||
var err error
|
|
||||||
if len(metadata.DestinationAddresses) > 0 {
|
|
||||||
outConn, err = dialer.DialSerialNetwork(ctx, this, N.NetworkTCP, metadata.Destination, metadata.DestinationAddresses, metadata.NetworkStrategy, metadata.NetworkType, metadata.FallbackNetworkType, metadata.FallbackDelay)
|
|
||||||
} else {
|
|
||||||
outConn, err = this.DialContext(ctx, N.NetworkTCP, metadata.Destination)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return N.ReportHandshakeFailure(conn, err)
|
|
||||||
}
|
|
||||||
err = N.ReportConnHandshakeSuccess(conn, outConn)
|
|
||||||
if err != nil {
|
|
||||||
outConn.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return CopyEarlyConn(ctx, conn, outConn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPacketConnection(ctx context.Context, this N.Dialer, conn N.PacketConn, metadata adapter.InboundContext) error {
|
|
||||||
defer conn.Close()
|
|
||||||
ctx = adapter.WithContext(ctx, &metadata)
|
|
||||||
var (
|
|
||||||
outPacketConn net.PacketConn
|
|
||||||
outConn net.Conn
|
|
||||||
destinationAddress netip.Addr
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
if metadata.UDPConnect {
|
|
||||||
if len(metadata.DestinationAddresses) > 0 {
|
|
||||||
if parallelDialer, isParallelDialer := this.(dialer.ParallelInterfaceDialer); isParallelDialer {
|
|
||||||
outConn, err = dialer.DialSerialNetwork(ctx, parallelDialer, N.NetworkUDP, metadata.Destination, metadata.DestinationAddresses, metadata.NetworkStrategy, metadata.NetworkType, metadata.FallbackNetworkType, metadata.FallbackDelay)
|
|
||||||
} else {
|
|
||||||
outConn, err = N.DialSerial(ctx, this, N.NetworkUDP, metadata.Destination, metadata.DestinationAddresses)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
outConn, err = this.DialContext(ctx, N.NetworkUDP, metadata.Destination)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return N.ReportHandshakeFailure(conn, err)
|
|
||||||
}
|
|
||||||
outPacketConn = bufio.NewUnbindPacketConn(outConn)
|
|
||||||
connRemoteAddr := M.AddrFromNet(outConn.RemoteAddr())
|
|
||||||
if connRemoteAddr != metadata.Destination.Addr {
|
|
||||||
destinationAddress = connRemoteAddr
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if len(metadata.DestinationAddresses) > 0 {
|
|
||||||
outPacketConn, destinationAddress, err = dialer.ListenSerialNetworkPacket(ctx, this, metadata.Destination, metadata.DestinationAddresses, metadata.NetworkStrategy, metadata.NetworkType, metadata.FallbackNetworkType, metadata.FallbackDelay)
|
|
||||||
} else {
|
|
||||||
outPacketConn, err = this.ListenPacket(ctx, metadata.Destination)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return N.ReportHandshakeFailure(conn, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = N.ReportPacketConnHandshakeSuccess(conn, outPacketConn)
|
|
||||||
if err != nil {
|
|
||||||
outPacketConn.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if destinationAddress.IsValid() {
|
|
||||||
var originDestination M.Socksaddr
|
|
||||||
if metadata.RouteOriginalDestination.IsValid() {
|
|
||||||
originDestination = metadata.RouteOriginalDestination
|
|
||||||
} else {
|
|
||||||
originDestination = metadata.Destination
|
|
||||||
}
|
|
||||||
if metadata.Destination != M.SocksaddrFrom(destinationAddress, metadata.Destination.Port) {
|
|
||||||
if metadata.UDPDisableDomainUnmapping {
|
|
||||||
outPacketConn = bufio.NewUnidirectionalNATPacketConn(bufio.NewPacketConn(outPacketConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), originDestination)
|
|
||||||
} else {
|
|
||||||
outPacketConn = bufio.NewNATPacketConn(bufio.NewPacketConn(outPacketConn), M.SocksaddrFrom(destinationAddress, metadata.Destination.Port), originDestination)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if natConn, loaded := common.Cast[bufio.NATPacketConn](conn); loaded {
|
|
||||||
natConn.UpdateDestination(destinationAddress)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch metadata.Protocol {
|
|
||||||
case C.ProtocolSTUN:
|
|
||||||
ctx, conn = canceler.NewPacketConn(ctx, conn, C.STUNTimeout)
|
|
||||||
case C.ProtocolQUIC:
|
|
||||||
ctx, conn = canceler.NewPacketConn(ctx, conn, C.QUICTimeout)
|
|
||||||
case C.ProtocolDNS:
|
|
||||||
ctx, conn = canceler.NewPacketConn(ctx, conn, C.DNSTimeout)
|
|
||||||
}
|
|
||||||
return bufio.CopyPacketConn(ctx, conn, bufio.NewPacketConn(outPacketConn))
|
|
||||||
}
|
|
||||||
|
|
||||||
func CopyEarlyConn(ctx context.Context, conn net.Conn, serverConn net.Conn) error {
|
|
||||||
if cachedReader, isCached := conn.(N.CachedReader); isCached {
|
|
||||||
payload := cachedReader.ReadCached()
|
|
||||||
if payload != nil && !payload.IsEmpty() {
|
|
||||||
_, err := serverConn.Write(payload.Bytes())
|
|
||||||
payload.Release()
|
|
||||||
if err != nil {
|
|
||||||
serverConn.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return bufio.CopyConn(ctx, conn, serverConn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if earlyConn, isEarlyConn := common.Cast[N.EarlyConn](serverConn); isEarlyConn && earlyConn.NeedHandshake() {
|
|
||||||
payload := buf.NewPacket()
|
|
||||||
err := conn.SetReadDeadline(time.Now().Add(C.ReadPayloadTimeout))
|
|
||||||
if err != os.ErrInvalid {
|
|
||||||
if err != nil {
|
|
||||||
payload.Release()
|
|
||||||
serverConn.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = payload.ReadOnceFrom(conn)
|
|
||||||
if err != nil && !E.IsTimeout(err) {
|
|
||||||
payload.Release()
|
|
||||||
serverConn.Close()
|
|
||||||
return E.Cause(err, "read payload")
|
|
||||||
}
|
|
||||||
err = conn.SetReadDeadline(time.Time{})
|
|
||||||
if err != nil {
|
|
||||||
payload.Release()
|
|
||||||
serverConn.Close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_, err = serverConn.Write(payload.Bytes())
|
|
||||||
payload.Release()
|
|
||||||
if err != nil {
|
|
||||||
serverConn.Close()
|
|
||||||
return N.ReportHandshakeFailure(conn, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bufio.CopyConn(ctx, conn, serverConn)
|
|
||||||
}
|
|
||||||
@@ -10,6 +10,7 @@ const (
|
|||||||
ProtocolDTLS = "dtls"
|
ProtocolDTLS = "dtls"
|
||||||
ProtocolSSH = "ssh"
|
ProtocolSSH = "ssh"
|
||||||
ProtocolRDP = "rdp"
|
ProtocolRDP = "rdp"
|
||||||
|
ProtocolNTP = "ntp"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ const (
|
|||||||
TCPTimeout = 15 * time.Second
|
TCPTimeout = 15 * time.Second
|
||||||
ReadPayloadTimeout = 300 * time.Millisecond
|
ReadPayloadTimeout = 300 * time.Millisecond
|
||||||
DNSTimeout = 10 * time.Second
|
DNSTimeout = 10 * time.Second
|
||||||
QUICTimeout = 30 * time.Second
|
|
||||||
STUNTimeout = 15 * time.Second
|
|
||||||
UDPTimeout = 5 * time.Minute
|
UDPTimeout = 5 * time.Minute
|
||||||
DefaultURLTestInterval = 3 * time.Minute
|
DefaultURLTestInterval = 3 * time.Minute
|
||||||
DefaultURLTestIdleTimeout = 30 * time.Minute
|
DefaultURLTestIdleTimeout = 30 * time.Minute
|
||||||
@@ -19,3 +17,18 @@ const (
|
|||||||
FatalStopTimeout = 10 * time.Second
|
FatalStopTimeout = 10 * time.Second
|
||||||
FakeIPMetadataSaveInterval = 10 * time.Second
|
FakeIPMetadataSaveInterval = 10 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var PortProtocols = map[uint16]string{
|
||||||
|
53: ProtocolDNS,
|
||||||
|
123: ProtocolNTP,
|
||||||
|
3478: ProtocolSTUN,
|
||||||
|
443: ProtocolQUIC,
|
||||||
|
}
|
||||||
|
|
||||||
|
var ProtocolTimeouts = map[string]time.Duration{
|
||||||
|
ProtocolDNS: 10 * time.Second,
|
||||||
|
ProtocolNTP: 10 * time.Second,
|
||||||
|
ProtocolSTUN: 10 * time.Second,
|
||||||
|
ProtocolQUIC: 30 * time.Second,
|
||||||
|
ProtocolDTLS: 30 * time.Second,
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,19 @@
|
|||||||
icon: material/alert-decagram
|
icon: material/alert-decagram
|
||||||
---
|
---
|
||||||
|
|
||||||
|
#### 1.11.0-alpha.23
|
||||||
|
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
#### 1.11.0-alpha.22
|
||||||
|
|
||||||
|
* Add UDP timeout route option **1**
|
||||||
|
* Fixes and improvements
|
||||||
|
|
||||||
|
**1**:
|
||||||
|
|
||||||
|
See [Rule Action](/configuration/route/rule_action/#udp_timeout).
|
||||||
|
|
||||||
#### 1.11.0-alpha.20
|
#### 1.11.0-alpha.20
|
||||||
|
|
||||||
* Add UDP GSO support for WireGuard
|
* Add UDP GSO support for WireGuard
|
||||||
|
|||||||
@@ -41,7 +41,8 @@ See `route-options` fields below.
|
|||||||
"network_strategy": "",
|
"network_strategy": "",
|
||||||
"fallback_delay": "",
|
"fallback_delay": "",
|
||||||
"udp_disable_domain_unmapping": false,
|
"udp_disable_domain_unmapping": false,
|
||||||
"udp_connect": false
|
"udp_connect": false,
|
||||||
|
"udp_timeout": ""
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -86,6 +87,28 @@ do not support receiving UDP packets with domain addresses, such as Surge.
|
|||||||
|
|
||||||
If enabled, attempts to connect UDP connection to the destination instead of listen.
|
If enabled, attempts to connect UDP connection to the destination instead of listen.
|
||||||
|
|
||||||
|
#### udp_timeout
|
||||||
|
|
||||||
|
Timeout for UDP connections.
|
||||||
|
|
||||||
|
Setting a larger value than the UDP timeout in inbounds will have no effect.
|
||||||
|
|
||||||
|
Default value for protocol sniffed connections:
|
||||||
|
|
||||||
|
| Timeout | Protocol |
|
||||||
|
|---------|----------------------|
|
||||||
|
| `10s` | `dns`, `ntp`, `stun` |
|
||||||
|
| `30s` | `quic`, `dtls` |
|
||||||
|
|
||||||
|
If no protocol is sniffed, the following ports will be recognized as protocols by default:
|
||||||
|
|
||||||
|
| Port | Protocol |
|
||||||
|
|------|----------|
|
||||||
|
| 53 | `dns` |
|
||||||
|
| 123 | `ntp` |
|
||||||
|
| 443 | `quic` |
|
||||||
|
| 3478 | `stun` |
|
||||||
|
|
||||||
### reject
|
### reject
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ icon: material/new-box
|
|||||||
"network_strategy": "",
|
"network_strategy": "",
|
||||||
"fallback_delay": "",
|
"fallback_delay": "",
|
||||||
"udp_disable_domain_unmapping": false,
|
"udp_disable_domain_unmapping": false,
|
||||||
"udp_connect": false
|
"udp_connect": false,
|
||||||
|
"udp_timeout": ""
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -84,6 +85,28 @@ icon: material/new-box
|
|||||||
|
|
||||||
如果启用,将尝试将 UDP 连接 connect 到目标而不是 listen。
|
如果启用,将尝试将 UDP 连接 connect 到目标而不是 listen。
|
||||||
|
|
||||||
|
#### udp_timeout
|
||||||
|
|
||||||
|
UDP 连接超时时间。
|
||||||
|
|
||||||
|
设置比入站 UDP 超时更大的值将无效。
|
||||||
|
|
||||||
|
已探测协议连接的默认值:
|
||||||
|
|
||||||
|
| 超时 | 协议 |
|
||||||
|
|-------|----------------------|
|
||||||
|
| `10s` | `dns`, `ntp`, `stun` |
|
||||||
|
| `30s` | `quic`, `dtls` |
|
||||||
|
|
||||||
|
如果没有探测到协议,以下端口将默认识别为协议:
|
||||||
|
|
||||||
|
| 端口 | 协议 |
|
||||||
|
|------|--------|
|
||||||
|
| 53 | `dns` |
|
||||||
|
| 123 | `ntp` |
|
||||||
|
| 443 | `quic` |
|
||||||
|
| 3478 | `stun` |
|
||||||
|
|
||||||
### reject
|
### reject
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
|||||||
+1
-1
@@ -25,7 +25,7 @@ require (
|
|||||||
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff
|
github.com/sagernet/gvisor v0.0.0-20241123041152-536d05261cff
|
||||||
github.com/sagernet/quic-go v0.48.1-beta.1
|
github.com/sagernet/quic-go v0.48.1-beta.1
|
||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
|
||||||
github.com/sagernet/sing v0.6.0-alpha.18
|
github.com/sagernet/sing v0.6.0-alpha.20
|
||||||
github.com/sagernet/sing-dns v0.4.0-alpha.3
|
github.com/sagernet/sing-dns v0.4.0-alpha.3
|
||||||
github.com/sagernet/sing-mux v0.3.0-alpha.1
|
github.com/sagernet/sing-mux v0.3.0-alpha.1
|
||||||
github.com/sagernet/sing-quic v0.4.0-alpha.4
|
github.com/sagernet/sing-quic v0.4.0-alpha.4
|
||||||
|
|||||||
+2
-2
@@ -110,8 +110,8 @@ github.com/sagernet/quic-go v0.48.1-beta.1/go.mod h1:1WgdDIVD1Gybp40JTWketeSfKA/
|
|||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
|
||||||
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
|
||||||
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
||||||
github.com/sagernet/sing v0.6.0-alpha.18 h1:ih4CurU8KvbhfagYjSqVrE2LR0oBSXSZTNH2sAGPGiM=
|
github.com/sagernet/sing v0.6.0-alpha.20 h1:coxvnzeEGSLNNPntUW7l8WUEHPIwqKszZNbU019To9c=
|
||||||
github.com/sagernet/sing v0.6.0-alpha.18/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
github.com/sagernet/sing v0.6.0-alpha.20/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||||
github.com/sagernet/sing-dns v0.4.0-alpha.3 h1:TcAQdz68Gs28VD9o9zDIW7IS8A9LZDruTPI9g9JbGHA=
|
github.com/sagernet/sing-dns v0.4.0-alpha.3 h1:TcAQdz68Gs28VD9o9zDIW7IS8A9LZDruTPI9g9JbGHA=
|
||||||
github.com/sagernet/sing-dns v0.4.0-alpha.3/go.mod h1:9LHcYKg2bGQpbtXrfNbopz8ok/zBK9ljiI2kmFG9JKg=
|
github.com/sagernet/sing-dns v0.4.0-alpha.3/go.mod h1:9LHcYKg2bGQpbtXrfNbopz8ok/zBK9ljiI2kmFG9JKg=
|
||||||
github.com/sagernet/sing-mux v0.3.0-alpha.1 h1:IgNX5bJBpL41gGbp05pdDOvh/b5eUQ6cv9240+Ngipg=
|
github.com/sagernet/sing-mux v0.3.0-alpha.1 h1:IgNX5bJBpL41gGbp05pdDOvh/b5eUQ6cv9240+Ngipg=
|
||||||
|
|||||||
@@ -148,8 +148,9 @@ type RawRouteOptionsActionOptions struct {
|
|||||||
NetworkStrategy NetworkStrategy `json:"network_strategy,omitempty"`
|
NetworkStrategy NetworkStrategy `json:"network_strategy,omitempty"`
|
||||||
FallbackDelay uint32 `json:"fallback_delay,omitempty"`
|
FallbackDelay uint32 `json:"fallback_delay,omitempty"`
|
||||||
|
|
||||||
UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"`
|
UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"`
|
||||||
UDPConnect bool `json:"udp_connect,omitempty"`
|
UDPConnect bool `json:"udp_connect,omitempty"`
|
||||||
|
UDPTimeout badoption.Duration `json:"udp_timeout,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RouteOptionsActionOptions RawRouteOptionsActionOptions
|
type RouteOptionsActionOptions RawRouteOptionsActionOptions
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ type WireGuardEndpointOptions struct {
|
|||||||
PrivateKey string `json:"private_key"`
|
PrivateKey string `json:"private_key"`
|
||||||
ListenPort uint16 `json:"listen_port,omitempty"`
|
ListenPort uint16 `json:"listen_port,omitempty"`
|
||||||
Peers []WireGuardPeer `json:"peers,omitempty"`
|
Peers []WireGuardPeer `json:"peers,omitempty"`
|
||||||
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
|
UDPTimeout badoption.Duration `json:"udp_timeout,omitempty"`
|
||||||
Workers int `json:"workers,omitempty"`
|
Workers int `json:"workers,omitempty"`
|
||||||
DialerOptions
|
DialerOptions
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"github.com/sagernet/sing/common/atomic"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
@@ -21,17 +22,22 @@ func RegisterSelector(registry *outbound.Registry) {
|
|||||||
outbound.Register[option.SelectorOutboundOptions](registry, C.TypeSelector, NewSelector)
|
outbound.Register[option.SelectorOutboundOptions](registry, C.TypeSelector, NewSelector)
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ adapter.OutboundGroup = (*Selector)(nil)
|
var (
|
||||||
|
_ adapter.OutboundGroup = (*Selector)(nil)
|
||||||
|
_ adapter.ConnectionHandlerEx = (*Selector)(nil)
|
||||||
|
_ adapter.PacketConnectionHandlerEx = (*Selector)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
type Selector struct {
|
type Selector struct {
|
||||||
outbound.Adapter
|
outbound.Adapter
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
outboundManager adapter.OutboundManager
|
outbound adapter.OutboundManager
|
||||||
|
connection adapter.ConnectionManager
|
||||||
logger logger.ContextLogger
|
logger logger.ContextLogger
|
||||||
tags []string
|
tags []string
|
||||||
defaultTag string
|
defaultTag string
|
||||||
outbounds map[string]adapter.Outbound
|
outbounds map[string]adapter.Outbound
|
||||||
selected adapter.Outbound
|
selected atomic.TypedValue[adapter.Outbound]
|
||||||
interruptGroup *interrupt.Group
|
interruptGroup *interrupt.Group
|
||||||
interruptExternalConnections bool
|
interruptExternalConnections bool
|
||||||
}
|
}
|
||||||
@@ -40,7 +46,8 @@ func NewSelector(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
outbound := &Selector{
|
outbound := &Selector{
|
||||||
Adapter: outbound.NewAdapter(C.TypeSelector, tag, nil, options.Outbounds),
|
Adapter: outbound.NewAdapter(C.TypeSelector, tag, nil, options.Outbounds),
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
outboundManager: service.FromContext[adapter.OutboundManager](ctx),
|
outbound: service.FromContext[adapter.OutboundManager](ctx),
|
||||||
|
connection: service.FromContext[adapter.ConnectionManager](ctx),
|
||||||
logger: logger,
|
logger: logger,
|
||||||
tags: options.Outbounds,
|
tags: options.Outbounds,
|
||||||
defaultTag: options.Default,
|
defaultTag: options.Default,
|
||||||
@@ -55,15 +62,16 @@ func NewSelector(ctx context.Context, router adapter.Router, logger log.ContextL
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Selector) Network() []string {
|
func (s *Selector) Network() []string {
|
||||||
if s.selected == nil {
|
selected := s.selected.Load()
|
||||||
|
if selected == nil {
|
||||||
return []string{N.NetworkTCP, N.NetworkUDP}
|
return []string{N.NetworkTCP, N.NetworkUDP}
|
||||||
}
|
}
|
||||||
return s.selected.Network()
|
return selected.Network()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Selector) Start() error {
|
func (s *Selector) Start() error {
|
||||||
for i, tag := range s.tags {
|
for i, tag := range s.tags {
|
||||||
detour, loaded := s.outboundManager.Outbound(tag)
|
detour, loaded := s.outbound.Outbound(tag)
|
||||||
if !loaded {
|
if !loaded {
|
||||||
return E.New("outbound ", i, " not found: ", tag)
|
return E.New("outbound ", i, " not found: ", tag)
|
||||||
}
|
}
|
||||||
@@ -77,7 +85,7 @@ func (s *Selector) Start() error {
|
|||||||
if selected != "" {
|
if selected != "" {
|
||||||
detour, loaded := s.outbounds[selected]
|
detour, loaded := s.outbounds[selected]
|
||||||
if loaded {
|
if loaded {
|
||||||
s.selected = detour
|
s.selected.Store(detour)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,16 +97,16 @@ func (s *Selector) Start() error {
|
|||||||
if !loaded {
|
if !loaded {
|
||||||
return E.New("default outbound not found: ", s.defaultTag)
|
return E.New("default outbound not found: ", s.defaultTag)
|
||||||
}
|
}
|
||||||
s.selected = detour
|
s.selected.Store(detour)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
s.selected = s.outbounds[s.tags[0]]
|
s.selected.Store(s.outbounds[s.tags[0]])
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Selector) Now() string {
|
func (s *Selector) Now() string {
|
||||||
selected := s.selected
|
selected := s.selected.Load()
|
||||||
if selected == nil {
|
if selected == nil {
|
||||||
return s.tags[0]
|
return s.tags[0]
|
||||||
}
|
}
|
||||||
@@ -114,10 +122,9 @@ func (s *Selector) SelectOutbound(tag string) bool {
|
|||||||
if !loaded {
|
if !loaded {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if s.selected == detour {
|
if s.selected.Swap(detour) == detour {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
s.selected = detour
|
|
||||||
if s.Tag() != "" {
|
if s.Tag() != "" {
|
||||||
cacheFile := service.FromContext[adapter.CacheFile](s.ctx)
|
cacheFile := service.FromContext[adapter.CacheFile](s.ctx)
|
||||||
if cacheFile != nil {
|
if cacheFile != nil {
|
||||||
@@ -132,7 +139,7 @@ func (s *Selector) SelectOutbound(tag string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Selector) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
func (s *Selector) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
conn, err := s.selected.DialContext(ctx, network, destination)
|
conn, err := s.selected.Load().DialContext(ctx, network, destination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -140,32 +147,30 @@ func (s *Selector) DialContext(ctx context.Context, network string, destination
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Selector) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
func (s *Selector) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
conn, err := s.selected.ListenPacket(ctx, destination)
|
conn, err := s.selected.Load().ListenPacket(ctx, destination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return s.interruptGroup.NewPacketConn(conn, interrupt.IsExternalConnectionFromContext(ctx)), nil
|
return s.interruptGroup.NewPacketConn(conn, interrupt.IsExternalConnectionFromContext(ctx)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
func (s *Selector) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
// Deprecated
|
|
||||||
func (s *Selector) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
|
||||||
ctx = interrupt.ContextWithIsExternalConnection(ctx)
|
ctx = interrupt.ContextWithIsExternalConnection(ctx)
|
||||||
if legacyHandler, ok := s.selected.(adapter.ConnectionHandler); ok {
|
selected := s.selected.Load()
|
||||||
return legacyHandler.NewConnection(ctx, conn, metadata)
|
if outboundHandler, isHandler := selected.(adapter.ConnectionHandlerEx); isHandler {
|
||||||
|
outboundHandler.NewConnectionEx(ctx, conn, metadata, onClose)
|
||||||
} else {
|
} else {
|
||||||
return outbound.NewConnection(ctx, s.selected, conn, metadata)
|
s.connection.NewConnection(ctx, selected, conn, metadata, onClose)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
func (s *Selector) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
// Deprecated
|
|
||||||
func (s *Selector) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
|
||||||
ctx = interrupt.ContextWithIsExternalConnection(ctx)
|
ctx = interrupt.ContextWithIsExternalConnection(ctx)
|
||||||
if legacyHandler, ok := s.selected.(adapter.PacketConnectionHandler); ok {
|
selected := s.selected.Load()
|
||||||
return legacyHandler.NewPacketConnection(ctx, conn, metadata)
|
if outboundHandler, isHandler := selected.(adapter.PacketConnectionHandlerEx); isHandler {
|
||||||
|
outboundHandler.NewPacketConnectionEx(ctx, conn, metadata, onClose)
|
||||||
} else {
|
} else {
|
||||||
return outbound.NewPacketConnection(ctx, s.selected, conn, metadata)
|
s.connection.NewPacketConnection(ctx, selected, conn, metadata, onClose)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ type URLTest struct {
|
|||||||
outbound.Adapter
|
outbound.Adapter
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
router adapter.Router
|
router adapter.Router
|
||||||
outboundManager adapter.OutboundManager
|
outbound adapter.OutboundManager
|
||||||
|
connection adapter.ConnectionManager
|
||||||
logger log.ContextLogger
|
logger log.ContextLogger
|
||||||
tags []string
|
tags []string
|
||||||
link string
|
link string
|
||||||
@@ -52,7 +53,8 @@ func NewURLTest(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
Adapter: outbound.NewAdapter(C.TypeURLTest, tag, []string{N.NetworkTCP, N.NetworkUDP}, options.Outbounds),
|
Adapter: outbound.NewAdapter(C.TypeURLTest, tag, []string{N.NetworkTCP, N.NetworkUDP}, options.Outbounds),
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
router: router,
|
router: router,
|
||||||
outboundManager: service.FromContext[adapter.OutboundManager](ctx),
|
outbound: service.FromContext[adapter.OutboundManager](ctx),
|
||||||
|
connection: service.FromContext[adapter.ConnectionManager](ctx),
|
||||||
logger: logger,
|
logger: logger,
|
||||||
tags: options.Outbounds,
|
tags: options.Outbounds,
|
||||||
link: options.URL,
|
link: options.URL,
|
||||||
@@ -70,13 +72,13 @@ func NewURLTest(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
func (s *URLTest) Start() error {
|
func (s *URLTest) Start() error {
|
||||||
outbounds := make([]adapter.Outbound, 0, len(s.tags))
|
outbounds := make([]adapter.Outbound, 0, len(s.tags))
|
||||||
for i, tag := range s.tags {
|
for i, tag := range s.tags {
|
||||||
detour, loaded := s.outboundManager.Outbound(tag)
|
detour, loaded := s.outbound.Outbound(tag)
|
||||||
if !loaded {
|
if !loaded {
|
||||||
return E.New("outbound ", i, " not found: ", tag)
|
return E.New("outbound ", i, " not found: ", tag)
|
||||||
}
|
}
|
||||||
outbounds = append(outbounds, detour)
|
outbounds = append(outbounds, detour)
|
||||||
}
|
}
|
||||||
group, err := NewURLTestGroup(s.ctx, s.outboundManager, s.logger, outbounds, s.link, s.interval, s.tolerance, s.idleTimeout, s.interruptExternalConnections)
|
group, err := NewURLTestGroup(s.ctx, s.outbound, s.logger, outbounds, s.link, s.interval, s.tolerance, s.idleTimeout, s.interruptExternalConnections)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -160,18 +162,14 @@ func (s *URLTest) ListenPacket(ctx context.Context, destination M.Socksaddr) (ne
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
func (s *URLTest) NewConnectionEx(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
// Deprecated
|
|
||||||
func (s *URLTest) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
|
|
||||||
ctx = interrupt.ContextWithIsExternalConnection(ctx)
|
ctx = interrupt.ContextWithIsExternalConnection(ctx)
|
||||||
return outbound.NewConnection(ctx, s, conn, metadata)
|
s.connection.NewConnection(ctx, s, conn, metadata, onClose)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
func (s *URLTest) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) {
|
||||||
// Deprecated
|
|
||||||
func (s *URLTest) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
|
|
||||||
ctx = interrupt.ContextWithIsExternalConnection(ctx)
|
ctx = interrupt.ContextWithIsExternalConnection(ctx)
|
||||||
return outbound.NewPacketConnection(ctx, s, conn, metadata)
|
s.connection.NewPacketConnection(ctx, s, conn, metadata, onClose)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *URLTest) InterfaceUpdated() {
|
func (s *URLTest) InterfaceUpdated() {
|
||||||
|
|||||||
+42
-29
@@ -6,11 +6,14 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
"github.com/sagernet/sing-box/common/dialer"
|
"github.com/sagernet/sing-box/common/dialer"
|
||||||
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/bufio"
|
"github.com/sagernet/sing/common/bufio"
|
||||||
|
"github.com/sagernet/sing/common/canceler"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
@@ -79,39 +82,34 @@ func (m *ConnectionManager) connectionCopy(ctx context.Context, source io.Reader
|
|||||||
if cachedSrc, isCached := source.(N.CachedReader); isCached {
|
if cachedSrc, isCached := source.(N.CachedReader); isCached {
|
||||||
cachedBuffer := cachedSrc.ReadCached()
|
cachedBuffer := cachedSrc.ReadCached()
|
||||||
if cachedBuffer != nil {
|
if cachedBuffer != nil {
|
||||||
if !cachedBuffer.IsEmpty() {
|
dataLen := cachedBuffer.Len()
|
||||||
dataLen := cachedBuffer.Len()
|
_, err := destination.Write(cachedBuffer.Bytes())
|
||||||
for _, counter := range readCounters {
|
|
||||||
counter(int64(dataLen))
|
|
||||||
}
|
|
||||||
_, err := destination.Write(cachedBuffer.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
m.logger.ErrorContext(ctx, "connection upload payload: ", err)
|
|
||||||
cachedBuffer.Release()
|
|
||||||
if done.Swap(true) {
|
|
||||||
if onClose != nil {
|
|
||||||
onClose(err)
|
|
||||||
}
|
|
||||||
common.Close(source, destination)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, counter := range writeCounters {
|
|
||||||
counter(int64(dataLen))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cachedBuffer.Release()
|
cachedBuffer.Release()
|
||||||
continue
|
if err != nil {
|
||||||
|
m.logger.ErrorContext(ctx, "connection upload payload: ", err)
|
||||||
|
if done.Swap(true) {
|
||||||
|
if onClose != nil {
|
||||||
|
onClose(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
common.Close(source, destination)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, counter := range readCounters {
|
||||||
|
counter(int64(dataLen))
|
||||||
|
}
|
||||||
|
for _, counter := range writeCounters {
|
||||||
|
counter(int64(dataLen))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
var (
|
_, err := bufio.CopyWithCounters(destination, source, originSource, readCounters, writeCounters)
|
||||||
dstDuplex bool
|
if err != nil {
|
||||||
err error
|
common.Close(destination, source)
|
||||||
)
|
} else if _, dstDuplex := destination.(N.WriteCloser); dstDuplex {
|
||||||
_, err = bufio.CopyWithCounters(destination, source, originSource, readCounters, writeCounters)
|
|
||||||
if _, dstDuplex = common.Cast[N.WriteCloser](destination); dstDuplex && err == nil {
|
|
||||||
N.CloseWrite(destination)
|
N.CloseWrite(destination)
|
||||||
} else {
|
} else {
|
||||||
common.Close(destination)
|
common.Close(destination)
|
||||||
@@ -206,6 +204,21 @@ func (m *ConnectionManager) NewPacketConnection(ctx context.Context, this N.Dial
|
|||||||
natConn.UpdateDestination(destinationAddress)
|
natConn.UpdateDestination(destinationAddress)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var udpTimeout time.Duration
|
||||||
|
if metadata.UDPTimeout > 0 {
|
||||||
|
udpTimeout = metadata.UDPTimeout
|
||||||
|
} else {
|
||||||
|
protocol := metadata.Protocol
|
||||||
|
if protocol == "" {
|
||||||
|
protocol = C.PortProtocols[metadata.Destination.Port]
|
||||||
|
}
|
||||||
|
if protocol != "" {
|
||||||
|
udpTimeout = C.ProtocolTimeouts[protocol]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if udpTimeout > 0 {
|
||||||
|
ctx, conn = canceler.NewPacketConn(ctx, conn, udpTimeout)
|
||||||
|
}
|
||||||
destination := bufio.NewPacketConn(remotePacketConn)
|
destination := bufio.NewPacketConn(remotePacketConn)
|
||||||
if ctx.Done() != nil {
|
if ctx.Done() != nil {
|
||||||
onClose = N.AppendClose(onClose, m.monitor.Add(ctx, conn))
|
onClose = N.AppendClose(onClose, m.monitor.Add(ctx, conn))
|
||||||
@@ -273,11 +286,11 @@ func (m *ConnectionManager) packetConnectionCopy(ctx context.Context, source N.P
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !done.Swap(true) {
|
if !done.Swap(true) {
|
||||||
common.Close(source, destination)
|
|
||||||
if onClose != nil {
|
if onClose != nil {
|
||||||
onClose(err)
|
onClose(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
common.Close(source, destination)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*type udpHijacker struct {
|
/*type udpHijacker struct {
|
||||||
|
|||||||
+7
-16
@@ -132,23 +132,11 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
|
|||||||
if r.tracker != nil {
|
if r.tracker != nil {
|
||||||
conn = r.tracker.RoutedConnection(ctx, conn, metadata, selectedRule, selectedOutbound)
|
conn = r.tracker.RoutedConnection(ctx, conn, metadata, selectedRule, selectedOutbound)
|
||||||
}
|
}
|
||||||
legacyOutbound, isLegacy := selectedOutbound.(adapter.ConnectionHandler)
|
if outboundHandler, isHandler := selectedOutbound.(adapter.ConnectionHandlerEx); isHandler {
|
||||||
if isLegacy {
|
outboundHandler.NewConnectionEx(ctx, conn, metadata, onClose)
|
||||||
err = legacyOutbound.NewConnection(ctx, conn, metadata)
|
} else {
|
||||||
if err != nil {
|
r.connection.NewConnection(ctx, selectedOutbound, conn, metadata, onClose)
|
||||||
conn.Close()
|
|
||||||
if onClose != nil {
|
|
||||||
onClose(err)
|
|
||||||
}
|
|
||||||
return E.Cause(err, F.ToString("outbound/", selectedOutbound.Type(), "[", selectedOutbound.Tag(), "]"))
|
|
||||||
} else {
|
|
||||||
if onClose != nil {
|
|
||||||
onClose(nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
r.connection.NewConnection(ctx, selectedOutbound, conn, metadata, onClose)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -440,6 +428,9 @@ match:
|
|||||||
if routeOptions.UDPConnect {
|
if routeOptions.UDPConnect {
|
||||||
metadata.UDPConnect = true
|
metadata.UDPConnect = true
|
||||||
}
|
}
|
||||||
|
if routeOptions.UDPTimeout > 0 {
|
||||||
|
metadata.UDPTimeout = routeOptions.UDPTimeout
|
||||||
|
}
|
||||||
}
|
}
|
||||||
switch action := currentRule.Action().(type) {
|
switch action := currentRule.Action().(type) {
|
||||||
case *rule.RuleActionSniff:
|
case *rule.RuleActionSniff:
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ func NewRuleAction(ctx context.Context, logger logger.ContextLogger, action opti
|
|||||||
FallbackDelay: time.Duration(action.RouteOptionsOptions.FallbackDelay),
|
FallbackDelay: time.Duration(action.RouteOptionsOptions.FallbackDelay),
|
||||||
UDPDisableDomainUnmapping: action.RouteOptionsOptions.UDPDisableDomainUnmapping,
|
UDPDisableDomainUnmapping: action.RouteOptionsOptions.UDPDisableDomainUnmapping,
|
||||||
UDPConnect: action.RouteOptionsOptions.UDPConnect,
|
UDPConnect: action.RouteOptionsOptions.UDPConnect,
|
||||||
|
UDPTimeout: time.Duration(action.RouteOptionsOptions.UDPTimeout),
|
||||||
}, nil
|
}, nil
|
||||||
case C.RuleActionTypeDirect:
|
case C.RuleActionTypeDirect:
|
||||||
directDialer, err := dialer.New(ctx, option.DialerOptions(action.DirectOptions))
|
directDialer, err := dialer.New(ctx, option.DialerOptions(action.DirectOptions))
|
||||||
@@ -152,6 +153,7 @@ type RuleActionRouteOptions struct {
|
|||||||
FallbackDelay time.Duration
|
FallbackDelay time.Duration
|
||||||
UDPDisableDomainUnmapping bool
|
UDPDisableDomainUnmapping bool
|
||||||
UDPConnect bool
|
UDPConnect bool
|
||||||
|
UDPTimeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuleActionRouteOptions) Type() string {
|
func (r *RuleActionRouteOptions) Type() string {
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ func (w *systemDevice) Start() error {
|
|||||||
Inet6RouteAddress: common.Filter(w.options.AllowedAddress, func(it netip.Prefix) bool { return it.Addr().Is6() }),
|
Inet6RouteAddress: common.Filter(w.options.AllowedAddress, func(it netip.Prefix) bool { return it.Addr().Is6() }),
|
||||||
InterfaceMonitor: networkManager.InterfaceMonitor(),
|
InterfaceMonitor: networkManager.InterfaceMonitor(),
|
||||||
InterfaceFinder: networkManager.InterfaceFinder(),
|
InterfaceFinder: networkManager.InterfaceFinder(),
|
||||||
|
Logger: w.options.Logger,
|
||||||
}
|
}
|
||||||
// works with Linux, macOS with IFSCOPE routes, not tested on Windows
|
// works with Linux, macOS with IFSCOPE routes, not tested on Windows
|
||||||
if runtime.GOOS == "darwin" {
|
if runtime.GOOS == "darwin" {
|
||||||
|
|||||||
+3
-3
@@ -9,9 +9,9 @@ PKG_RELEASE:=1
|
|||||||
|
|
||||||
PKG_SOURCE_PROTO:=git
|
PKG_SOURCE_PROTO:=git
|
||||||
PKG_SOURCE_URL:=https://gn.googlesource.com/gn.git
|
PKG_SOURCE_URL:=https://gn.googlesource.com/gn.git
|
||||||
PKG_SOURCE_DATE:=2024-10-14
|
PKGOURCE_DATE:=2024-11-22
|
||||||
PKG_SOURCE_VERSION:=feafd1012a32c05ec6095f69ddc3850afb621f3a
|
PKG_SOURCE_VERSION:=468c6128db7fabe32a29d4753460ef53594406fc
|
||||||
PKG_MIRROR_HASH:=c5e7d8357105b8b6708772490d6c2a7670c9298472efc203e8e409f229de8fb7
|
PKG_MIRROR_HASH:=22b5814a82742e8a39835c26390550a22ecbac5f2d853216057ab3fb4e377fd9
|
||||||
|
|
||||||
PKG_LICENSE:=BSD 3-Clause
|
PKG_LICENSE:=BSD 3-Clause
|
||||||
PKG_LICENSE_FILES:=LICENSE
|
PKG_LICENSE_FILES:=LICENSE
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#ifndef OUT_LAST_COMMIT_POSITION_H_
|
#ifndef OUT_LAST_COMMIT_POSITION_H_
|
||||||
#define OUT_LAST_COMMIT_POSITION_H_
|
#define OUT_LAST_COMMIT_POSITION_H_
|
||||||
|
|
||||||
#define LAST_COMMIT_POSITION_NUM 2202
|
#define LAST_COMMIT_POSITION_NUM 2205
|
||||||
#define LAST_COMMIT_POSITION "2202 (feafd1012a32)"
|
#define LAST_COMMIT_POSITION "2205 (468c6128db7f)"
|
||||||
|
|
||||||
#endif // OUT_LAST_COMMIT_POSITION_H_
|
#endif // OUT_LAST_COMMIT_POSITION_H_
|
||||||
|
|||||||
@@ -378,10 +378,10 @@ return view.extend({
|
|||||||
|
|
||||||
o = s.taboption('external_control', form.Value, 'ui_url', '*' + ' ' + _('UI Url'));
|
o = s.taboption('external_control', form.Value, 'ui_url', '*' + ' ' + _('UI Url'));
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
o.value('https://mirror.ghproxy.com/https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip', 'MetaCubeXD');
|
o.value('https://ghp.ci/https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip', 'MetaCubeXD');
|
||||||
o.value('https://mirror.ghproxy.com/https://github.com/MetaCubeX/Yacd-meta/archive/refs/heads/gh-pages.zip', 'YACD');
|
o.value('https://ghp.ci/https://github.com/MetaCubeX/Yacd-meta/archive/refs/heads/gh-pages.zip', 'YACD');
|
||||||
o.value('https://mirror.ghproxy.com/https://github.com/MetaCubeX/Razord-meta/archive/refs/heads/gh-pages.zip', 'Razord');
|
o.value('https://ghp.ci/https://github.com/MetaCubeX/Razord-meta/archive/refs/heads/gh-pages.zip', 'Razord');
|
||||||
o.value('https://mirror.ghproxy.com/https://github.com/Zephyruso/sing-box-dashboard/archive/refs/heads/gh-pages.zip', 'SD');
|
o.value('https://ghp.ci/https://github.com/Zephyruso/zashboard/archive/refs/heads/gh-pages.zip', 'Zashboard');
|
||||||
|
|
||||||
o = s.taboption('external_control', form.Value, 'api_port', '*' + ' ' + _('API Port'));
|
o = s.taboption('external_control', form.Value, 'api_port', '*' + ' ' + _('API Port'));
|
||||||
o.datatype = 'port';
|
o.datatype = 'port';
|
||||||
|
|||||||
@@ -98,9 +98,50 @@ if not fs.access(CACHE_PATH) then
|
|||||||
fs.mkdir(CACHE_PATH)
|
fs.mkdir(CACHE_PATH)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local LOCAL_EXTEND_ARG = ""
|
||||||
if LOCAL_GROUP == "nil" then
|
if LOCAL_GROUP == "nil" then
|
||||||
LOCAL_GROUP = nil
|
LOCAL_GROUP = nil
|
||||||
log(" * 注意:国内分组名未设置,可能会导致 DNS 解析异常!")
|
log(" * 注意:国内分组名未设置,可能会导致 DNS 分流错误!")
|
||||||
|
else
|
||||||
|
--从smartdns配置中读取参数
|
||||||
|
local custom_conf_path = "/etc/smartdns/custom.conf"
|
||||||
|
local options = {
|
||||||
|
{key = "dualstack_ip_selection", config_key = "dualstack-ip-selection", yes_no = true, arg_yes = "-d yes", arg_no = "-d no", default = "yes"},
|
||||||
|
{key = "speed_check_mode", config_key = "speed-check-mode", prefix = "-c ", default = "ping,tcp:80,tcp:443"},
|
||||||
|
{key = "serve_expired", config_key = "serve-expired", yes_no = true, arg_yes = "", arg_no = "-no-serve-expired", default = "yes"},
|
||||||
|
{key = "response_mode", config_key = "response-mode", prefix = "-r ", default = "first-ping"},
|
||||||
|
{key = "rr_ttl", config_key = "rr-ttl", prefix = "-rr-ttl "},
|
||||||
|
{key = "rr_ttl_min", config_key = "rr-ttl-min", prefix = "-rr-ttl-min "},
|
||||||
|
{key = "rr_ttl_max", config_key = "rr-ttl-max", prefix = "-rr-ttl-max "}
|
||||||
|
}
|
||||||
|
-- 从 custom.conf 中读取值,以最后出现的值为准
|
||||||
|
local custom_config = {}
|
||||||
|
local f_in = io.open(custom_conf_path, "r")
|
||||||
|
if f_in then
|
||||||
|
for line in f_in:lines() do
|
||||||
|
line = line:match("^%s*(.-)%s*$")
|
||||||
|
if line ~= "" and not line:match("^#") then
|
||||||
|
local param, value = line:match("^(%S+)%s+(%S+)$")
|
||||||
|
if param and value then custom_config[param] = value end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
f_in:close()
|
||||||
|
end
|
||||||
|
-- 从 smartdns 配置中读取值,优先级以 custom.conf 为准
|
||||||
|
for _, opt in ipairs(options) do
|
||||||
|
local val = custom_config[opt.config_key] or uci:get("smartdns", "@smartdns[0]", opt.key) or opt.default
|
||||||
|
if val == "yes" then val = "1" elseif val == "no" then val = "0" end
|
||||||
|
if opt.yes_no then
|
||||||
|
local arg = (val == "1" and opt.arg_yes or opt.arg_no)
|
||||||
|
if arg and arg ~= "" then
|
||||||
|
LOCAL_EXTEND_ARG = LOCAL_EXTEND_ARG .. (LOCAL_EXTEND_ARG ~= "" and " " or "") .. arg
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if val and (not opt.value or (opt.invert and val ~= opt.value) or (not opt.invert and val == opt.value)) then
|
||||||
|
LOCAL_EXTEND_ARG = LOCAL_EXTEND_ARG .. (LOCAL_EXTEND_ARG ~= "" and " " or "") .. (opt.prefix or "") .. (opt.arg or val)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if not REMOTE_GROUP or REMOTE_GROUP == "nil" then
|
if not REMOTE_GROUP or REMOTE_GROUP == "nil" then
|
||||||
@@ -167,6 +208,8 @@ if DEFAULT_DNS_GROUP then
|
|||||||
if NO_PROXY_IPV6 == "1" and only_global == 1 and uci:get(appname, TCP_NODE, "protocol") ~= "_shunt" then
|
if NO_PROXY_IPV6 == "1" and only_global == 1 and uci:get(appname, TCP_NODE, "protocol") ~= "_shunt" then
|
||||||
domain_rules_str = domain_rules_str .. " -address #6"
|
domain_rules_str = domain_rules_str .. " -address #6"
|
||||||
end
|
end
|
||||||
|
elseif DEFAULT_DNS_GROUP == LOCAL_GROUP then
|
||||||
|
domain_rules_str = domain_rules_str .. (LOCAL_EXTEND_ARG ~= "" and " " .. LOCAL_EXTEND_ARG or "")
|
||||||
end
|
end
|
||||||
table.insert(config_lines, domain_rules_str)
|
table.insert(config_lines, domain_rules_str)
|
||||||
end
|
end
|
||||||
@@ -226,6 +269,7 @@ if is_file_nonzero(file_vpslist) then
|
|||||||
}
|
}
|
||||||
local domain_rules_str = string.format('domain-rules /domain-set:%s/ %s', domain_set_name, LOCAL_GROUP and "-nameserver " .. LOCAL_GROUP or "")
|
local domain_rules_str = string.format('domain-rules /domain-set:%s/ %s', domain_set_name, LOCAL_GROUP and "-nameserver " .. LOCAL_GROUP or "")
|
||||||
domain_rules_str = domain_rules_str .. " " .. set_type .. " #4:" .. setflag .. "passwall_vpslist,#6:" .. setflag .. "passwall_vpslist6"
|
domain_rules_str = domain_rules_str .. " " .. set_type .. " #4:" .. setflag .. "passwall_vpslist,#6:" .. setflag .. "passwall_vpslist6"
|
||||||
|
domain_rules_str = domain_rules_str .. (LOCAL_EXTEND_ARG ~= "" and " " .. LOCAL_EXTEND_ARG or "")
|
||||||
table.insert(tmp_lines, domain_rules_str)
|
table.insert(tmp_lines, domain_rules_str)
|
||||||
insert_array_after(config_lines, tmp_lines, "#--8")
|
insert_array_after(config_lines, tmp_lines, "#--8")
|
||||||
log(string.format(" - 节点列表中的域名(vpslist)使用分组:%s", LOCAL_GROUP or "默认"))
|
log(string.format(" - 节点列表中的域名(vpslist)使用分组:%s", LOCAL_GROUP or "默认"))
|
||||||
@@ -256,6 +300,7 @@ if USE_DIRECT_LIST == "1" and is_file_nonzero(file_direct_host) then
|
|||||||
}
|
}
|
||||||
local domain_rules_str = string.format('domain-rules /domain-set:%s/ %s', domain_set_name, LOCAL_GROUP and "-nameserver " .. LOCAL_GROUP or "")
|
local domain_rules_str = string.format('domain-rules /domain-set:%s/ %s', domain_set_name, LOCAL_GROUP and "-nameserver " .. LOCAL_GROUP or "")
|
||||||
domain_rules_str = domain_rules_str .. " " .. set_type .. " #4:" .. setflag .. "passwall_whitelist,#6:" .. setflag .. "passwall_whitelist6"
|
domain_rules_str = domain_rules_str .. " " .. set_type .. " #4:" .. setflag .. "passwall_whitelist,#6:" .. setflag .. "passwall_whitelist6"
|
||||||
|
domain_rules_str = domain_rules_str .. (LOCAL_EXTEND_ARG ~= "" and " " .. LOCAL_EXTEND_ARG or "")
|
||||||
table.insert(tmp_lines, domain_rules_str)
|
table.insert(tmp_lines, domain_rules_str)
|
||||||
insert_array_after(config_lines, tmp_lines, "#--6")
|
insert_array_after(config_lines, tmp_lines, "#--6")
|
||||||
log(string.format(" - 域名白名单(whitelist)使用分组:%s", LOCAL_GROUP or "默认"))
|
log(string.format(" - 域名白名单(whitelist)使用分组:%s", LOCAL_GROUP or "默认"))
|
||||||
@@ -328,6 +373,7 @@ if CHN_LIST ~= "0" and is_file_nonzero(RULES_PATH .. "/chnlist") then
|
|||||||
if CHN_LIST == "direct" then
|
if CHN_LIST == "direct" then
|
||||||
local domain_rules_str = string.format('domain-rules /domain-set:%s/ %s', domain_set_name, LOCAL_GROUP and "-nameserver " .. LOCAL_GROUP or "")
|
local domain_rules_str = string.format('domain-rules /domain-set:%s/ %s', domain_set_name, LOCAL_GROUP and "-nameserver " .. LOCAL_GROUP or "")
|
||||||
domain_rules_str = domain_rules_str .. " " .. set_type .. " #4:" .. setflag .. "passwall_chnroute,#6:" .. setflag .. "passwall_chnroute6"
|
domain_rules_str = domain_rules_str .. " " .. set_type .. " #4:" .. setflag .. "passwall_chnroute,#6:" .. setflag .. "passwall_chnroute6"
|
||||||
|
domain_rules_str = domain_rules_str .. (LOCAL_EXTEND_ARG ~= "" and " " .. LOCAL_EXTEND_ARG or "")
|
||||||
table.insert(tmp_lines, domain_rules_str)
|
table.insert(tmp_lines, domain_rules_str)
|
||||||
insert_array_after(config_lines, tmp_lines, "#--2")
|
insert_array_after(config_lines, tmp_lines, "#--2")
|
||||||
log(string.format(" - 中国域名表(chnroute)使用分组:%s", LOCAL_GROUP or "默认"))
|
log(string.format(" - 中国域名表(chnroute)使用分组:%s", LOCAL_GROUP or "默认"))
|
||||||
@@ -419,6 +465,7 @@ if uci:get(appname, TCP_NODE, "protocol") == "_shunt" then
|
|||||||
}
|
}
|
||||||
local domain_rules_str = string.format('domain-rules /domain-set:%s/ %s', domain_set_name, LOCAL_GROUP and "-nameserver " .. LOCAL_GROUP or "")
|
local domain_rules_str = string.format('domain-rules /domain-set:%s/ %s', domain_set_name, LOCAL_GROUP and "-nameserver " .. LOCAL_GROUP or "")
|
||||||
domain_rules_str = domain_rules_str .. " " .. set_type .. " #4:" .. setflag .. "passwall_whitelist,#6:" .. setflag .. "passwall_whitelist6"
|
domain_rules_str = domain_rules_str .. " " .. set_type .. " #4:" .. setflag .. "passwall_whitelist,#6:" .. setflag .. "passwall_whitelist6"
|
||||||
|
domain_rules_str = domain_rules_str .. (LOCAL_EXTEND_ARG ~= "" and " " .. LOCAL_EXTEND_ARG or "")
|
||||||
table.insert(tmp_lines, domain_rules_str)
|
table.insert(tmp_lines, domain_rules_str)
|
||||||
insert_array_after(config_lines, tmp_lines, "#--3")
|
insert_array_after(config_lines, tmp_lines, "#--3")
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ config mixin 'mixin'
|
|||||||
option 'tcp_keep_alive_idle' '600'
|
option 'tcp_keep_alive_idle' '600'
|
||||||
option 'tcp_keep_alive_interval' '15'
|
option 'tcp_keep_alive_interval' '15'
|
||||||
option 'ui_name' 'metacubexd'
|
option 'ui_name' 'metacubexd'
|
||||||
option 'ui_url' 'https://mirror.ghproxy.com/https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip'
|
option 'ui_url' 'https://ghp.ci/https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip'
|
||||||
option 'api_port' '9090'
|
option 'api_port' '9090'
|
||||||
option 'api_secret' ''
|
option 'api_secret' ''
|
||||||
option 'selection_cache' '1'
|
option 'selection_cache' '1'
|
||||||
@@ -90,10 +90,10 @@ config mixin 'mixin'
|
|||||||
option 'dns_nameserver_policy' '0'
|
option 'dns_nameserver_policy' '0'
|
||||||
option 'geoip_format' 'dat'
|
option 'geoip_format' 'dat'
|
||||||
option 'geodata_loader' 'memconservative'
|
option 'geodata_loader' 'memconservative'
|
||||||
option 'geosite_url' 'https://mirror.ghproxy.com/https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat'
|
option 'geosite_url' 'https://ghp.ci/https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat'
|
||||||
option 'geoip_mmdb_url' 'https://mirror.ghproxy.com/https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip-lite.metadb'
|
option 'geoip_mmdb_url' 'https://ghp.ci/https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip-lite.metadb'
|
||||||
option 'geoip_dat_url' 'https://mirror.ghproxy.com/https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip-lite.dat'
|
option 'geoip_dat_url' 'https://ghp.ci/https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip-lite.dat'
|
||||||
option 'geoip_asn_url' 'https://mirror.ghproxy.com/https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/GeoLite2-ASN.mmdb'
|
option 'geoip_asn_url' 'https://ghp.ci/https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/GeoLite2-ASN.mmdb'
|
||||||
option 'geox_auto_update' '0'
|
option 'geox_auto_update' '0'
|
||||||
option 'geox_update_interval' '24'
|
option 'geox_update_interval' '24'
|
||||||
option 'mixin_file_content' '0'
|
option 'mixin_file_content' '0'
|
||||||
|
|||||||
@@ -623,11 +623,23 @@ update_subscription() {
|
|||||||
if [ -z "$subscription_section" ]; then
|
if [ -z "$subscription_section" ]; then
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
# load config
|
||||||
config_load mihomo
|
config_load mihomo
|
||||||
|
# get subscription config
|
||||||
local subscription_name subscription_url subscription_user_agent
|
local subscription_name subscription_url subscription_user_agent
|
||||||
config_get subscription_name "$subscription_section" "name"
|
config_get subscription_name "$subscription_section" "name"
|
||||||
config_get subscription_url "$subscription_section" "url"
|
config_get subscription_url "$subscription_section" "url"
|
||||||
config_get subscription_user_agent "$subscription_section" "user_agent"
|
config_get subscription_user_agent "$subscription_section" "user_agent"
|
||||||
|
# reset subscription info
|
||||||
|
uci_remove "mihomo" "$subscription_section" "expire"
|
||||||
|
uci_remove "mihomo" "$subscription_section" "upload"
|
||||||
|
uci_remove "mihomo" "$subscription_section" "download"
|
||||||
|
uci_remove "mihomo" "$subscription_section" "total"
|
||||||
|
uci_remove "mihomo" "$subscription_section" "used"
|
||||||
|
uci_remove "mihomo" "$subscription_section" "avaliable"
|
||||||
|
uci_remove "mihomo" "$subscription_section" "update"
|
||||||
|
uci_remove "mihomo" "$subscription_section" "success"
|
||||||
|
# update subscription
|
||||||
log "Update Subscription: $subscription_name."
|
log "Update Subscription: $subscription_name."
|
||||||
local subscription_header_tmpfile; subscription_header_tmpfile="/tmp/$subscription_section.header"
|
local subscription_header_tmpfile; subscription_header_tmpfile="/tmp/$subscription_section.header"
|
||||||
local subscription_tmpfile; subscription_tmpfile="/tmp/$subscription_section.yaml"
|
local subscription_tmpfile; subscription_tmpfile="/tmp/$subscription_section.yaml"
|
||||||
@@ -645,6 +657,7 @@ update_subscription() {
|
|||||||
subscription_avaliable=$((subscription_total - subscription_upload - subscription_download))
|
subscription_avaliable=$((subscription_total - subscription_upload - subscription_download))
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
# update subscription info
|
||||||
if [ -n "$subscription_expire" ]; then
|
if [ -n "$subscription_expire" ]; then
|
||||||
uci_set "mihomo" "$subscription_section" "expire" "$(date "+%Y-%m-%d %H:%M:%S" -d @$subscription_expire)"
|
uci_set "mihomo" "$subscription_section" "expire" "$(date "+%Y-%m-%d %H:%M:%S" -d @$subscription_expire)"
|
||||||
fi
|
fi
|
||||||
@@ -664,12 +677,17 @@ update_subscription() {
|
|||||||
uci_set "mihomo" "$subscription_section" "avaliable" "$(format_filesize $subscription_avaliable)"
|
uci_set "mihomo" "$subscription_section" "avaliable" "$(format_filesize $subscription_avaliable)"
|
||||||
fi
|
fi
|
||||||
uci_set "mihomo" "$subscription_section" "update" "$(date "+%Y-%m-%d %H:%M:%S")"
|
uci_set "mihomo" "$subscription_section" "update" "$(date "+%Y-%m-%d %H:%M:%S")"
|
||||||
uci_commit "mihomo"
|
uci_set "mihomo" "$subscription_section" "success" "1"
|
||||||
|
# update subscription file
|
||||||
rm -f "$subscription_header_tmpfile"
|
rm -f "$subscription_header_tmpfile"
|
||||||
mv -f "$subscription_tmpfile" "$subscription_file"
|
mv -f "$subscription_tmpfile" "$subscription_file"
|
||||||
else
|
else
|
||||||
log "Subscription update failed."
|
log "Subscription update failed."
|
||||||
|
# update subscription info
|
||||||
|
uci_set "mihomo" "$subscription_section" "success" "0"
|
||||||
|
# remove tmpfile
|
||||||
rm -f "$subscription_header_tmpfile"
|
rm -f "$subscription_header_tmpfile"
|
||||||
rm -f "$subscription_tmpfile"
|
rm -f "$subscription_tmpfile"
|
||||||
fi
|
fi
|
||||||
|
uci_commit "mihomo"
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-5
@@ -15,11 +15,12 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Set up Node.js
|
- name: Set up Node.js
|
||||||
uses: actions/setup-node@v4
|
shell: bash
|
||||||
with:
|
run: |
|
||||||
node-version: lts/*
|
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
|
||||||
cache: 'yarn'
|
brew install node@20
|
||||||
cache-dependency-path: gui/yarn.lock
|
echo "PATH=\"$(brew --prefix)/opt/node@20/bin:$PATH\"" >> $GITHUB_ENV
|
||||||
|
echo "PATH=\"$(brew --prefix)/opt/node@20/bin:$PATH\"" >> ~/.bash_profile
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update -y && sudo apt-get install -y gzip
|
sudo apt-get update -y && sudo apt-get install -y gzip
|
||||||
|
|||||||
+6
-5
@@ -15,11 +15,12 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Set up Node.js
|
- name: Set up Node.js
|
||||||
uses: actions/setup-node@v4
|
shell: bash
|
||||||
with:
|
run: |
|
||||||
node-version: lts/*
|
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
|
||||||
cache: 'yarn'
|
brew install node@20
|
||||||
cache-dependency-path: gui/yarn.lock
|
echo "PATH=\"$(brew --prefix)/opt/node@20/bin:$PATH\"" >> $GITHUB_ENV
|
||||||
|
echo "PATH=\"$(brew --prefix)/opt/node@20/bin:$PATH\"" >> ~/.bash_profile
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update -y && sudo apt-get install -y gzip
|
sudo apt-get update -y && sudo apt-get install -y gzip
|
||||||
|
|||||||
+8
-18
@@ -1,12 +1,10 @@
|
|||||||
name: Build & Release v2rayA
|
name: Build & Release v2rayA
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
tag:
|
tag:
|
||||||
type: string
|
type: string
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Build_v2rayA_Web:
|
Build_v2rayA_Web:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
@@ -15,11 +13,12 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Set up Node.js
|
- name: Set up Node.js
|
||||||
uses: actions/setup-node@v4
|
shell: bash
|
||||||
with:
|
run: |
|
||||||
node-version: lts/*
|
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
|
||||||
cache: 'yarn'
|
brew install node@20
|
||||||
cache-dependency-path: gui/yarn.lock
|
echo "PATH=\"$(brew --prefix)/opt/node@20/bin:$PATH\"" >> $GITHUB_ENV
|
||||||
|
echo "PATH=\"$(brew --prefix)/opt/node@20/bin:$PATH\"" >> ~/.bash_profile
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update -y && sudo apt-get install -y gzip
|
sudo apt-get update -y && sudo apt-get install -y gzip
|
||||||
@@ -123,13 +122,11 @@ jobs:
|
|||||||
go build -tags "with_gvisor" -o ../v2raya_binaries/v2raya_${filename}_${env:VERSION} -ldflags="-X github.com/v2rayA/v2rayA/conf.Version=${env:VERSION} -s -w" -trimpath
|
go build -tags "with_gvisor" -o ../v2raya_binaries/v2raya_${filename}_${env:VERSION} -ldflags="-X github.com/v2rayA/v2rayA/conf.Version=${env:VERSION} -s -w" -trimpath
|
||||||
Set-Location -Path ..
|
Set-Location -Path ..
|
||||||
}
|
}
|
||||||
|
|
||||||
- name: Upload Artifact
|
- name: Upload Artifact
|
||||||
uses: nanoufo/action-upload-artifacts-and-release-assets@v2
|
uses: nanoufo/action-upload-artifacts-and-release-assets@v2
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
v2raya_binaries/*
|
v2raya_binaries/*
|
||||||
|
|
||||||
Build_Windows_Installers:
|
Build_Windows_Installers:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
needs: [Build_v2rayA_Binaries]
|
needs: [Build_v2rayA_Binaries]
|
||||||
@@ -399,7 +396,6 @@ jobs:
|
|||||||
--data '{"purge_everything":true}'
|
--data '{"purge_everything":true}'
|
||||||
Build_v2ray_Debian_Packages:
|
Build_v2ray_Debian_Packages:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Install Tools
|
- name: Install Tools
|
||||||
run: |
|
run: |
|
||||||
@@ -543,7 +539,6 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "V2RAY_VERSION=$V2RAY_VERSION" >> ./v2ray_packages_version.txt
|
echo "V2RAY_VERSION=$V2RAY_VERSION" >> ./v2ray_packages_version.txt
|
||||||
echo "XRAY_VERSION=$XRAY_VERSION" >> ./xray_packages_version.txt
|
echo "XRAY_VERSION=$XRAY_VERSION" >> ./xray_packages_version.txt
|
||||||
|
|
||||||
- name: Upload Artifact
|
- name: Upload Artifact
|
||||||
uses: nanoufo/action-upload-artifacts-and-release-assets@v2
|
uses: nanoufo/action-upload-artifacts-and-release-assets@v2
|
||||||
with:
|
with:
|
||||||
@@ -555,7 +550,6 @@ jobs:
|
|||||||
Build_APT_Repository_and_AUR:
|
Build_APT_Repository_and_AUR:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
needs: [Build_v2rayA_Binaries, Build_Linux_Packages, Build_v2ray_Debian_Packages]
|
needs: [Build_v2rayA_Binaries, Build_Linux_Packages, Build_v2ray_Debian_Packages]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
@@ -638,7 +632,6 @@ jobs:
|
|||||||
Release_to_Homebrew:
|
Release_to_Homebrew:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
needs: [Build_v2rayA_Binaries]
|
needs: [Build_v2rayA_Binaries]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
@@ -693,7 +686,6 @@ jobs:
|
|||||||
Release_v2rayA_to_Docker:
|
Release_v2rayA_to_Docker:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
needs: [GitHub_Release]
|
needs: [GitHub_Release]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
@@ -733,7 +725,7 @@ jobs:
|
|||||||
context: .
|
context: .
|
||||||
builder: ${{ steps.buildx.outputs.name }}
|
builder: ${{ steps.buildx.outputs.name }}
|
||||||
file: install/docker/Dockerfile.Action
|
file: install/docker/Dockerfile.Action
|
||||||
platforms: linux/arm,linux/arm64,linux/amd64
|
platforms: linux/arm,linux/arm64,linux/amd64,linux/riscv64
|
||||||
push: true
|
push: true
|
||||||
tags: |
|
tags: |
|
||||||
${{ steps.prep.outputs.image }}:${{ steps.prep.outputs.tag }}
|
${{ steps.prep.outputs.image }}:${{ steps.prep.outputs.tag }}
|
||||||
@@ -745,7 +737,6 @@ jobs:
|
|||||||
Release_v2rayA_GUI_to_Docker:
|
Release_v2rayA_GUI_to_Docker:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
needs: [Build_v2rayA_Web]
|
needs: [Build_v2rayA_Web]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
@@ -788,7 +779,7 @@ jobs:
|
|||||||
context: .
|
context: .
|
||||||
builder: ${{ steps.buildx.outputs.name }}
|
builder: ${{ steps.buildx.outputs.name }}
|
||||||
file: install/docker/Dockerfile.GUI.Action
|
file: install/docker/Dockerfile.GUI.Action
|
||||||
platforms: linux/arm,linux/arm64,linux/amd64
|
platforms: linux/arm,linux/arm64,linux/amd64,linux/riscv64
|
||||||
push: true
|
push: true
|
||||||
tags: |
|
tags: |
|
||||||
mzz2017/v2raya-gui:latest
|
mzz2017/v2raya-gui:latest
|
||||||
@@ -798,7 +789,6 @@ jobs:
|
|||||||
Submit_to_Microsoft_winget:
|
Submit_to_Microsoft_winget:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
needs: [GitHub_Release]
|
needs: [GitHub_Release]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
+7
-5
@@ -4,6 +4,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
- dependabot/*
|
||||||
paths:
|
paths:
|
||||||
- "**/*.go"
|
- "**/*.go"
|
||||||
- "go.mod"
|
- "go.mod"
|
||||||
@@ -19,11 +20,12 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Set up Node.js
|
- name: Set up Node.js
|
||||||
uses: actions/setup-node@v4
|
shell: bash
|
||||||
with:
|
run: |
|
||||||
node-version: lts/*
|
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
|
||||||
cache: 'yarn'
|
brew install node@20
|
||||||
cache-dependency-path: gui/yarn.lock
|
echo "PATH=\"$(brew --prefix)/opt/node@20/bin:$PATH\"" >> $GITHUB_ENV
|
||||||
|
echo "PATH=\"$(brew --prefix)/opt/node@20/bin:$PATH\"" >> ~/.bash_profile
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update -y && sudo apt-get install -y gzip
|
sudo apt-get update -y && sudo apt-get install -y gzip
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
FROM nginx:stable-alpine
|
FROM busybox:latest AS builder
|
||||||
WORKDIR /build
|
WORKDIR /
|
||||||
COPY ./web /usr/share/nginx/html
|
COPY ./web /usr/share/v2raya-web
|
||||||
|
ENTRYPOINT ["/bin/httpd", "-f", "-h", "/usr/share/v2raya-web", "-p", "80"]
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
@@ -14,6 +14,10 @@ case "$(arch)" in
|
|||||||
v2ray_arch="arm64-v8a"
|
v2ray_arch="arm64-v8a"
|
||||||
v2raya_arch="arm64"
|
v2raya_arch="arm64"
|
||||||
;;
|
;;
|
||||||
|
riscv64)
|
||||||
|
v2ray_arch="riscv64"
|
||||||
|
v2raya_arch="riscv64"
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<Version>7.2.0</Version>
|
<Version>7.2.1</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ android {
|
|||||||
applicationId = "com.v2ray.ang"
|
applicationId = "com.v2ray.ang"
|
||||||
minSdk = 21
|
minSdk = 21
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = 615
|
versionCode = 616
|
||||||
versionName = "1.9.19"
|
versionName = "1.9.20"
|
||||||
multiDexEnabled = true
|
multiDexEnabled = true
|
||||||
|
|
||||||
splits {
|
splits {
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#### Important changes
|
#### Important changes
|
||||||
- **Login with OAuth is no longer supported for YouTube**
|
- **Login with OAuth is no longer supported for YouTube**
|
||||||
Due to a change made by the site, yt-dlp is longer able to support OAuth login for YouTube. [Read more](https://github.com/yt-dlp/yt-dlp/issues/11462#issuecomment-2471703090)
|
Due to a change made by the site, yt-dlp is no longer able to support OAuth login for YouTube. [Read more](https://github.com/yt-dlp/yt-dlp/issues/11462#issuecomment-2471703090)
|
||||||
|
|
||||||
#### Core changes
|
#### Core changes
|
||||||
- [Catch broken Cryptodome installations](https://github.com/yt-dlp/yt-dlp/commit/b83ca24eb72e1e558b0185bd73975586c0bc0546) ([#11486](https://github.com/yt-dlp/yt-dlp/issues/11486)) by [seproDev](https://github.com/seproDev)
|
- [Catch broken Cryptodome installations](https://github.com/yt-dlp/yt-dlp/commit/b83ca24eb72e1e558b0185bd73975586c0bc0546) ([#11486](https://github.com/yt-dlp/yt-dlp/issues/11486)) by [seproDev](https://github.com/seproDev)
|
||||||
|
|||||||
@@ -1294,6 +1294,7 @@ The available fields are:
|
|||||||
- `playlist_uploader_id` (string): Nickname or id of the playlist uploader
|
- `playlist_uploader_id` (string): Nickname or id of the playlist uploader
|
||||||
- `playlist_channel` (string): Display name of the channel that uploaded the playlist
|
- `playlist_channel` (string): Display name of the channel that uploaded the playlist
|
||||||
- `playlist_channel_id` (string): Identifier of the channel that uploaded the playlist
|
- `playlist_channel_id` (string): Identifier of the channel that uploaded the playlist
|
||||||
|
- `playlist_webpage_url` (string): URL of the playlist webpage
|
||||||
- `webpage_url` (string): A URL to the video webpage which, if given to yt-dlp, should yield the same result again
|
- `webpage_url` (string): A URL to the video webpage which, if given to yt-dlp, should yield the same result again
|
||||||
- `webpage_url_basename` (string): The basename of the webpage URL
|
- `webpage_url_basename` (string): The basename of the webpage URL
|
||||||
- `webpage_url_domain` (string): The domain of the webpage URL
|
- `webpage_url_domain` (string): The domain of the webpage URL
|
||||||
|
|||||||
@@ -238,6 +238,6 @@
|
|||||||
{
|
{
|
||||||
"action": "add",
|
"action": "add",
|
||||||
"when": "52c0ffe40ad6e8404d93296f575007b05b04c686",
|
"when": "52c0ffe40ad6e8404d93296f575007b05b04c686",
|
||||||
"short": "[priority] **Login with OAuth is no longer supported for YouTube**\nDue to a change made by the site, yt-dlp is longer able to support OAuth login for YouTube. [Read more](https://github.com/yt-dlp/yt-dlp/issues/11462#issuecomment-2471703090)"
|
"short": "[priority] **Login with OAuth is no longer supported for YouTube**\nDue to a change made by the site, yt-dlp is no longer able to support OAuth login for YouTube. [Read more](https://github.com/yt-dlp/yt-dlp/issues/11462#issuecomment-2471703090)"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ default = [
|
|||||||
"pycryptodomex",
|
"pycryptodomex",
|
||||||
"requests>=2.32.2,<3",
|
"requests>=2.32.2,<3",
|
||||||
"urllib3>=1.26.17,<3",
|
"urllib3>=1.26.17,<3",
|
||||||
"websockets>=13.0,<14",
|
"websockets>=13.0",
|
||||||
]
|
]
|
||||||
curl-cffi = [
|
curl-cffi = [
|
||||||
"curl-cffi==0.5.10; os_name=='nt' and implementation_name=='cpython'",
|
"curl-cffi==0.5.10; os_name=='nt' and implementation_name=='cpython'",
|
||||||
|
|||||||
@@ -216,7 +216,9 @@ class SocksWebSocketTestRequestHandler(SocksTestRequestHandler):
|
|||||||
protocol = websockets.ServerProtocol()
|
protocol = websockets.ServerProtocol()
|
||||||
connection = websockets.sync.server.ServerConnection(socket=self.request, protocol=protocol, close_timeout=0)
|
connection = websockets.sync.server.ServerConnection(socket=self.request, protocol=protocol, close_timeout=0)
|
||||||
connection.handshake()
|
connection.handshake()
|
||||||
connection.send(json.dumps(self.socks_info))
|
for message in connection:
|
||||||
|
if message == 'socks_info':
|
||||||
|
connection.send(json.dumps(self.socks_info))
|
||||||
connection.close()
|
connection.close()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1947,6 +1947,7 @@ class YoutubeDL:
|
|||||||
'playlist_uploader_id': ie_result.get('uploader_id'),
|
'playlist_uploader_id': ie_result.get('uploader_id'),
|
||||||
'playlist_channel': ie_result.get('channel'),
|
'playlist_channel': ie_result.get('channel'),
|
||||||
'playlist_channel_id': ie_result.get('channel_id'),
|
'playlist_channel_id': ie_result.get('channel_id'),
|
||||||
|
'playlist_webpage_url': ie_result.get('webpage_url'),
|
||||||
**kwargs,
|
**kwargs,
|
||||||
}
|
}
|
||||||
if strict:
|
if strict:
|
||||||
|
|||||||
@@ -261,6 +261,7 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
|
|||||||
'tags': [],
|
'tags': [],
|
||||||
'view_count': int,
|
'view_count': int,
|
||||||
'like_count': int,
|
'like_count': int,
|
||||||
|
'thumbnail': r're:https://\w+.dmcdn.net/v/WnEY61cmvMxt2Fi6d/x1080',
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
# https://geo.dailymotion.com/player/xf7zn.html?playlist=x7wdsj
|
# https://geo.dailymotion.com/player/xf7zn.html?playlist=x7wdsj
|
||||||
@@ -288,6 +289,25 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
|
|||||||
'description': 'À bord du « véloto », l’alternative à la voiture pour la campagne',
|
'description': 'À bord du « véloto », l’alternative à la voiture pour la campagne',
|
||||||
'tags': ['biclou', 'vélo', 'véloto', 'campagne', 'voiture', 'environnement', 'véhicules intermédiaires'],
|
'tags': ['biclou', 'vélo', 'véloto', 'campagne', 'voiture', 'environnement', 'véhicules intermédiaires'],
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
# https://geo.dailymotion.com/player/xry80.html?video=x8vu47w
|
||||||
|
'url': 'https://www.metatube.com/en/videos/546765/This-frogs-decorates-Christmas-tree/',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'x8vu47w',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'like_count': int,
|
||||||
|
'uploader': 'Metatube',
|
||||||
|
'thumbnail': r're:https://\w+.dmcdn.net/v/W1G_S1coGSFTfkTeR/x1080',
|
||||||
|
'upload_date': '20240326',
|
||||||
|
'view_count': int,
|
||||||
|
'timestamp': 1711496732,
|
||||||
|
'age_limit': 0,
|
||||||
|
'uploader_id': 'x2xpy74',
|
||||||
|
'title': 'Está lindas ranitas ponen su arbolito',
|
||||||
|
'duration': 28,
|
||||||
|
'description': 'Que lindura',
|
||||||
|
'tags': [],
|
||||||
|
},
|
||||||
}]
|
}]
|
||||||
_GEO_BYPASS = False
|
_GEO_BYPASS = False
|
||||||
_COMMON_MEDIA_FIELDS = '''description
|
_COMMON_MEDIA_FIELDS = '''description
|
||||||
@@ -302,7 +322,7 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
|
|||||||
yield from super()._extract_embed_urls(url, webpage)
|
yield from super()._extract_embed_urls(url, webpage)
|
||||||
for mobj in re.finditer(
|
for mobj in re.finditer(
|
||||||
r'(?s)DM\.player\([^,]+,\s*{.*?video[\'"]?\s*:\s*["\']?(?P<id>[0-9a-zA-Z]+).+?}\s*\);', webpage):
|
r'(?s)DM\.player\([^,]+,\s*{.*?video[\'"]?\s*:\s*["\']?(?P<id>[0-9a-zA-Z]+).+?}\s*\);', webpage):
|
||||||
yield from 'https://www.dailymotion.com/embed/video/' + mobj.group('id')
|
yield 'https://www.dailymotion.com/embed/video/' + mobj.group('id')
|
||||||
for mobj in re.finditer(
|
for mobj in re.finditer(
|
||||||
r'(?s)<script [^>]*\bsrc=(["\'])(?:https?:)?//[\w-]+\.dailymotion\.com/player/(?:(?!\1).)+\1[^>]*>', webpage):
|
r'(?s)<script [^>]*\bsrc=(["\'])(?:https?:)?//[\w-]+\.dailymotion\.com/player/(?:(?!\1).)+\1[^>]*>', webpage):
|
||||||
attrs = extract_attributes(mobj.group(0))
|
attrs = extract_attributes(mobj.group(0))
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ class FacebookIE(InfoExtractor):
|
|||||||
[^/]+/videos/(?:[^/]+/)?|
|
[^/]+/videos/(?:[^/]+/)?|
|
||||||
[^/]+/posts/|
|
[^/]+/posts/|
|
||||||
events/(?:[^/]+/)?|
|
events/(?:[^/]+/)?|
|
||||||
groups/[^/]+/(?:permalink|posts)/|
|
groups/[^/]+/(?:permalink|posts)/(?:[\da-f]+/)?|
|
||||||
watchparty/
|
watchparty/
|
||||||
)|
|
)|
|
||||||
facebook:
|
facebook:
|
||||||
@@ -410,6 +410,9 @@ class FacebookIE(InfoExtractor):
|
|||||||
'uploader': 'Comitato Liberi Pensatori',
|
'uploader': 'Comitato Liberi Pensatori',
|
||||||
'uploader_id': '100065709540881',
|
'uploader_id': '100065709540881',
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.facebook.com/groups/1513990329015294/posts/d41d8cd9/2013209885760000/?app=fbl',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
_SUPPORTED_PAGLETS_REGEX = r'(?:pagelet_group_mall|permalink_video_pagelet|hyperfeed_story_id_[0-9a-f]+)'
|
_SUPPORTED_PAGLETS_REGEX = r'(?:pagelet_group_mall|permalink_video_pagelet|hyperfeed_story_id_[0-9a-f]+)'
|
||||||
_api_config = {
|
_api_config = {
|
||||||
|
|||||||
@@ -28,24 +28,21 @@ class StripchatIE(InfoExtractor):
|
|||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
webpage = self._download_webpage(url, video_id, headers=self.geo_verification_headers())
|
webpage = self._download_webpage(url, video_id, headers=self.geo_verification_headers())
|
||||||
|
data = self._search_json(
|
||||||
|
r'<script\b[^>]*>\s*window\.__PRELOADED_STATE__\s*=',
|
||||||
|
webpage, 'data', video_id, transform_source=lowercase_escape)
|
||||||
|
|
||||||
data = self._parse_json(
|
if traverse_obj(data, ('viewCam', 'show', {dict})):
|
||||||
self._search_regex(
|
raise ExtractorError('Model is in a private show', expected=True)
|
||||||
r'<script\b[^>]*>\s*window\.__PRELOADED_STATE__\s*=(?P<value>.*?)<\/script>',
|
if not traverse_obj(data, ('viewCam', 'model', 'isLive', {bool})):
|
||||||
webpage, 'data', default='{}', group='value'),
|
|
||||||
video_id, transform_source=lowercase_escape, fatal=False)
|
|
||||||
if not data:
|
|
||||||
raise ExtractorError('Unable to find configuration for stream.')
|
|
||||||
|
|
||||||
if traverse_obj(data, ('viewCam', 'show'), expected_type=dict):
|
|
||||||
raise ExtractorError('Model is in private show', expected=True)
|
|
||||||
elif not traverse_obj(data, ('viewCam', 'model', 'isLive'), expected_type=bool):
|
|
||||||
raise UserNotLive(video_id=video_id)
|
raise UserNotLive(video_id=video_id)
|
||||||
|
|
||||||
model_id = traverse_obj(data, ('viewCam', 'model', 'id'), expected_type=int)
|
model_id = data['viewCam']['model']['id']
|
||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
for host in traverse_obj(data, ('config', 'data', (
|
# HLS hosts are currently found in .configV3.static.features.hlsFallback.fallbackDomains[]
|
||||||
|
# The rest of the path is for backwards compatibility and to guard against A/B testing
|
||||||
|
for host in traverse_obj(data, ((('config', 'data'), ('configV3', 'static')), (
|
||||||
(('features', 'featuresV2'), 'hlsFallback', 'fallbackDomains', ...), 'hlsStreamHost'))):
|
(('features', 'featuresV2'), 'hlsFallback', 'fallbackDomains', ...), 'hlsStreamHost'))):
|
||||||
formats = self._extract_m3u8_formats(
|
formats = self._extract_m3u8_formats(
|
||||||
f'https://edge-hls.{host}/hls/{model_id}/master/{model_id}_auto.m3u8',
|
f'https://edge-hls.{host}/hls/{model_id}/master/{model_id}_auto.m3u8',
|
||||||
@@ -53,7 +50,7 @@ class StripchatIE(InfoExtractor):
|
|||||||
if formats:
|
if formats:
|
||||||
break
|
break
|
||||||
if not formats:
|
if not formats:
|
||||||
self.raise_no_formats('No active streams found', expected=True)
|
self.raise_no_formats('Unable to extract stream host', video_id=video_id)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
|
|||||||
@@ -4986,6 +4986,10 @@ class YoutubeTabBaseInfoExtractor(YoutubeBaseInfoExtractor):
|
|||||||
for item in grid_renderer['items']:
|
for item in grid_renderer['items']:
|
||||||
if not isinstance(item, dict):
|
if not isinstance(item, dict):
|
||||||
continue
|
continue
|
||||||
|
if lockup_view_model := traverse_obj(item, ('lockupViewModel', {dict})):
|
||||||
|
if entry := self._extract_lockup_view_model(lockup_view_model):
|
||||||
|
yield entry
|
||||||
|
continue
|
||||||
renderer = self._extract_basic_item_renderer(item)
|
renderer = self._extract_basic_item_renderer(item)
|
||||||
if not isinstance(renderer, dict):
|
if not isinstance(renderer, dict):
|
||||||
continue
|
continue
|
||||||
@@ -5084,10 +5088,30 @@ class YoutubeTabBaseInfoExtractor(YoutubeBaseInfoExtractor):
|
|||||||
continue
|
continue
|
||||||
yield self._extract_video(renderer)
|
yield self._extract_video(renderer)
|
||||||
|
|
||||||
|
def _extract_lockup_view_model(self, view_model):
|
||||||
|
content_id = view_model.get('contentId')
|
||||||
|
if not content_id:
|
||||||
|
return
|
||||||
|
content_type = view_model.get('contentType')
|
||||||
|
if content_type not in ('LOCKUP_CONTENT_TYPE_PLAYLIST', 'LOCKUP_CONTENT_TYPE_PODCAST'):
|
||||||
|
self.report_warning(
|
||||||
|
f'Unsupported lockup view model content type "{content_type}"{bug_reports_message()}', only_once=True)
|
||||||
|
return
|
||||||
|
return self.url_result(
|
||||||
|
f'https://www.youtube.com/playlist?list={content_id}', ie=YoutubeTabIE, video_id=content_id,
|
||||||
|
title=traverse_obj(view_model, (
|
||||||
|
'metadata', 'lockupMetadataViewModel', 'title', 'content', {str})),
|
||||||
|
thumbnails=self._extract_thumbnails(view_model, (
|
||||||
|
'contentImage', 'collectionThumbnailViewModel', 'primaryThumbnail', 'thumbnailViewModel', 'image'), final_key='sources'))
|
||||||
|
|
||||||
def _rich_entries(self, rich_grid_renderer):
|
def _rich_entries(self, rich_grid_renderer):
|
||||||
|
if lockup_view_model := traverse_obj(rich_grid_renderer, ('content', 'lockupViewModel', {dict})):
|
||||||
|
if entry := self._extract_lockup_view_model(lockup_view_model):
|
||||||
|
yield entry
|
||||||
|
return
|
||||||
renderer = traverse_obj(
|
renderer = traverse_obj(
|
||||||
rich_grid_renderer,
|
rich_grid_renderer,
|
||||||
('content', ('videoRenderer', 'reelItemRenderer', 'playlistRenderer', 'shortsLockupViewModel', 'lockupViewModel'), any)) or {}
|
('content', ('videoRenderer', 'reelItemRenderer', 'playlistRenderer', 'shortsLockupViewModel'), any)) or {}
|
||||||
video_id = renderer.get('videoId')
|
video_id = renderer.get('videoId')
|
||||||
if video_id:
|
if video_id:
|
||||||
yield self._extract_video(renderer)
|
yield self._extract_video(renderer)
|
||||||
@@ -5114,18 +5138,6 @@ class YoutubeTabBaseInfoExtractor(YoutubeBaseInfoExtractor):
|
|||||||
})),
|
})),
|
||||||
thumbnails=self._extract_thumbnails(renderer, 'thumbnail', final_key='sources'))
|
thumbnails=self._extract_thumbnails(renderer, 'thumbnail', final_key='sources'))
|
||||||
return
|
return
|
||||||
# lockupViewModel extraction
|
|
||||||
content_id = renderer.get('contentId')
|
|
||||||
if content_id and renderer.get('contentType') == 'LOCKUP_CONTENT_TYPE_PODCAST':
|
|
||||||
yield self.url_result(
|
|
||||||
f'https://www.youtube.com/playlist?list={content_id}',
|
|
||||||
ie=YoutubeTabIE, video_id=content_id,
|
|
||||||
**traverse_obj(renderer, {
|
|
||||||
'title': ('metadata', 'lockupMetadataViewModel', 'title', 'content', {str}),
|
|
||||||
}),
|
|
||||||
thumbnails=self._extract_thumbnails(renderer, (
|
|
||||||
'contentImage', 'collectionThumbnailViewModel', 'primaryThumbnail', 'thumbnailViewModel', 'image'), final_key='sources'))
|
|
||||||
return
|
|
||||||
|
|
||||||
def _video_entry(self, video_renderer):
|
def _video_entry(self, video_renderer):
|
||||||
video_id = video_renderer.get('videoId')
|
video_id = video_renderer.get('videoId')
|
||||||
@@ -5794,7 +5806,7 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
|
|||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'UCYO_jab_esuFRV4b17AJtAw',
|
'id': 'UCYO_jab_esuFRV4b17AJtAw',
|
||||||
'title': '3Blue1Brown - Playlists',
|
'title': '3Blue1Brown - Playlists',
|
||||||
'description': 'md5:4d1da95432004b7ba840ebc895b6b4c9',
|
'description': 'md5:602e3789e6a0cb7d9d352186b720e395',
|
||||||
'channel_url': 'https://www.youtube.com/channel/UCYO_jab_esuFRV4b17AJtAw',
|
'channel_url': 'https://www.youtube.com/channel/UCYO_jab_esuFRV4b17AJtAw',
|
||||||
'channel': '3Blue1Brown',
|
'channel': '3Blue1Brown',
|
||||||
'channel_id': 'UCYO_jab_esuFRV4b17AJtAw',
|
'channel_id': 'UCYO_jab_esuFRV4b17AJtAw',
|
||||||
|
|||||||
Reference in New Issue
Block a user