diff --git a/.github/update.log b/.github/update.log index 8ba1cde732..60e0691b11 100644 --- a/.github/update.log +++ b/.github/update.log @@ -894,3 +894,4 @@ Update On Wed Jan 22 19:34:22 CET 2025 Update On Thu Jan 23 19:35:00 CET 2025 Update On Fri Jan 24 19:33:49 CET 2025 Update On Sat Jan 25 19:31:08 CET 2025 +Update On Sun Jan 26 19:30:14 CET 2025 diff --git a/brook/ping/ping.json b/brook/ping/ping.json index c27dcb3aa9..22fcc96f7f 100644 --- a/brook/ping/ping.json +++ b/brook/ping/ping.json @@ -2,6 +2,6 @@ "version": "20250202", "text": "Zhi - A Zero-Trust End-to-End Encrypted Instant Messaging App", "link": "https://www.txthinking.com/zhi.html", - "text_zh": "纸,一个零信任端到端加密的聊天应用", - "link_zh": "https://www.txthinking.com/zhi.html" + "text_zh": "生成自用的中国域名直连模块", + "link_zh": "https://www.txthinking.com/talks/articles/china-list.article" } diff --git a/openwrt-passwall2/luci-app-passwall2/luasrc/controller/passwall2.lua b/openwrt-passwall2/luci-app-passwall2/luasrc/controller/passwall2.lua index efcb3bd569..ab80723227 100644 --- a/openwrt-passwall2/luci-app-passwall2/luasrc/controller/passwall2.lua +++ b/openwrt-passwall2/luci-app-passwall2/luasrc/controller/passwall2.lua @@ -7,6 +7,7 @@ local uci = api.uci -- in funtion index() local http = require "luci.http" local util = require "luci.util" local i18n = require "luci.i18n" +local fs = api.fs function index() if not nixio.fs.access("/etc/config/passwall2") then @@ -45,7 +46,7 @@ function index() entry({"admin", "services", appname, "socks_config"}, cbi(appname .. "/client/socks_config")).leaf = true entry({"admin", "services", appname, "acl"}, cbi(appname .. "/client/acl"), _("Access control"), 98).leaf = true entry({"admin", "services", appname, "acl_config"}, cbi(appname .. "/client/acl_config")).leaf = true - entry({"admin", "services", appname, "log"}, form(appname .. "/client/log"), _("Watch Logs"), 999).leaf = true + entry({"admin", "services", appname, "log"}, form(appname .. "/client/log"), _("Log Maint"), 999).leaf = true --[[ Server ]] entry({"admin", "services", appname, "server"}, cbi(appname .. "/server/index"), _("Server-Side"), 99).leaf = true @@ -84,6 +85,9 @@ function index() entry({"admin", "services", appname, "check_" .. com}, call("com_check", com)).leaf = true entry({"admin", "services", appname, "update_" .. com}, call("com_update", com)).leaf = true end + + --[[Backup]] + entry({"admin", "services", appname, "backup"}, call("create_backup")).leaf = true end local function http_write_json(content) @@ -437,4 +441,21 @@ function com_update(comname) http_write_json(json) end +function create_backup() + local backup_files = { + "/etc/config/passwall2", + "/etc/config/passwall2_server", + "/usr/share/passwall2/domains_excluded" + } + local date = os.date("%y%m%d%H%M") + local tar_file = "/tmp/passwall2-" .. date .. "-backup.tar.gz" + fs.remove(tar_file) + local cmd = "tar -czf " .. tar_file .. " " .. table.concat(backup_files, " ") + api.sys.call(cmd) + http.header("Content-Disposition", "attachment; filename=passwall2-" .. date .. "-backup.tar.gz") + http.header("X-Backup-Filename", "passwall2-" .. date .. "-backup.tar.gz") + http.prepare_content("application/octet-stream") + http.write(fs.readfile(tar_file)) + fs.remove(tar_file) +end diff --git a/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/acl_config.lua b/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/acl_config.lua index b4064555f5..c850fdba5f 100644 --- a/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/acl_config.lua +++ b/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/acl_config.lua @@ -1,14 +1,19 @@ local api = require "luci.passwall2.api" local appname = api.appname + +m = Map(appname) +api.set_apply_on_parse(m) + +if not arg[1] or not m:get(arg[1]) then + luci.http.redirect(api.url("acl")) +end + local sys = api.sys local port_validate = function(self, value, t) return value:gsub("-", ":") end -m = Map(appname) -api.set_apply_on_parse(m) - local nodes_table = {} for k, e in ipairs(api.get_valid_nodes()) do nodes_table[#nodes_table + 1] = e diff --git a/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/haproxy.lua b/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/haproxy.lua index b0c8b04ee7..584d7b2bc7 100644 --- a/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/haproxy.lua +++ b/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/haproxy.lua @@ -28,16 +28,21 @@ o = s:option(Flag, "balancing_enable", translate("Enable Load Balancing")) o.rmempty = false o.default = false +---- Console Login Auth +o = s:option(Flag, "console_auth", translate("Console Login Auth")) +o.default = false +o:depends("balancing_enable", true) + ---- Console Username o = s:option(Value, "console_user", translate("Console Username")) o.default = "" -o:depends("balancing_enable", true) +o:depends("console_auth", true) ---- Console Password o = s:option(Value, "console_password", translate("Console Password")) o.password = true o.default = "" -o:depends("balancing_enable", true) +o:depends("console_auth", true) ---- Console Port o = s:option(Value, "console_port", translate("Console Port"), translate( diff --git a/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/log.lua b/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/log.lua index ff7e74fa19..384cf1b315 100644 --- a/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/log.lua +++ b/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/log.lua @@ -1,8 +1,70 @@ local api = require "luci.passwall2.api" -local appname = api.appname +local appname = "passwall2" +local http = require "luci.http" +local fs = api.fs +local sys = api.sys f = SimpleForm(appname) f.reset = false f.submit = false f:append(Template(appname .. "/log/log")) -return f + +fb = SimpleForm('backup-restore') +fb.reset = false +fb.submit = false +s = fb:section(SimpleSection, translate("Backup and Restore"), translate("Backup or Restore Client and Server Configurations.") .. + "
" .. + translate("Note: Restoring configurations across different versions may cause compatibility issues.") .. + "") + +s.anonymous = true +s:append(Template(appname .. "/log/backup_restore")) + +local backup_files = { + "/etc/config/passwall2", + "/etc/config/passwall2_server", + "/usr/share/passwall2/domains_excluded" +} + +local file_path = '/tmp/passwall2_upload.tar.gz' +local temp_dir = '/tmp/passwall2_bak' +local fd +http.setfilehandler(function(meta, chunk, eof) + if not fd and meta and meta.name == "ulfile" and chunk then + sys.call("rm -rf " .. temp_dir) + fs.remove(file_path) + fd = nixio.open(file_path, "w") + sys.call("echo '' > /tmp/log/passwall2.log") + end + if fd and chunk then + fd:write(chunk) + end + if eof and fd then + fd:close() + fd = nil + if fs.access(file_path) then + api.log(" * PassWall2 配置文件上传成功…") + sys.call("mkdir -p " .. temp_dir) + if sys.call("tar -xzf " .. file_path .. " -C " .. temp_dir) == 0 then + for _, backup_file in ipairs(backup_files) do + local temp_file = temp_dir .. backup_file + if fs.access(temp_file) then + sys.call("cp -f " .. temp_file .. " " .. backup_file) + end + end + api.log(" * PassWall2 配置还原成功…") + api.log(" * 重启 PassWall2 服务中…\n") + sys.call('/etc/init.d/passwall2 restart > /dev/null 2>&1 &') + sys.call('/etc/init.d/passwall2_server restart > /dev/null 2>&1 &') + else + api.log(" * PassWall2 配置文件解压失败,请重试!") + end + else + api.log(" * PassWall2 配置文件上传失败,请重试!") + end + sys.call("rm -rf " .. temp_dir) + fs.remove(file_path) + end +end) + +return f, fb diff --git a/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe.lua b/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe.lua index e561c2cea1..e480dacc5d 100644 --- a/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe.lua +++ b/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe.lua @@ -1,5 +1,6 @@ local api = require "luci.passwall2.api" local appname = api.appname +local uci = api.uci local has_ss = api.is_finded("ss-redir") local has_ss_rust = api.is_finded("sslocal") local has_singbox = api.finded_com("singbox") @@ -41,10 +42,28 @@ end m = Map(appname) api.set_apply_on_parse(m) +if api.is_js_luci() then + m.on_after_apply = function(self) + uci:foreach(appname, "subscribe_list", function(e) + uci:delete(appname, e[".name"], "md5") + end) + uci:commit(appname) + end +end + -- [[ Subscribe Settings ]]-- s = m:section(TypedSection, "global_subscribe", "") s.anonymous = true +function m.commit_handler(self) + if self.no_commit then + return + end + self.uci:foreach(appname, "subscribe_list", function(e) + self:del(e[".name"], "md5") + end) +end + o = s:option(ListValue, "filter_keyword_mode", translate("Filter keyword Mode")) o:value("0", translate("Close")) o:value("1", translate("Discard List")) @@ -112,13 +131,15 @@ o:value("ipv6_only", translate("IPv6 Only")) o = s:option(Button, "_stop", translate("Delete All Subscribe Node")) o.inputstyle = "remove" function o.write(e, e) - luci.sys.call("lua /usr/share/" .. appname .. "/subscribe.lua truncate > /dev/null 2>&1") + luci.sys.call("lua /usr/share/" .. appname .. "/subscribe.lua truncate all-node > /dev/null 2>&1") + m.no_commit = true end o = s:option(Button, "_update", translate("Manual subscription All")) o.inputstyle = "apply" function o.write(t, n) luci.sys.call("lua /usr/share/" .. appname .. "/subscribe.lua start > /dev/null 2>&1 &") + m.no_commit = true luci.http.redirect(api.url("log")) end @@ -151,17 +172,23 @@ o.validate = function(self, value, t) end end -o = s:option(DummyValue, "_node_count") +o = s:option(DummyValue, "_node_count", translate("Subscribe Info")) o.rawhtml = true o.cfgvalue = function(t, n) local remark = m:get(n, "remark") or "" + local str = m:get(n, "rem_traffic") or "" + local expired_date = m:get(n, "expired_date") or "" + if expired_date ~= "" then + str = str .. (str ~= "" and "/" or "") .. expired_date + end + str = str ~= "" and "
" .. str or "" local num = 0 m.uci:foreach(appname, "nodes", function(s) if s["add_from"] ~= "" and s["add_from"] == remark then num = num + 1 end end) - return string.format("%s", remark .. " " .. translate("Node num") .. ": " .. num, num) + return string.format("%s%s", translate("Node num") .. ": " .. num, str) end o = s:option(Value, "url", translate("Subscribe URL")) @@ -173,12 +200,14 @@ o.inputstyle = "remove" function o.write(t, n) local remark = m:get(n, "remark") or "" luci.sys.call("lua /usr/share/" .. appname .. "/subscribe.lua truncate " .. remark .. " > /dev/null 2>&1") + m.no_commit = true end o = s:option(Button, "_update", translate("Manual subscription")) o.inputstyle = "apply" function o.write(t, n) luci.sys.call("lua /usr/share/" .. appname .. "/subscribe.lua start " .. n .. " > /dev/null 2>&1 &") + m.no_commit = true luci.http.redirect(api.url("log")) end diff --git a/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua b/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua index 64d37e2897..a2b17111b8 100644 --- a/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua +++ b/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua @@ -1,5 +1,14 @@ local api = require "luci.passwall2.api" local appname = api.appname + +m = Map(appname) +m.redirect = api.url("node_subscribe") +api.set_apply_on_parse(m) + +if not arg[1] or not m:get(arg[1]) then + luci.http.redirect(m.redirect) +end + local has_ss = api.is_finded("ss-redir") local has_ss_rust = api.is_finded("sslocal") local has_singbox = api.finded_com("singbox") @@ -38,14 +47,14 @@ if has_hysteria2 then table.insert(hysteria2_type, s) end -m = Map(appname) -m.redirect = api.url("node_subscribe") -api.set_apply_on_parse(m) - s = m:section(NamedSection, arg[1]) s.addremove = false s.dynamic = false +function m.commit_handler(self) + self:del(arg[1], "md5") +end + o = s:option(Value, "remark", translate("Subscribe Remark")) o.rmempty = false diff --git a/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/other.lua b/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/other.lua index 2f66f7616c..6fbeb069f9 100644 --- a/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/other.lua +++ b/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/other.lua @@ -111,13 +111,16 @@ if (os.execute("lsmod | grep -i REDIRECT >/dev/null") == 0 and os.execute("lsmod o:value("redirect", "REDIRECT") o:value("tproxy", "TPROXY") o:depends("ipv6_tproxy", false) + o.remove = function(self, section) + -- 禁止在隐藏时删除 + end o = s:option(ListValue, "_tcp_proxy_way", translate("TCP Proxy Way")) o.default = "tproxy" o:value("tproxy", "TPROXY") o:depends("ipv6_tproxy", true) o.write = function(self, section, value) - return self.map:set(section, "tcp_proxy_way", value) + self.map:set(section, "tcp_proxy_way", value) end if os.execute("lsmod | grep -i ip6table_mangle >/dev/null") == 0 or os.execute("lsmod | grep -i nft_tproxy >/dev/null") == 0 then @@ -210,6 +213,7 @@ if has_xray then o = s_xray_noise:option(ListValue, "type", translate("Type")) o:value("rand", "rand") o:value("str", "str") + o:value("hex", "hex") o:value("base64", "base64") o = s_xray_noise:option(Value, "packet", translate("Packet")) diff --git a/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/socks_config.lua b/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/socks_config.lua index fa87ec0452..c970d0f106 100644 --- a/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/socks_config.lua +++ b/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/socks_config.lua @@ -1,11 +1,16 @@ local api = require "luci.passwall2.api" local appname = api.appname -local has_singbox = api.finded_com("singbox") -local has_xray = api.finded_com("xray") m = Map(appname) api.set_apply_on_parse(m) +if not arg[1] or not m:get(arg[1]) then + luci.http.redirect(api.url()) +end + +local has_singbox = api.finded_com("singbox") +local has_xray = api.finded_com("xray") + local nodes_table = {} for k, e in ipairs(api.get_valid_nodes()) do nodes_table[#nodes_table + 1] = e diff --git a/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua b/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua index b10af78282..1252194976 100644 --- a/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua +++ b/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua @@ -569,14 +569,16 @@ o = s:option(Value, _n("xhttp_path"), translate("XHTTP Path")) o.placeholder = "/" o:depends({ [_n("transport")] = "xhttp" }) -o = s:option(TextValue, _n("xhttp_extra"), translate("XHTTP Extra"), translate("An XHTTP extra object in raw json")) +o = s:option(Flag, _n("use_xhttp_extra"), translate("XHTTP Extra")) +o.default = "0" o:depends({ [_n("transport")] = "xhttp" }) + +o = s:option(TextValue, _n("xhttp_extra"), " ", translate("An XHttpObject in JSON format, used for sharing.")) +o:depends({ [_n("use_xhttp_extra")] = true }) o.rows = 15 o.wrap = "off" o.custom_write = function(self, section, value) - m:set(section, self.option:sub(1 + #option_prefix), value) - local success, data = pcall(jsonc.parse, value) if success and data then local address = (data.extra and data.extra.downloadSettings and data.extra.downloadSettings.address) @@ -597,6 +599,10 @@ o.validate = function(self, value) end return value end +o.custom_remove = function(self, section, value) + m:del(section, self.option:sub(1 + #option_prefix)) + m:del(section, "download_address") +end -- [[ Mux.Cool ]]-- o = s:option(Flag, _n("mux"), "Mux", translate("Enable Mux.Cool")) diff --git a/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ray.lua b/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ray.lua index ad3d2204a5..d508d71551 100644 --- a/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ray.lua +++ b/openwrt-passwall2/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ray.lua @@ -149,8 +149,26 @@ o = s:option(Value, _n("reality_dest"), translate("Dest")) o.default = "google.com:443" o:depends({ [_n("reality")] = true }) -o = s:option(Value, _n("reality_serverNames"), translate("serverNames")) +o = s:option(DynamicList, _n("reality_serverNames"), translate("serverNames")) o:depends({ [_n("reality")] = true }) +function o.write(self, section, value) + local t = {} + local t2 = {} + if type(value) == "table" then + local x + for _, x in ipairs(value) do + if x and #x > 0 then + if not t2[x] then + t2[x] = x + t[#t+1] = x + end + end + end + else + t = { value } + end + return DynamicList.write(self, section, t) +end o = s:option(ListValue, _n("alpn"), translate("alpn")) o.default = "h2,http/1.1" diff --git a/openwrt-passwall2/luci-app-passwall2/luasrc/passwall2/api.lua b/openwrt-passwall2/luci-app-passwall2/luasrc/passwall2/api.lua index 5dd8468d76..3499e1a128 100644 --- a/openwrt-passwall2/luci-app-passwall2/luasrc/passwall2/api.lua +++ b/openwrt-passwall2/luci-app-passwall2/luasrc/passwall2/api.lua @@ -1232,11 +1232,16 @@ function luci_types(id, m, s, type_name, option_prefix) end s.fields[key].remove = function(self, section) if s.fields["type"]:formvalue(id) == type_name then - if self.rewrite_option and rewrite_option_table[self.rewrite_option] == 1 then - m:del(section, self.rewrite_option) + -- 添加自定义 custom_remove 属性,如果有自定义的 custom_remove 函数,则使用自定义的 remove 逻辑 + if self.custom_remove then + self:custom_remove(section) else - if self.option:find(option_prefix) == 1 then - m:del(section, self.option:sub(1 + #option_prefix)) + if self.rewrite_option and rewrite_option_table[self.rewrite_option] == 1 then + m:del(section, self.rewrite_option) + else + if self.option:find(option_prefix) == 1 then + m:del(section, self.option:sub(1 + #option_prefix)) + end end end end diff --git a/openwrt-passwall2/luci-app-passwall2/luasrc/passwall2/util_xray.lua b/openwrt-passwall2/luci-app-passwall2/luasrc/passwall2/util_xray.lua index f7dd5c35ed..d8cbd949d7 100644 --- a/openwrt-passwall2/luci-app-passwall2/luasrc/passwall2/util_xray.lua +++ b/openwrt-passwall2/luci-app-passwall2/luasrc/passwall2/util_xray.lua @@ -533,9 +533,7 @@ function gen_config_server(node) config.inbounds[1].streamSettings.realitySettings = { show = false, dest = node.reality_dest, - serverNames = { - node.reality_serverNames - }, + serverNames = node.reality_serverNames or {}, privateKey = node.reality_private_key, shortIds = node.reality_shortId or "" } or nil diff --git a/openwrt-passwall2/luci-app-passwall2/luasrc/view/passwall2/global/faq.htm b/openwrt-passwall2/luci-app-passwall2/luasrc/view/passwall2/global/faq.htm index 08ead501f9..ae9d0aa8e6 100644 --- a/openwrt-passwall2/luci-app-passwall2/luasrc/view/passwall2/global/faq.htm +++ b/openwrt-passwall2/luci-app-passwall2/luasrc/view/passwall2/global/faq.htm @@ -47,16 +47,9 @@ local api = require "luci.passwall2.api" diff --git a/openwrt-passwall2/luci-app-passwall2/luasrc/view/passwall2/node_list/link_share_man.htm b/openwrt-passwall2/luci-app-passwall2/luasrc/view/passwall2/node_list/link_share_man.htm index 2cd22fb76c..27b1aec7b7 100644 --- a/openwrt-passwall2/luci-app-passwall2/luasrc/view/passwall2/node_list/link_share_man.htm +++ b/openwrt-passwall2/luci-app-passwall2/luasrc/view/passwall2/node_list/link_share_man.htm @@ -379,7 +379,13 @@ local hysteria2_type = map:get("@global_subscribe[0]", "hysteria2_type") or "sin params += opt.query("host", dom_prefix + "xhttp_host"); params += opt.query("path", dom_prefix + "xhttp_path"); params += opt.query("mode", dom_prefix + "xhttp_mode"); - params += opt.query("extra", dom_prefix + "xhttp_extra"); + if (opt.get(dom_prefix + "use_xhttp_extra").checked) { + params += opt.query("extra", dom_prefix + "xhttp_extra"); + } + } else if (v_transport === "httpupgrade") { + v_transport = "httpupgrade"; + params += opt.query("host", dom_prefix + "httpupgrade_host"); + params += opt.query("path", dom_prefix + "httpupgrade_path"); } params += "&type=" + v_transport; @@ -1245,7 +1251,11 @@ local hysteria2_type = map:get("@global_subscribe[0]", "hysteria2_type") or "sin opt.set(dom_prefix + 'xhttp_host', queryParam.host || ""); opt.set(dom_prefix + 'xhttp_path', queryParam.path || ""); opt.set(dom_prefix + 'xhttp_mode', queryParam.mode || "auto"); + opt.set(dom_prefix + 'use_xhttp_extra', !!queryParam.extra); opt.set(dom_prefix + 'xhttp_extra', queryParam.extra || ""); + } else if (queryParam.type === "httpupgrade") { + opt.set(dom_prefix + 'httpupgrade_host', queryParam.host || ""); + opt.set(dom_prefix + 'httpupgrade_path', queryParam.path || ""); } if (m.hash) { diff --git a/openwrt-passwall2/luci-app-passwall2/luasrc/view/passwall2/node_list/node_list.htm b/openwrt-passwall2/luci-app-passwall2/luasrc/view/passwall2/node_list/node_list.htm index 70db75fc1c..48066f7628 100644 --- a/openwrt-passwall2/luci-app-passwall2/luasrc/view/passwall2/node_list/node_list.htm +++ b/openwrt-passwall2/luci-app-passwall2/luasrc/view/passwall2/node_list/node_list.htm @@ -46,9 +46,19 @@ table td, .table .td { color: red !important; } +._now_use_bg { + background: #5e72e445 !important; +} + .ping a:hover{ text-decoration : underline; } + +@media (prefers-color-scheme: dark) { + ._now_use_bg { + background: #4a90e2 !important; + } +}
" data-index="<%=self.index%>" data-depends="<%=pcdata(self:deplist2json(section))%>"> - - -
\ No newline at end of file + +
+ + +
+ diff --git a/openwrt-passwall2/luci-app-passwall2/po/zh-cn/passwall2.po b/openwrt-passwall2/luci-app-passwall2/po/zh-cn/passwall2.po index a2121e55d6..8950086d07 100644 --- a/openwrt-passwall2/luci-app-passwall2/po/zh-cn/passwall2.po +++ b/openwrt-passwall2/luci-app-passwall2/po/zh-cn/passwall2.po @@ -46,9 +46,6 @@ msgstr "规则列表" msgid "Access control" msgstr "访问控制" -msgid "Watch Logs" -msgstr "查看日志" - msgid "Node Config" msgstr "节点配置" @@ -199,18 +196,12 @@ msgstr "客户端DNS和默认网关必须指向本路由器。" msgid "If you have a wrong DNS process, the consequences are at your own risk!" msgstr "如果你自行配置了错误的DNS流程,后果自负!" -msgid "Restore the default configuration method. Input example in the address bar:" -msgstr "恢复默认配置方法,地址栏输入例:" - msgid "Hide menu method, input example in the address bar:" msgstr "隐藏菜单方法,地址栏输入例:" msgid "After the hidden to the display, input example in the address bar:" msgstr "当你隐藏后想再次显示,地址栏输入例:" -msgid "Are you sure to reset?" -msgstr "你确定要恢复吗?" - msgid "Are you sure to hide?" msgstr "你确定要隐藏吗?" @@ -235,9 +226,6 @@ msgstr "对于移动设备,可通过重新接入网络的方式清除。比如 msgid "Please make sure your device's network settings point both the DNS server and default gateway to this router, to ensure DNS queries are properly routed." msgstr "请确认您设备的网络设置,客户端DNS服务器和默认网关应均指向本路由器,以确保DNS查询正确路由。" -msgid "Restore to default configuration:" -msgstr "恢复默认配置:" - msgid "Browser access:" msgstr "浏览器访问:" @@ -718,6 +706,9 @@ msgstr "请输入节点关键字,注意区分空格、大写和小写。" msgid "Enable Load Balancing" msgstr "开启负载均衡" +msgid "Console Login Auth" +msgstr "控制台登录认证" + msgid "Console Username" msgstr "控制台账号" @@ -919,6 +910,9 @@ msgstr "节点订阅" msgid "Subscribe Remark" msgstr "订阅备注(机场)" +msgid "Subscribe Info" +msgstr "订阅信息" + msgid "Subscribe URL" msgstr "订阅网址" @@ -1417,8 +1411,8 @@ msgstr "客户端文件不适合当前设备。" msgid "Can't move new file to path: %s" msgstr "无法移动新文件到:%s" -msgid "An XHTTP extra object in raw json" -msgstr "一个 json 格式的 XHTTP extra object" +msgid "An XHttpObject in JSON format, used for sharing." +msgstr "JSON 格式的 XHttpObject,用来实现分享。" msgid "Enable Mux.Cool" msgstr "启用 Mux.Cool" @@ -1611,3 +1605,45 @@ msgstr "仅 IPv4" msgid "IPv6 Only" msgstr "仅 IPv6" + +msgid "Log Maint" +msgstr "日志维护" + +msgid "Backup and Restore" +msgstr "备份还原" + +msgid "Backup or Restore Client and Server Configurations." +msgstr "备份或还原客户端及服务端配置。" + +msgid "Note: Restoring configurations across different versions may cause compatibility issues." +msgstr "注意:不同版本间的配置恢复可能会导致兼容性问题。" + +msgid "Create Backup File" +msgstr "创建备份文件" + +msgid "Restore Backup File" +msgstr "恢复备份文件" + +msgid "DL Backup" +msgstr "下载备份" + +msgid "RST Backup" +msgstr "恢复备份" + +msgid "UL Restore" +msgstr "上传恢复" + +msgid "CLOSE WIN" +msgstr "关闭窗口" + +msgid "Restore to default configuration" +msgstr "恢复默认配置" + +msgid "Do Reset" +msgstr "执行重置" + +msgid "Do you want to restore the client to default settings?" +msgstr "是否要恢复客户端默认配置?" + +msgid "Are you sure you want to restore the client to default settings?" +msgstr "是否真的要恢复客户端默认配置?" diff --git a/openwrt-passwall2/luci-app-passwall2/root/usr/share/passwall2/0_default_config b/openwrt-passwall2/luci-app-passwall2/root/usr/share/passwall2/0_default_config index 83e60ae887..79dce26964 100644 --- a/openwrt-passwall2/luci-app-passwall2/root/usr/share/passwall2/0_default_config +++ b/openwrt-passwall2/luci-app-passwall2/root/usr/share/passwall2/0_default_config @@ -55,6 +55,8 @@ config global_app config global_subscribe option filter_keyword_mode '1' + list filter_discard_list '距离下次重置剩余' + list filter_discard_list '套餐到期' list filter_discard_list '过期时间' list filter_discard_list '剩余流量' list filter_discard_list 'QQ群' diff --git a/openwrt-passwall2/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua b/openwrt-passwall2/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua index 10d6ddf11c..ee6d4c129a 100755 --- a/openwrt-passwall2/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua +++ b/openwrt-passwall2/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua @@ -379,6 +379,24 @@ local function trim(text) return (sgsub(text, "^%s*(.-)%s*$", "%1")) end +-- 取机场信息(剩余流量、到期时间) +local subscribe_info = {} +local function get_subscribe_info(cfgid, value) + if type(cfgid) ~= "string" or cfgid == "" or type(value) ~= "string" then + return + end + value = value:gsub("%s+", "") + local expired_date = value:match("套餐到期:(.+)") + local rem_traffic = value:match("剩余流量:(.+)") + subscribe_info[cfgid] = subscribe_info[cfgid] or {expired_date = "", rem_traffic = ""} + if expired_date then + subscribe_info[cfgid]["expired_date"] = expired_date + end + if rem_traffic then + subscribe_info[cfgid]["rem_traffic"] = rem_traffic + end +end + -- 处理数据 local function processData(szType, content, add_mode, add_from) --log(content, add_mode, add_from) @@ -519,6 +537,10 @@ local function processData(szType, content, add_mode, add_from) result.download_address = nil end end + if info.net == 'httpupgrade' then + result.httpupgrade_host = info.host + result.httpupgrade_path = info.path + end if not info.security then result.security = "auto" end if info.tls == "tls" or info.tls == "1" then result.tls = "1" @@ -882,6 +904,10 @@ local function processData(szType, content, add_mode, add_from) result.xhttp_host = params.host result.xhttp_path = params.path end + if params.type == 'httpupgrade' then + result.httpupgrade_host = params.host + result.httpupgrade_path = params.path + end result.encryption = params.encryption or "none" @@ -1021,6 +1047,21 @@ local function processData(szType, content, add_mode, add_from) if params.type == 'xhttp' or params.type == 'splithttp' then result.xhttp_host = params.host result.xhttp_path = params.path + result.xhttp_mode = params.mode or "auto" + result.use_xhttp_extra = (params.extra and params.extra ~= "") and "1" or nil + result.xhttp_extra = (params.extra and params.extra ~= "") and params.extra or nil + local success, Data = pcall(jsonParse, params.extra) + if success and Data then + local address = (Data.extra and Data.extra.downloadSettings and Data.extra.downloadSettings.address) + or (Data.downloadSettings and Data.downloadSettings.address) + result.download_address = address and address ~= "" and address or nil + else + result.download_address = nil + end + end + if params.type == 'httpupgrade' then + result.httpupgrade_host = params.host + result.httpupgrade_path = params.path end result.encryption = params.encryption or "none" @@ -1279,7 +1320,7 @@ local function truncate_nodes(add_from) end) if add_from then uci:foreach(appname, "subscribe_list", function(o) - if o.remark == add_from then + if add_from == "all-node" or add_from == o.remark then uci:delete(appname, o['.name'], "md5") end end) @@ -1438,6 +1479,16 @@ local function update_node(manual) end end end + -- 更新机场信息 + for cfgid, info in pairs(subscribe_info) do + for key, value in pairs(info) do + if value ~= "" then + uci:set(appname, cfgid, key, value) + else + uci:delete(appname, cfgid, key) + end + end + end api.uci_save(uci, appname, true) if next(CONFIG) then @@ -1469,7 +1520,7 @@ local function update_node(manual) luci.sys.call("/etc/init.d/" .. appname .. " restart > /dev/null 2>&1 &") end -local function parse_link(raw, add_mode, add_from) +local function parse_link(raw, add_mode, add_from, cfgid) if raw and #raw > 0 then local nodes, szType local node_list = {} @@ -1531,6 +1582,9 @@ local function parse_link(raw, add_mode, add_from) else tinsert(node_list, result) end + if add_mode == "2" then + get_subscribe_info(cfgid, result.remarks) + end end end, function (err) --log(err) @@ -1634,7 +1688,7 @@ local execute = function() log('订阅:【' .. remark .. '】没有变化,无需更新。') else os.remove("/tmp/" .. cfgid) - parse_link(raw, "2", remark) + parse_link(raw, "2", remark, cfgid) uci:set(appname, cfgid, "md5", new_md5) end else diff --git a/small/luci-app-fchomo/root/etc/fchomo/scripts/fchomo.uc b/small/luci-app-fchomo/root/etc/fchomo/scripts/fchomo.uc old mode 100755 new mode 100644 diff --git a/small/luci-app-fchomo/root/usr/share/rpcd/ucode/luci.fchomo b/small/luci-app-fchomo/root/usr/share/rpcd/ucode/luci.fchomo old mode 100755 new mode 100644 diff --git a/small/luci-app-passwall2/luasrc/controller/passwall2.lua b/small/luci-app-passwall2/luasrc/controller/passwall2.lua index efcb3bd569..ab80723227 100644 --- a/small/luci-app-passwall2/luasrc/controller/passwall2.lua +++ b/small/luci-app-passwall2/luasrc/controller/passwall2.lua @@ -7,6 +7,7 @@ local uci = api.uci -- in funtion index() local http = require "luci.http" local util = require "luci.util" local i18n = require "luci.i18n" +local fs = api.fs function index() if not nixio.fs.access("/etc/config/passwall2") then @@ -45,7 +46,7 @@ function index() entry({"admin", "services", appname, "socks_config"}, cbi(appname .. "/client/socks_config")).leaf = true entry({"admin", "services", appname, "acl"}, cbi(appname .. "/client/acl"), _("Access control"), 98).leaf = true entry({"admin", "services", appname, "acl_config"}, cbi(appname .. "/client/acl_config")).leaf = true - entry({"admin", "services", appname, "log"}, form(appname .. "/client/log"), _("Watch Logs"), 999).leaf = true + entry({"admin", "services", appname, "log"}, form(appname .. "/client/log"), _("Log Maint"), 999).leaf = true --[[ Server ]] entry({"admin", "services", appname, "server"}, cbi(appname .. "/server/index"), _("Server-Side"), 99).leaf = true @@ -84,6 +85,9 @@ function index() entry({"admin", "services", appname, "check_" .. com}, call("com_check", com)).leaf = true entry({"admin", "services", appname, "update_" .. com}, call("com_update", com)).leaf = true end + + --[[Backup]] + entry({"admin", "services", appname, "backup"}, call("create_backup")).leaf = true end local function http_write_json(content) @@ -437,4 +441,21 @@ function com_update(comname) http_write_json(json) end +function create_backup() + local backup_files = { + "/etc/config/passwall2", + "/etc/config/passwall2_server", + "/usr/share/passwall2/domains_excluded" + } + local date = os.date("%y%m%d%H%M") + local tar_file = "/tmp/passwall2-" .. date .. "-backup.tar.gz" + fs.remove(tar_file) + local cmd = "tar -czf " .. tar_file .. " " .. table.concat(backup_files, " ") + api.sys.call(cmd) + http.header("Content-Disposition", "attachment; filename=passwall2-" .. date .. "-backup.tar.gz") + http.header("X-Backup-Filename", "passwall2-" .. date .. "-backup.tar.gz") + http.prepare_content("application/octet-stream") + http.write(fs.readfile(tar_file)) + fs.remove(tar_file) +end diff --git a/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/acl_config.lua b/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/acl_config.lua index b4064555f5..c850fdba5f 100644 --- a/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/acl_config.lua +++ b/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/acl_config.lua @@ -1,14 +1,19 @@ local api = require "luci.passwall2.api" local appname = api.appname + +m = Map(appname) +api.set_apply_on_parse(m) + +if not arg[1] or not m:get(arg[1]) then + luci.http.redirect(api.url("acl")) +end + local sys = api.sys local port_validate = function(self, value, t) return value:gsub("-", ":") end -m = Map(appname) -api.set_apply_on_parse(m) - local nodes_table = {} for k, e in ipairs(api.get_valid_nodes()) do nodes_table[#nodes_table + 1] = e diff --git a/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/haproxy.lua b/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/haproxy.lua index b0c8b04ee7..584d7b2bc7 100644 --- a/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/haproxy.lua +++ b/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/haproxy.lua @@ -28,16 +28,21 @@ o = s:option(Flag, "balancing_enable", translate("Enable Load Balancing")) o.rmempty = false o.default = false +---- Console Login Auth +o = s:option(Flag, "console_auth", translate("Console Login Auth")) +o.default = false +o:depends("balancing_enable", true) + ---- Console Username o = s:option(Value, "console_user", translate("Console Username")) o.default = "" -o:depends("balancing_enable", true) +o:depends("console_auth", true) ---- Console Password o = s:option(Value, "console_password", translate("Console Password")) o.password = true o.default = "" -o:depends("balancing_enable", true) +o:depends("console_auth", true) ---- Console Port o = s:option(Value, "console_port", translate("Console Port"), translate( diff --git a/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/log.lua b/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/log.lua index ff7e74fa19..384cf1b315 100644 --- a/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/log.lua +++ b/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/log.lua @@ -1,8 +1,70 @@ local api = require "luci.passwall2.api" -local appname = api.appname +local appname = "passwall2" +local http = require "luci.http" +local fs = api.fs +local sys = api.sys f = SimpleForm(appname) f.reset = false f.submit = false f:append(Template(appname .. "/log/log")) -return f + +fb = SimpleForm('backup-restore') +fb.reset = false +fb.submit = false +s = fb:section(SimpleSection, translate("Backup and Restore"), translate("Backup or Restore Client and Server Configurations.") .. + "
" .. + translate("Note: Restoring configurations across different versions may cause compatibility issues.") .. + "") + +s.anonymous = true +s:append(Template(appname .. "/log/backup_restore")) + +local backup_files = { + "/etc/config/passwall2", + "/etc/config/passwall2_server", + "/usr/share/passwall2/domains_excluded" +} + +local file_path = '/tmp/passwall2_upload.tar.gz' +local temp_dir = '/tmp/passwall2_bak' +local fd +http.setfilehandler(function(meta, chunk, eof) + if not fd and meta and meta.name == "ulfile" and chunk then + sys.call("rm -rf " .. temp_dir) + fs.remove(file_path) + fd = nixio.open(file_path, "w") + sys.call("echo '' > /tmp/log/passwall2.log") + end + if fd and chunk then + fd:write(chunk) + end + if eof and fd then + fd:close() + fd = nil + if fs.access(file_path) then + api.log(" * PassWall2 配置文件上传成功…") + sys.call("mkdir -p " .. temp_dir) + if sys.call("tar -xzf " .. file_path .. " -C " .. temp_dir) == 0 then + for _, backup_file in ipairs(backup_files) do + local temp_file = temp_dir .. backup_file + if fs.access(temp_file) then + sys.call("cp -f " .. temp_file .. " " .. backup_file) + end + end + api.log(" * PassWall2 配置还原成功…") + api.log(" * 重启 PassWall2 服务中…\n") + sys.call('/etc/init.d/passwall2 restart > /dev/null 2>&1 &') + sys.call('/etc/init.d/passwall2_server restart > /dev/null 2>&1 &') + else + api.log(" * PassWall2 配置文件解压失败,请重试!") + end + else + api.log(" * PassWall2 配置文件上传失败,请重试!") + end + sys.call("rm -rf " .. temp_dir) + fs.remove(file_path) + end +end) + +return f, fb diff --git a/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe.lua b/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe.lua index e561c2cea1..e480dacc5d 100644 --- a/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe.lua +++ b/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe.lua @@ -1,5 +1,6 @@ local api = require "luci.passwall2.api" local appname = api.appname +local uci = api.uci local has_ss = api.is_finded("ss-redir") local has_ss_rust = api.is_finded("sslocal") local has_singbox = api.finded_com("singbox") @@ -41,10 +42,28 @@ end m = Map(appname) api.set_apply_on_parse(m) +if api.is_js_luci() then + m.on_after_apply = function(self) + uci:foreach(appname, "subscribe_list", function(e) + uci:delete(appname, e[".name"], "md5") + end) + uci:commit(appname) + end +end + -- [[ Subscribe Settings ]]-- s = m:section(TypedSection, "global_subscribe", "") s.anonymous = true +function m.commit_handler(self) + if self.no_commit then + return + end + self.uci:foreach(appname, "subscribe_list", function(e) + self:del(e[".name"], "md5") + end) +end + o = s:option(ListValue, "filter_keyword_mode", translate("Filter keyword Mode")) o:value("0", translate("Close")) o:value("1", translate("Discard List")) @@ -112,13 +131,15 @@ o:value("ipv6_only", translate("IPv6 Only")) o = s:option(Button, "_stop", translate("Delete All Subscribe Node")) o.inputstyle = "remove" function o.write(e, e) - luci.sys.call("lua /usr/share/" .. appname .. "/subscribe.lua truncate > /dev/null 2>&1") + luci.sys.call("lua /usr/share/" .. appname .. "/subscribe.lua truncate all-node > /dev/null 2>&1") + m.no_commit = true end o = s:option(Button, "_update", translate("Manual subscription All")) o.inputstyle = "apply" function o.write(t, n) luci.sys.call("lua /usr/share/" .. appname .. "/subscribe.lua start > /dev/null 2>&1 &") + m.no_commit = true luci.http.redirect(api.url("log")) end @@ -151,17 +172,23 @@ o.validate = function(self, value, t) end end -o = s:option(DummyValue, "_node_count") +o = s:option(DummyValue, "_node_count", translate("Subscribe Info")) o.rawhtml = true o.cfgvalue = function(t, n) local remark = m:get(n, "remark") or "" + local str = m:get(n, "rem_traffic") or "" + local expired_date = m:get(n, "expired_date") or "" + if expired_date ~= "" then + str = str .. (str ~= "" and "/" or "") .. expired_date + end + str = str ~= "" and "
" .. str or "" local num = 0 m.uci:foreach(appname, "nodes", function(s) if s["add_from"] ~= "" and s["add_from"] == remark then num = num + 1 end end) - return string.format("%s", remark .. " " .. translate("Node num") .. ": " .. num, num) + return string.format("%s%s", translate("Node num") .. ": " .. num, str) end o = s:option(Value, "url", translate("Subscribe URL")) @@ -173,12 +200,14 @@ o.inputstyle = "remove" function o.write(t, n) local remark = m:get(n, "remark") or "" luci.sys.call("lua /usr/share/" .. appname .. "/subscribe.lua truncate " .. remark .. " > /dev/null 2>&1") + m.no_commit = true end o = s:option(Button, "_update", translate("Manual subscription")) o.inputstyle = "apply" function o.write(t, n) luci.sys.call("lua /usr/share/" .. appname .. "/subscribe.lua start " .. n .. " > /dev/null 2>&1 &") + m.no_commit = true luci.http.redirect(api.url("log")) end diff --git a/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua b/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua index 64d37e2897..a2b17111b8 100644 --- a/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua +++ b/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua @@ -1,5 +1,14 @@ local api = require "luci.passwall2.api" local appname = api.appname + +m = Map(appname) +m.redirect = api.url("node_subscribe") +api.set_apply_on_parse(m) + +if not arg[1] or not m:get(arg[1]) then + luci.http.redirect(m.redirect) +end + local has_ss = api.is_finded("ss-redir") local has_ss_rust = api.is_finded("sslocal") local has_singbox = api.finded_com("singbox") @@ -38,14 +47,14 @@ if has_hysteria2 then table.insert(hysteria2_type, s) end -m = Map(appname) -m.redirect = api.url("node_subscribe") -api.set_apply_on_parse(m) - s = m:section(NamedSection, arg[1]) s.addremove = false s.dynamic = false +function m.commit_handler(self) + self:del(arg[1], "md5") +end + o = s:option(Value, "remark", translate("Subscribe Remark")) o.rmempty = false diff --git a/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/other.lua b/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/other.lua index 2f66f7616c..6fbeb069f9 100644 --- a/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/other.lua +++ b/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/other.lua @@ -111,13 +111,16 @@ if (os.execute("lsmod | grep -i REDIRECT >/dev/null") == 0 and os.execute("lsmod o:value("redirect", "REDIRECT") o:value("tproxy", "TPROXY") o:depends("ipv6_tproxy", false) + o.remove = function(self, section) + -- 禁止在隐藏时删除 + end o = s:option(ListValue, "_tcp_proxy_way", translate("TCP Proxy Way")) o.default = "tproxy" o:value("tproxy", "TPROXY") o:depends("ipv6_tproxy", true) o.write = function(self, section, value) - return self.map:set(section, "tcp_proxy_way", value) + self.map:set(section, "tcp_proxy_way", value) end if os.execute("lsmod | grep -i ip6table_mangle >/dev/null") == 0 or os.execute("lsmod | grep -i nft_tproxy >/dev/null") == 0 then @@ -210,6 +213,7 @@ if has_xray then o = s_xray_noise:option(ListValue, "type", translate("Type")) o:value("rand", "rand") o:value("str", "str") + o:value("hex", "hex") o:value("base64", "base64") o = s_xray_noise:option(Value, "packet", translate("Packet")) diff --git a/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/socks_config.lua b/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/socks_config.lua index fa87ec0452..c970d0f106 100644 --- a/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/socks_config.lua +++ b/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/socks_config.lua @@ -1,11 +1,16 @@ local api = require "luci.passwall2.api" local appname = api.appname -local has_singbox = api.finded_com("singbox") -local has_xray = api.finded_com("xray") m = Map(appname) api.set_apply_on_parse(m) +if not arg[1] or not m:get(arg[1]) then + luci.http.redirect(api.url()) +end + +local has_singbox = api.finded_com("singbox") +local has_xray = api.finded_com("xray") + local nodes_table = {} for k, e in ipairs(api.get_valid_nodes()) do nodes_table[#nodes_table + 1] = e diff --git a/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua b/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua index b10af78282..1252194976 100644 --- a/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua +++ b/small/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua @@ -569,14 +569,16 @@ o = s:option(Value, _n("xhttp_path"), translate("XHTTP Path")) o.placeholder = "/" o:depends({ [_n("transport")] = "xhttp" }) -o = s:option(TextValue, _n("xhttp_extra"), translate("XHTTP Extra"), translate("An XHTTP extra object in raw json")) +o = s:option(Flag, _n("use_xhttp_extra"), translate("XHTTP Extra")) +o.default = "0" o:depends({ [_n("transport")] = "xhttp" }) + +o = s:option(TextValue, _n("xhttp_extra"), " ", translate("An XHttpObject in JSON format, used for sharing.")) +o:depends({ [_n("use_xhttp_extra")] = true }) o.rows = 15 o.wrap = "off" o.custom_write = function(self, section, value) - m:set(section, self.option:sub(1 + #option_prefix), value) - local success, data = pcall(jsonc.parse, value) if success and data then local address = (data.extra and data.extra.downloadSettings and data.extra.downloadSettings.address) @@ -597,6 +599,10 @@ o.validate = function(self, value) end return value end +o.custom_remove = function(self, section, value) + m:del(section, self.option:sub(1 + #option_prefix)) + m:del(section, "download_address") +end -- [[ Mux.Cool ]]-- o = s:option(Flag, _n("mux"), "Mux", translate("Enable Mux.Cool")) diff --git a/small/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ray.lua b/small/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ray.lua index ad3d2204a5..d508d71551 100644 --- a/small/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ray.lua +++ b/small/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ray.lua @@ -149,8 +149,26 @@ o = s:option(Value, _n("reality_dest"), translate("Dest")) o.default = "google.com:443" o:depends({ [_n("reality")] = true }) -o = s:option(Value, _n("reality_serverNames"), translate("serverNames")) +o = s:option(DynamicList, _n("reality_serverNames"), translate("serverNames")) o:depends({ [_n("reality")] = true }) +function o.write(self, section, value) + local t = {} + local t2 = {} + if type(value) == "table" then + local x + for _, x in ipairs(value) do + if x and #x > 0 then + if not t2[x] then + t2[x] = x + t[#t+1] = x + end + end + end + else + t = { value } + end + return DynamicList.write(self, section, t) +end o = s:option(ListValue, _n("alpn"), translate("alpn")) o.default = "h2,http/1.1" diff --git a/small/luci-app-passwall2/luasrc/passwall2/api.lua b/small/luci-app-passwall2/luasrc/passwall2/api.lua index 5dd8468d76..3499e1a128 100644 --- a/small/luci-app-passwall2/luasrc/passwall2/api.lua +++ b/small/luci-app-passwall2/luasrc/passwall2/api.lua @@ -1232,11 +1232,16 @@ function luci_types(id, m, s, type_name, option_prefix) end s.fields[key].remove = function(self, section) if s.fields["type"]:formvalue(id) == type_name then - if self.rewrite_option and rewrite_option_table[self.rewrite_option] == 1 then - m:del(section, self.rewrite_option) + -- 添加自定义 custom_remove 属性,如果有自定义的 custom_remove 函数,则使用自定义的 remove 逻辑 + if self.custom_remove then + self:custom_remove(section) else - if self.option:find(option_prefix) == 1 then - m:del(section, self.option:sub(1 + #option_prefix)) + if self.rewrite_option and rewrite_option_table[self.rewrite_option] == 1 then + m:del(section, self.rewrite_option) + else + if self.option:find(option_prefix) == 1 then + m:del(section, self.option:sub(1 + #option_prefix)) + end end end end diff --git a/small/luci-app-passwall2/luasrc/passwall2/util_xray.lua b/small/luci-app-passwall2/luasrc/passwall2/util_xray.lua index f7dd5c35ed..d8cbd949d7 100644 --- a/small/luci-app-passwall2/luasrc/passwall2/util_xray.lua +++ b/small/luci-app-passwall2/luasrc/passwall2/util_xray.lua @@ -533,9 +533,7 @@ function gen_config_server(node) config.inbounds[1].streamSettings.realitySettings = { show = false, dest = node.reality_dest, - serverNames = { - node.reality_serverNames - }, + serverNames = node.reality_serverNames or {}, privateKey = node.reality_private_key, shortIds = node.reality_shortId or "" } or nil diff --git a/small/luci-app-passwall2/luasrc/view/passwall2/global/faq.htm b/small/luci-app-passwall2/luasrc/view/passwall2/global/faq.htm index 08ead501f9..ae9d0aa8e6 100644 --- a/small/luci-app-passwall2/luasrc/view/passwall2/global/faq.htm +++ b/small/luci-app-passwall2/luasrc/view/passwall2/global/faq.htm @@ -47,16 +47,9 @@ local api = require "luci.passwall2.api" diff --git a/small/luci-app-passwall2/luasrc/view/passwall2/node_list/link_share_man.htm b/small/luci-app-passwall2/luasrc/view/passwall2/node_list/link_share_man.htm index 2cd22fb76c..27b1aec7b7 100644 --- a/small/luci-app-passwall2/luasrc/view/passwall2/node_list/link_share_man.htm +++ b/small/luci-app-passwall2/luasrc/view/passwall2/node_list/link_share_man.htm @@ -379,7 +379,13 @@ local hysteria2_type = map:get("@global_subscribe[0]", "hysteria2_type") or "sin params += opt.query("host", dom_prefix + "xhttp_host"); params += opt.query("path", dom_prefix + "xhttp_path"); params += opt.query("mode", dom_prefix + "xhttp_mode"); - params += opt.query("extra", dom_prefix + "xhttp_extra"); + if (opt.get(dom_prefix + "use_xhttp_extra").checked) { + params += opt.query("extra", dom_prefix + "xhttp_extra"); + } + } else if (v_transport === "httpupgrade") { + v_transport = "httpupgrade"; + params += opt.query("host", dom_prefix + "httpupgrade_host"); + params += opt.query("path", dom_prefix + "httpupgrade_path"); } params += "&type=" + v_transport; @@ -1245,7 +1251,11 @@ local hysteria2_type = map:get("@global_subscribe[0]", "hysteria2_type") or "sin opt.set(dom_prefix + 'xhttp_host', queryParam.host || ""); opt.set(dom_prefix + 'xhttp_path', queryParam.path || ""); opt.set(dom_prefix + 'xhttp_mode', queryParam.mode || "auto"); + opt.set(dom_prefix + 'use_xhttp_extra', !!queryParam.extra); opt.set(dom_prefix + 'xhttp_extra', queryParam.extra || ""); + } else if (queryParam.type === "httpupgrade") { + opt.set(dom_prefix + 'httpupgrade_host', queryParam.host || ""); + opt.set(dom_prefix + 'httpupgrade_path', queryParam.path || ""); } if (m.hash) { diff --git a/small/luci-app-passwall2/luasrc/view/passwall2/node_list/node_list.htm b/small/luci-app-passwall2/luasrc/view/passwall2/node_list/node_list.htm index 70db75fc1c..48066f7628 100644 --- a/small/luci-app-passwall2/luasrc/view/passwall2/node_list/node_list.htm +++ b/small/luci-app-passwall2/luasrc/view/passwall2/node_list/node_list.htm @@ -46,9 +46,19 @@ table td, .table .td { color: red !important; } +._now_use_bg { + background: #5e72e445 !important; +} + .ping a:hover{ text-decoration : underline; } + +@media (prefers-color-scheme: dark) { + ._now_use_bg { + background: #4a90e2 !important; + } +}
" data-index="<%=self.index%>" data-depends="<%=pcdata(self:deplist2json(section))%>"> - - -
\ No newline at end of file + +
+ + +
+ diff --git a/small/luci-app-passwall2/po/zh-cn/passwall2.po b/small/luci-app-passwall2/po/zh-cn/passwall2.po index a2121e55d6..8950086d07 100644 --- a/small/luci-app-passwall2/po/zh-cn/passwall2.po +++ b/small/luci-app-passwall2/po/zh-cn/passwall2.po @@ -46,9 +46,6 @@ msgstr "规则列表" msgid "Access control" msgstr "访问控制" -msgid "Watch Logs" -msgstr "查看日志" - msgid "Node Config" msgstr "节点配置" @@ -199,18 +196,12 @@ msgstr "客户端DNS和默认网关必须指向本路由器。" msgid "If you have a wrong DNS process, the consequences are at your own risk!" msgstr "如果你自行配置了错误的DNS流程,后果自负!" -msgid "Restore the default configuration method. Input example in the address bar:" -msgstr "恢复默认配置方法,地址栏输入例:" - msgid "Hide menu method, input example in the address bar:" msgstr "隐藏菜单方法,地址栏输入例:" msgid "After the hidden to the display, input example in the address bar:" msgstr "当你隐藏后想再次显示,地址栏输入例:" -msgid "Are you sure to reset?" -msgstr "你确定要恢复吗?" - msgid "Are you sure to hide?" msgstr "你确定要隐藏吗?" @@ -235,9 +226,6 @@ msgstr "对于移动设备,可通过重新接入网络的方式清除。比如 msgid "Please make sure your device's network settings point both the DNS server and default gateway to this router, to ensure DNS queries are properly routed." msgstr "请确认您设备的网络设置,客户端DNS服务器和默认网关应均指向本路由器,以确保DNS查询正确路由。" -msgid "Restore to default configuration:" -msgstr "恢复默认配置:" - msgid "Browser access:" msgstr "浏览器访问:" @@ -718,6 +706,9 @@ msgstr "请输入节点关键字,注意区分空格、大写和小写。" msgid "Enable Load Balancing" msgstr "开启负载均衡" +msgid "Console Login Auth" +msgstr "控制台登录认证" + msgid "Console Username" msgstr "控制台账号" @@ -919,6 +910,9 @@ msgstr "节点订阅" msgid "Subscribe Remark" msgstr "订阅备注(机场)" +msgid "Subscribe Info" +msgstr "订阅信息" + msgid "Subscribe URL" msgstr "订阅网址" @@ -1417,8 +1411,8 @@ msgstr "客户端文件不适合当前设备。" msgid "Can't move new file to path: %s" msgstr "无法移动新文件到:%s" -msgid "An XHTTP extra object in raw json" -msgstr "一个 json 格式的 XHTTP extra object" +msgid "An XHttpObject in JSON format, used for sharing." +msgstr "JSON 格式的 XHttpObject,用来实现分享。" msgid "Enable Mux.Cool" msgstr "启用 Mux.Cool" @@ -1611,3 +1605,45 @@ msgstr "仅 IPv4" msgid "IPv6 Only" msgstr "仅 IPv6" + +msgid "Log Maint" +msgstr "日志维护" + +msgid "Backup and Restore" +msgstr "备份还原" + +msgid "Backup or Restore Client and Server Configurations." +msgstr "备份或还原客户端及服务端配置。" + +msgid "Note: Restoring configurations across different versions may cause compatibility issues." +msgstr "注意:不同版本间的配置恢复可能会导致兼容性问题。" + +msgid "Create Backup File" +msgstr "创建备份文件" + +msgid "Restore Backup File" +msgstr "恢复备份文件" + +msgid "DL Backup" +msgstr "下载备份" + +msgid "RST Backup" +msgstr "恢复备份" + +msgid "UL Restore" +msgstr "上传恢复" + +msgid "CLOSE WIN" +msgstr "关闭窗口" + +msgid "Restore to default configuration" +msgstr "恢复默认配置" + +msgid "Do Reset" +msgstr "执行重置" + +msgid "Do you want to restore the client to default settings?" +msgstr "是否要恢复客户端默认配置?" + +msgid "Are you sure you want to restore the client to default settings?" +msgstr "是否真的要恢复客户端默认配置?" diff --git a/small/luci-app-passwall2/root/usr/share/passwall2/0_default_config b/small/luci-app-passwall2/root/usr/share/passwall2/0_default_config index 83e60ae887..79dce26964 100644 --- a/small/luci-app-passwall2/root/usr/share/passwall2/0_default_config +++ b/small/luci-app-passwall2/root/usr/share/passwall2/0_default_config @@ -55,6 +55,8 @@ config global_app config global_subscribe option filter_keyword_mode '1' + list filter_discard_list '距离下次重置剩余' + list filter_discard_list '套餐到期' list filter_discard_list '过期时间' list filter_discard_list '剩余流量' list filter_discard_list 'QQ群' diff --git a/small/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua b/small/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua index 10d6ddf11c..ee6d4c129a 100755 --- a/small/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua +++ b/small/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua @@ -379,6 +379,24 @@ local function trim(text) return (sgsub(text, "^%s*(.-)%s*$", "%1")) end +-- 取机场信息(剩余流量、到期时间) +local subscribe_info = {} +local function get_subscribe_info(cfgid, value) + if type(cfgid) ~= "string" or cfgid == "" or type(value) ~= "string" then + return + end + value = value:gsub("%s+", "") + local expired_date = value:match("套餐到期:(.+)") + local rem_traffic = value:match("剩余流量:(.+)") + subscribe_info[cfgid] = subscribe_info[cfgid] or {expired_date = "", rem_traffic = ""} + if expired_date then + subscribe_info[cfgid]["expired_date"] = expired_date + end + if rem_traffic then + subscribe_info[cfgid]["rem_traffic"] = rem_traffic + end +end + -- 处理数据 local function processData(szType, content, add_mode, add_from) --log(content, add_mode, add_from) @@ -519,6 +537,10 @@ local function processData(szType, content, add_mode, add_from) result.download_address = nil end end + if info.net == 'httpupgrade' then + result.httpupgrade_host = info.host + result.httpupgrade_path = info.path + end if not info.security then result.security = "auto" end if info.tls == "tls" or info.tls == "1" then result.tls = "1" @@ -882,6 +904,10 @@ local function processData(szType, content, add_mode, add_from) result.xhttp_host = params.host result.xhttp_path = params.path end + if params.type == 'httpupgrade' then + result.httpupgrade_host = params.host + result.httpupgrade_path = params.path + end result.encryption = params.encryption or "none" @@ -1021,6 +1047,21 @@ local function processData(szType, content, add_mode, add_from) if params.type == 'xhttp' or params.type == 'splithttp' then result.xhttp_host = params.host result.xhttp_path = params.path + result.xhttp_mode = params.mode or "auto" + result.use_xhttp_extra = (params.extra and params.extra ~= "") and "1" or nil + result.xhttp_extra = (params.extra and params.extra ~= "") and params.extra or nil + local success, Data = pcall(jsonParse, params.extra) + if success and Data then + local address = (Data.extra and Data.extra.downloadSettings and Data.extra.downloadSettings.address) + or (Data.downloadSettings and Data.downloadSettings.address) + result.download_address = address and address ~= "" and address or nil + else + result.download_address = nil + end + end + if params.type == 'httpupgrade' then + result.httpupgrade_host = params.host + result.httpupgrade_path = params.path end result.encryption = params.encryption or "none" @@ -1279,7 +1320,7 @@ local function truncate_nodes(add_from) end) if add_from then uci:foreach(appname, "subscribe_list", function(o) - if o.remark == add_from then + if add_from == "all-node" or add_from == o.remark then uci:delete(appname, o['.name'], "md5") end end) @@ -1438,6 +1479,16 @@ local function update_node(manual) end end end + -- 更新机场信息 + for cfgid, info in pairs(subscribe_info) do + for key, value in pairs(info) do + if value ~= "" then + uci:set(appname, cfgid, key, value) + else + uci:delete(appname, cfgid, key) + end + end + end api.uci_save(uci, appname, true) if next(CONFIG) then @@ -1469,7 +1520,7 @@ local function update_node(manual) luci.sys.call("/etc/init.d/" .. appname .. " restart > /dev/null 2>&1 &") end -local function parse_link(raw, add_mode, add_from) +local function parse_link(raw, add_mode, add_from, cfgid) if raw and #raw > 0 then local nodes, szType local node_list = {} @@ -1531,6 +1582,9 @@ local function parse_link(raw, add_mode, add_from) else tinsert(node_list, result) end + if add_mode == "2" then + get_subscribe_info(cfgid, result.remarks) + end end end, function (err) --log(err) @@ -1634,7 +1688,7 @@ local execute = function() log('订阅:【' .. remark .. '】没有变化,无需更新。') else os.remove("/tmp/" .. cfgid) - parse_link(raw, "2", remark) + parse_link(raw, "2", remark, cfgid) uci:set(appname, cfgid, "md5", new_md5) end else diff --git a/v2ray-core/go.mod b/v2ray-core/go.mod index bdbd8db4eb..7be86c13a8 100644 --- a/v2ray-core/go.mod +++ b/v2ray-core/go.mod @@ -24,7 +24,7 @@ require ( github.com/pion/dtls/v2 v2.2.12 github.com/pion/transport/v2 v2.2.10 github.com/pires/go-proxyproto v0.8.0 - github.com/quic-go/quic-go v0.48.2 + github.com/quic-go/quic-go v0.49.0 github.com/refraction-networking/utls v1.6.7 github.com/seiflotfy/cuckoofilter v0.0.0-20220411075957-e3b120b3f5fb github.com/stretchr/testify v1.10.0 @@ -86,7 +86,7 @@ require ( github.com/secure-io/siv-go v0.0.0-20180922214919-5ff40651e2c4 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/xtaci/smux v1.5.24 // indirect - go.uber.org/mock v0.4.0 // indirect + go.uber.org/mock v0.5.0 // indirect golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect golang.org/x/mod v0.18.0 // indirect golang.org/x/text v0.21.0 // indirect diff --git a/v2ray-core/go.sum b/v2ray-core/go.sum index f5976fb414..59669177f3 100644 --- a/v2ray-core/go.sum +++ b/v2ray-core/go.sum @@ -442,8 +442,8 @@ github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= -github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE= -github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs= +github.com/quic-go/quic-go v0.49.0 h1:w5iJHXwHxs1QxyBv1EHKuC50GX5to8mJAxvtnttJp94= +github.com/quic-go/quic-go v0.49.0/go.mod h1:s2wDnmCdooUQBmQfpUSTCYBl1/D4FcqbULMMkASvR6s= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM= github.com/refraction-networking/utls v1.6.7/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0= @@ -567,8 +567,8 @@ go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= -go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= -go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= diff --git a/xray-core/README.md b/xray-core/README.md index 570237e429..033569b685 100644 --- a/xray-core/README.md +++ b/xray-core/README.md @@ -98,6 +98,7 @@ - [Shadowrocket](https://apps.apple.com/app/shadowrocket/id932747118) - Xray Tools - [xray-knife](https://github.com/lilendian0x00/xray-knife) + - [xray-checker](https://github.com/kutovoys/xray-checker) - Xray Wrapper - [XTLS/libXray](https://github.com/XTLS/libXray) - [xtlsapi](https://github.com/hiddify/xtlsapi) diff --git a/xray-core/app/dns/nameserver_quic.go b/xray-core/app/dns/nameserver_quic.go index 0691fac99f..997635a8ff 100644 --- a/xray-core/app/dns/nameserver_quic.go +++ b/xray-core/app/dns/nameserver_quic.go @@ -8,7 +8,7 @@ import ( "sync" "time" - "github.com/xtls/quic-go" + "github.com/quic-go/quic-go" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/errors" diff --git a/xray-core/app/proxyman/inbound/worker.go b/xray-core/app/proxyman/inbound/worker.go index 14df725dc6..6ef8cca4b7 100644 --- a/xray-core/app/proxyman/inbound/worker.go +++ b/xray-core/app/proxyman/inbound/worker.go @@ -2,6 +2,7 @@ package inbound import ( "context" + "strings" "sync" "sync/atomic" "time" @@ -463,9 +464,19 @@ func (w *dsWorker) callback(conn stat.Connection) { WriteCounter: w.downlinkCounter, } } + // For most of time, unix obviously have no source addr. But if we leave it empty, it will cause panic. + // So we use gateway as source for log. + // However, there are some special situations where a valid source address might be available. + // Such as the source address parsed from X-Forwarded-For in websocket. + // In that case, we keep it. + var source net.Destination + if !strings.Contains(conn.RemoteAddr().String(), "unix") { + source = net.DestinationFromAddr(conn.RemoteAddr()) + } else { + source = net.UnixDestination(w.address) + } ctx = session.ContextWithInbound(ctx, &session.Inbound{ - // Unix have no source addr, so we use gateway as source for log. - Source: net.UnixDestination(w.address), + Source: source, Gateway: net.UnixDestination(w.address), Tag: w.tag, Conn: conn, diff --git a/xray-core/common/protocol/quic/sniff.go b/xray-core/common/protocol/quic/sniff.go index 855ef9f782..779e291bd3 100644 --- a/xray-core/common/protocol/quic/sniff.go +++ b/xray-core/common/protocol/quic/sniff.go @@ -8,7 +8,7 @@ import ( "encoding/binary" "io" - "github.com/xtls/quic-go/quicvarint" + "github.com/quic-go/quic-go/quicvarint" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/bytespool" diff --git a/xray-core/go.mod b/xray-core/go.mod index 9491e051fa..dd93bf5cf3 100644 --- a/xray-core/go.mod +++ b/xray-core/go.mod @@ -12,6 +12,7 @@ require ( github.com/miekg/dns v1.1.62 github.com/pelletier/go-toml v1.9.5 github.com/pires/go-proxyproto v0.8.0 + github.com/quic-go/quic-go v0.49.0 github.com/refraction-networking/utls v1.6.7 github.com/sagernet/sing v0.5.1 github.com/sagernet/sing-shadowsocks v0.2.7 @@ -19,7 +20,6 @@ require ( github.com/stretchr/testify v1.10.0 github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e github.com/vishvananda/netlink v1.3.0 - github.com/xtls/quic-go v0.48.2 github.com/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d go4.org/netipx v0.0.0-20231129151722-fdeea329fbba golang.org/x/crypto v0.32.0 @@ -48,7 +48,7 @@ require ( github.com/quic-go/qpack v0.5.1 // indirect github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect github.com/vishvananda/netns v0.0.4 // indirect - go.uber.org/mock v0.4.0 // indirect + go.uber.org/mock v0.5.0 // indirect golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect golang.org/x/mod v0.21.0 // indirect golang.org/x/text v0.21.0 // indirect diff --git a/xray-core/go.sum b/xray-core/go.sum index 91fbd76a83..b33ef529a9 100644 --- a/xray-core/go.sum +++ b/xray-core/go.sum @@ -54,6 +54,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= +github.com/quic-go/quic-go v0.49.0 h1:w5iJHXwHxs1QxyBv1EHKuC50GX5to8mJAxvtnttJp94= +github.com/quic-go/quic-go v0.49.0/go.mod h1:s2wDnmCdooUQBmQfpUSTCYBl1/D4FcqbULMMkASvR6s= github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM= github.com/refraction-networking/utls v1.6.7/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg= @@ -74,8 +76,6 @@ github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQ github.com/vishvananda/netlink v1.3.0/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs= github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= -github.com/xtls/quic-go v0.48.2 h1:59Gs+E9qtc9s0uniXYDA649gNEZlMWcNpFLyp9jfkuE= -github.com/xtls/quic-go v0.48.2/go.mod h1:rcyY5J0JT+1d5pa5Y+FbCsXM7Zu79jE87ZSFOBfiH7Q= github.com/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d h1:+B97uD9uHLgAAulhigmys4BVwZZypzK7gPN3WtpgRJg= github.com/xtls/reality v0.0.0-20240712055506-48f0b2d5ed6d/go.mod h1:dm4y/1QwzjGaK17ofi0Vs6NpKAHegZky8qk6J2JJZAE= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= @@ -89,8 +89,8 @@ go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiy go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= -go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= -go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= diff --git a/xray-core/transport/internet/splithttp/dialer.go b/xray-core/transport/internet/splithttp/dialer.go index 6a276e8f52..b97c39e90f 100644 --- a/xray-core/transport/internet/splithttp/dialer.go +++ b/xray-core/transport/internet/splithttp/dialer.go @@ -13,8 +13,8 @@ import ( "sync/atomic" "time" - "github.com/xtls/quic-go" - "github.com/xtls/quic-go/http3" + "github.com/quic-go/quic-go" + "github.com/quic-go/quic-go/http3" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/errors" diff --git a/xray-core/transport/internet/splithttp/hub.go b/xray-core/transport/internet/splithttp/hub.go index 58aec803c1..a9aba35050 100644 --- a/xray-core/transport/internet/splithttp/hub.go +++ b/xray-core/transport/internet/splithttp/hub.go @@ -13,8 +13,8 @@ import ( "sync" "time" - "github.com/xtls/quic-go" - "github.com/xtls/quic-go/http3" + "github.com/quic-go/quic-go" + "github.com/quic-go/quic-go/http3" goreality "github.com/xtls/reality" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/errors" diff --git a/yt-dlp/.gitignore b/yt-dlp/.gitignore index fdd904f7fe..8fcd0de641 100644 --- a/yt-dlp/.gitignore +++ b/yt-dlp/.gitignore @@ -92,6 +92,7 @@ updates_key.pem *.class *.isorted *.stackdump +uv.lock # Generated AUTHORS diff --git a/yt-dlp/CONTRIBUTORS b/yt-dlp/CONTRIBUTORS index 0102264180..7376b18015 100644 --- a/yt-dlp/CONTRIBUTORS +++ b/yt-dlp/CONTRIBUTORS @@ -715,3 +715,24 @@ Crypto90 MutantPiggieGolem1 Sanceilaks Strkmn +0x9fff00 +4ft35t +7x11x13 +b5i +cotko +d3d9 +Dioarya +finch71 +hexahigh +InvalidUsernameException +jixunmoe +knackku +krandor +kvk-2015 +lonble +msm595 +n10dollar +NecroRomnt +pjrobertson +subsense +test20140 diff --git a/yt-dlp/Changelog.md b/yt-dlp/Changelog.md index b996d35f7a..3232c158b5 100644 --- a/yt-dlp/Changelog.md +++ b/yt-dlp/Changelog.md @@ -4,6 +4,60 @@ # To create a release, dispatch the https://github.com/yt-dlp/yt-dlp/actions/workflows/release.yml workflow on master --> +### 2025.01.26 + +#### Core changes +- [Fix float comparison values in format filters](https://github.com/yt-dlp/yt-dlp/commit/f7d071e8aa3bf67ed7e0f881e749ca9ab50b3f8f) ([#11880](https://github.com/yt-dlp/yt-dlp/issues/11880)) by [bashonly](https://github.com/bashonly), [Dioarya](https://github.com/Dioarya) +- **utils**: `sanitize_path`: [Fix some incorrect behavior](https://github.com/yt-dlp/yt-dlp/commit/fc12e724a3b4988cfc467d2981887dde48c26b69) ([#11923](https://github.com/yt-dlp/yt-dlp/issues/11923)) by [Grub4K](https://github.com/Grub4K) + +#### Extractor changes +- **1tv**: [Support sport1tv.ru domain](https://github.com/yt-dlp/yt-dlp/commit/61ae5dc34ac775d6c122575e21ef2153b1273a2b) ([#11889](https://github.com/yt-dlp/yt-dlp/issues/11889)) by [kvk-2015](https://github.com/kvk-2015) +- **abematv**: [Support season extraction](https://github.com/yt-dlp/yt-dlp/commit/c709cc41cbc16edc846e0a431cfa8508396d4cb6) ([#11771](https://github.com/yt-dlp/yt-dlp/issues/11771)) by [middlingphys](https://github.com/middlingphys) +- **bilibili** + - [Support space `/lists/` URLs](https://github.com/yt-dlp/yt-dlp/commit/465167910407449354eb48e9861efd0819f53eb5) ([#11964](https://github.com/yt-dlp/yt-dlp/issues/11964)) by [c-basalt](https://github.com/c-basalt) + - [Support space video list extraction without login](https://github.com/yt-dlp/yt-dlp/commit/78912ed9c81f109169b828c397294a6cf8eacf41) ([#12089](https://github.com/yt-dlp/yt-dlp/issues/12089)) by [grqz](https://github.com/grqz) +- **bilibilidynamic**: [Add extractor](https://github.com/yt-dlp/yt-dlp/commit/9676b05715b61c8c5dd5598871e60d8807fb1a86) ([#11838](https://github.com/yt-dlp/yt-dlp/issues/11838)) by [finch71](https://github.com/finch71), [grqz](https://github.com/grqz) +- **bluesky**: [Prefer source format](https://github.com/yt-dlp/yt-dlp/commit/ccda63934df7de2823f0834218c4254c7c4d2e4c) ([#12154](https://github.com/yt-dlp/yt-dlp/issues/12154)) by [0x9fff00](https://github.com/0x9fff00) +- **crunchyroll**: [Remove extractors](https://github.com/yt-dlp/yt-dlp/commit/ff44ed53061e065804da6275d182d7928cc03a5e) ([#12195](https://github.com/yt-dlp/yt-dlp/issues/12195)) by [seproDev](https://github.com/seproDev) +- **dropout**: [Fix extraction](https://github.com/yt-dlp/yt-dlp/commit/164368610456e2d96b279f8b120dea08f7b1d74f) ([#12102](https://github.com/yt-dlp/yt-dlp/issues/12102)) by [bashonly](https://github.com/bashonly) +- **eggs**: [Add extractors](https://github.com/yt-dlp/yt-dlp/commit/20c765d02385a105c8ef13b6f7a737491d29c19a) ([#11904](https://github.com/yt-dlp/yt-dlp/issues/11904)) by [seproDev](https://github.com/seproDev), [subsense](https://github.com/subsense) +- **funimation**: [Remove extractors](https://github.com/yt-dlp/yt-dlp/commit/cdcf1e86726b8fa44f7e7126bbf1c18e1798d25c) ([#12167](https://github.com/yt-dlp/yt-dlp/issues/12167)) by [doe1080](https://github.com/doe1080) +- **goodgame**: [Fix extractor](https://github.com/yt-dlp/yt-dlp/commit/e7cc02b14d8d323f805d14325a9c95593a170d28) ([#12173](https://github.com/yt-dlp/yt-dlp/issues/12173)) by [NecroRomnt](https://github.com/NecroRomnt) +- **lbry**: [Support signed URLs](https://github.com/yt-dlp/yt-dlp/commit/de30f652ffb7623500215f5906844f2ae0d92c7b) ([#12138](https://github.com/yt-dlp/yt-dlp/issues/12138)) by [seproDev](https://github.com/seproDev) +- **naver**: [Fix m3u8 formats extraction](https://github.com/yt-dlp/yt-dlp/commit/b3007c44cdac38187fc6600de76959a7079a44d1) ([#12037](https://github.com/yt-dlp/yt-dlp/issues/12037)) by [kclauhk](https://github.com/kclauhk) +- **nest**: [Add extractors](https://github.com/yt-dlp/yt-dlp/commit/1ef3ee7500c4ab8c26f7fdc5b0ad1da4d16eec8e) ([#11747](https://github.com/yt-dlp/yt-dlp/issues/11747)) by [pabs3](https://github.com/pabs3), [seproDev](https://github.com/seproDev) +- **niconico**: series: [Fix extractor](https://github.com/yt-dlp/yt-dlp/commit/bc88b904cd02314da41ce1b2fdf046d0680fe965) ([#11822](https://github.com/yt-dlp/yt-dlp/issues/11822)) by [test20140](https://github.com/test20140) +- **nrk** + - [Extract more formats](https://github.com/yt-dlp/yt-dlp/commit/89198bb23b4d03e0473ac408bfb50d67c2f71165) ([#12069](https://github.com/yt-dlp/yt-dlp/issues/12069)) by [hexahigh](https://github.com/hexahigh) + - [Fix extraction](https://github.com/yt-dlp/yt-dlp/commit/45732e2590a1bd0bc9608f5eb68c59341ca84f02) ([#12193](https://github.com/yt-dlp/yt-dlp/issues/12193)) by [hexahigh](https://github.com/hexahigh) +- **patreon**: [Extract attachment filename as `alt_title`](https://github.com/yt-dlp/yt-dlp/commit/e2e73b5c65593ec0a5e685663e6ec0f4aaffc1f1) ([#12000](https://github.com/yt-dlp/yt-dlp/issues/12000)) by [msm595](https://github.com/msm595) +- **pbs**: [Fix extractor](https://github.com/yt-dlp/yt-dlp/commit/13825ab77815ee6e1603abbecbb9f3795057b93c) ([#12024](https://github.com/yt-dlp/yt-dlp/issues/12024)) by [dirkf](https://github.com/dirkf), [krandor](https://github.com/krandor), [n10dollar](https://github.com/n10dollar) +- **piramidetv**: [Add extractors](https://github.com/yt-dlp/yt-dlp/commit/af2c821d74049b519895288aca23cee81fc4b049) ([#10777](https://github.com/yt-dlp/yt-dlp/issues/10777)) by [HobbyistDev](https://github.com/HobbyistDev), [kclauhk](https://github.com/kclauhk), [seproDev](https://github.com/seproDev) +- **redgifs**: [Support `/ifr/` URLs](https://github.com/yt-dlp/yt-dlp/commit/4850ce91d163579fa615c3c0d44c9bd64682c22b) ([#11805](https://github.com/yt-dlp/yt-dlp/issues/11805)) by [invertico](https://github.com/invertico) +- **rtvslo.si**: show: [Extract more metadata](https://github.com/yt-dlp/yt-dlp/commit/3fc46086562857d5493cbcff687f76e4e4ed303f) ([#12136](https://github.com/yt-dlp/yt-dlp/issues/12136)) by [cotko](https://github.com/cotko) +- **senategov**: [Fix extractors](https://github.com/yt-dlp/yt-dlp/commit/68221ecc87c6a3f3515757bac2a0f9674a38e3f2) ([#9361](https://github.com/yt-dlp/yt-dlp/issues/9361)) by [Grabien](https://github.com/Grabien), [seproDev](https://github.com/seproDev) +- **soundcloud** + - [Extract more metadata](https://github.com/yt-dlp/yt-dlp/commit/6d304133ab32bcd1eb78ff1467f1a41dd9b66c33) ([#11945](https://github.com/yt-dlp/yt-dlp/issues/11945)) by [7x11x13](https://github.com/7x11x13) + - user: [Add `/comments` page support](https://github.com/yt-dlp/yt-dlp/commit/7bfb4f72e490310d2681c7f4815218a2ebbc73ee) ([#11999](https://github.com/yt-dlp/yt-dlp/issues/11999)) by [7x11x13](https://github.com/7x11x13) +- **subsplash**: [Add extractors](https://github.com/yt-dlp/yt-dlp/commit/5d904b077d2f58ae44bdf208d2dcfcc3ff8347f5) ([#11054](https://github.com/yt-dlp/yt-dlp/issues/11054)) by [seproDev](https://github.com/seproDev), [subrat-lima](https://github.com/subrat-lima) +- **theatercomplextownppv**: [Support `live` URLs](https://github.com/yt-dlp/yt-dlp/commit/797d2472a299692e01ad1500e8c3b7bc1daa7fe4) ([#11720](https://github.com/yt-dlp/yt-dlp/issues/11720)) by [bashonly](https://github.com/bashonly) +- **vimeo**: [Fix thumbnail extraction](https://github.com/yt-dlp/yt-dlp/commit/9ff330948c92f6b2e1d9c928787362ab19cd6c62) ([#12142](https://github.com/yt-dlp/yt-dlp/issues/12142)) by [jixunmoe](https://github.com/jixunmoe) +- **vimp**: Playlist: [Add support for tags](https://github.com/yt-dlp/yt-dlp/commit/d4f5be1735c8feaeb3308666e0b878e9782f529d) ([#11688](https://github.com/yt-dlp/yt-dlp/issues/11688)) by [FestplattenSchnitzel](https://github.com/FestplattenSchnitzel) +- **weibo**: [Extend `_VALID_URL`](https://github.com/yt-dlp/yt-dlp/commit/a567f97b62ae9f6d6f5a9376c361512ab8dceda2) ([#12088](https://github.com/yt-dlp/yt-dlp/issues/12088)) by [4ft35t](https://github.com/4ft35t) +- **xhamster**: [Various improvements](https://github.com/yt-dlp/yt-dlp/commit/3b99a0f0e07f0120ab416f34a8f5ab75d4fdf1d1) ([#11738](https://github.com/yt-dlp/yt-dlp/issues/11738)) by [knackku](https://github.com/knackku) +- **xiaohongshu**: [Extract more formats](https://github.com/yt-dlp/yt-dlp/commit/f9f24ae376a9eaca777816479a4a29f6f0ce7681) ([#12147](https://github.com/yt-dlp/yt-dlp/issues/12147)) by [seproDev](https://github.com/seproDev) +- **youtube** + - [Download `tv` client Innertube config](https://github.com/yt-dlp/yt-dlp/commit/326fb1ffaf4e8349f1fe8ba2a81839652e044bff) ([#12168](https://github.com/yt-dlp/yt-dlp/issues/12168)) by [coletdjnz](https://github.com/coletdjnz) + - [Extract `media_type` for livestreams](https://github.com/yt-dlp/yt-dlp/commit/421bc72103d1faed473a451299cd17d6abb433bb) ([#11605](https://github.com/yt-dlp/yt-dlp/issues/11605)) by [nosoop](https://github.com/nosoop) + - [Restore convenience workarounds](https://github.com/yt-dlp/yt-dlp/commit/f0d4b8a5d6354b294bc9631cf15a7160b7bad5de) ([#12181](https://github.com/yt-dlp/yt-dlp/issues/12181)) by [bashonly](https://github.com/bashonly) + - [Update `ios` player client](https://github.com/yt-dlp/yt-dlp/commit/de82acf8769282ce321a86737ecc1d4bef0e82a7) ([#12155](https://github.com/yt-dlp/yt-dlp/issues/12155)) by [b5i](https://github.com/b5i) + - [Use different PO token for GVS and Player](https://github.com/yt-dlp/yt-dlp/commit/6b91d232e316efa406035915532eb126fbaeea38) ([#12090](https://github.com/yt-dlp/yt-dlp/issues/12090)) by [coletdjnz](https://github.com/coletdjnz) + - tab: [Improve shorts title extraction](https://github.com/yt-dlp/yt-dlp/commit/76ac023ff02f06e8c003d104f02a03deeddebdcd) ([#11997](https://github.com/yt-dlp/yt-dlp/issues/11997)) by [bashonly](https://github.com/bashonly), [d3d9](https://github.com/d3d9) +- **zdf**: [Fix extractors](https://github.com/yt-dlp/yt-dlp/commit/bb69f5dab79fb32c4ec0d50e05f7fa26d05d54ba) ([#11041](https://github.com/yt-dlp/yt-dlp/issues/11041)) by [InvalidUsernameException](https://github.com/InvalidUsernameException) + +#### Misc. changes +- **cleanup**: Miscellaneous: [3b45319](https://github.com/yt-dlp/yt-dlp/commit/3b4531934465580be22937fecbb6e1a3a9e2334f) by [bashonly](https://github.com/bashonly), [lonble](https://github.com/lonble), [pjrobertson](https://github.com/pjrobertson), [seproDev](https://github.com/seproDev) + ### 2025.01.15 #### Extractor changes diff --git a/yt-dlp/README.md b/yt-dlp/README.md index fd5547d36e..45c56434ab 100644 --- a/yt-dlp/README.md +++ b/yt-dlp/README.md @@ -1760,7 +1760,7 @@ $ yt-dlp --replace-in-metadata "title,uploader" "[ _]" "-" # EXTRACTOR ARGUMENTS -Some extractors accept additional arguments which can be passed using `--extractor-args KEY:ARGS`. `ARGS` is a `;` (semicolon) separated string of `ARG=VAL1,VAL2`. E.g. `--extractor-args "youtube:player-client=tv,mweb;formats=incomplete" --extractor-args "funimation:version=uncut"` +Some extractors accept additional arguments which can be passed using `--extractor-args KEY:ARGS`. `ARGS` is a `;` (semicolon) separated string of `ARG=VAL1,VAL2`. E.g. `--extractor-args "youtube:player-client=tv,mweb;formats=incomplete" --extractor-args "twitter:api=syndication"` Note: In CLI, `ARG` can use `-` instead of `_`; e.g. `youtube:player-client"` becomes `youtube:player_client"` @@ -1795,13 +1795,6 @@ The following extractors use this feature: * `is_live`: Bypass live HLS detection and manually set `live_status` - a value of `false` will set `not_live`, any other value (or no value) will set `is_live` * `impersonate`: Target(s) to try and impersonate with the initial webpage request; e.g. `generic:impersonate=safari,chrome-110`. Use `generic:impersonate` to impersonate any available target, and use `generic:impersonate=false` to disable impersonation (default) -#### funimation -* `language`: Audio languages to extract, e.g. `funimation:language=english,japanese` -* `version`: The video version to extract - `uncut` or `simulcast` - -#### crunchyrollbeta (Crunchyroll) -* `hardsub`: One or more hardsub versions to extract (in order of preference), or `all` (default: `None` = no hardsubs will be extracted), e.g. `crunchyrollbeta:hardsub=en-US,de-DE` - #### vikichannel * `video_types`: Types of videos to download - one or more of `episodes`, `movies`, `clips`, `trailers` diff --git a/yt-dlp/devscripts/changelog_override.json b/yt-dlp/devscripts/changelog_override.json index 079e2f7296..8aa7b7e2bc 100644 --- a/yt-dlp/devscripts/changelog_override.json +++ b/yt-dlp/devscripts/changelog_override.json @@ -239,5 +239,11 @@ "action": "add", "when": "52c0ffe40ad6e8404d93296f575007b05b04c686", "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)" + }, + { + "action": "change", + "when": "76ac023ff02f06e8c003d104f02a03deeddebdcd", + "short": "[ie/youtube:tab] Improve shorts title extraction (#11997)", + "authors": ["bashonly", "d3d9"] } ] diff --git a/yt-dlp/supportedsites.md b/yt-dlp/supportedsites.md index 1420742d17..70909ef002 100644 --- a/yt-dlp/supportedsites.md +++ b/yt-dlp/supportedsites.md @@ -171,6 +171,7 @@ - **BilibiliCheese** - **BilibiliCheeseSeason** - **BilibiliCollectionList** + - **BiliBiliDynamic** - **BilibiliFavoritesList** - **BiliBiliPlayer** - **BilibiliPlaylist** @@ -303,10 +304,6 @@ - **CrowdBunker** - **CrowdBunkerChannel** - **Crtvg** - - **crunchyroll**: [*crunchyroll*](## "netrc machine") - - **crunchyroll:artist**: [*crunchyroll*](## "netrc machine") - - **crunchyroll:music**: [*crunchyroll*](## "netrc machine") - - **crunchyroll:playlist**: [*crunchyroll*](## "netrc machine") - **CSpan**: C-SPAN - **CSpanCongress** - **CtsNews**: 華視新聞 @@ -393,6 +390,8 @@ - **Ebay** - **egghead:course**: egghead.io course - **egghead:lesson**: egghead.io lesson + - **eggs:artist** + - **eggs:single** - **EinsUndEinsTV**: [*1und1tv*](## "netrc machine") - **EinsUndEinsTVLive**: [*1und1tv*](## "netrc machine") - **EinsUndEinsTVRecordings**: [*1und1tv*](## "netrc machine") @@ -477,9 +476,6 @@ - **FrontendMastersCourse**: [*frontendmasters*](## "netrc machine") - **FrontendMastersLesson**: [*frontendmasters*](## "netrc machine") - **FujiTVFODPlus7** - - **Funimation**: [*funimation*](## "netrc machine") - - **funimation:page**: [*funimation*](## "netrc machine") - - **funimation:show**: [*funimation*](## "netrc machine") - **Funk** - **Funker530** - **Fux** @@ -892,6 +888,8 @@ - **nebula:video**: [*watchnebula*](## "netrc machine") - **NekoHacker** - **NerdCubedFeed** + - **Nest** + - **NestClip** - **netease:album**: 网易云音乐 - 专辑 - **netease:djradio**: 网易云音乐 - 电台 - **netease:mv**: 网易云音乐 - MV @@ -1071,6 +1069,8 @@ - **Pinkbike** - **Pinterest** - **PinterestCollection** + - **PiramideTV** + - **PiramideTVChannel** - **pixiv:sketch** - **pixiv:​sketch:user** - **Pladform** @@ -1396,6 +1396,8 @@ - **StretchInternet** - **Stripchat** - **stv:player** + - **Subsplash** + - **subsplash:playlist** - **Substack** - **SunPorno** - **sverigesradio:episode** diff --git a/yt-dlp/test/test_YoutubeDL.py b/yt-dlp/test/test_YoutubeDL.py index 6b022a7eaa..17e081bc6e 100644 --- a/yt-dlp/test/test_YoutubeDL.py +++ b/yt-dlp/test/test_YoutubeDL.py @@ -486,11 +486,11 @@ class TestFormatSelection(unittest.TestCase): def test_format_filtering(self): formats = [ - {'format_id': 'A', 'filesize': 500, 'width': 1000}, - {'format_id': 'B', 'filesize': 1000, 'width': 500}, - {'format_id': 'C', 'filesize': 1000, 'width': 400}, - {'format_id': 'D', 'filesize': 2000, 'width': 600}, - {'format_id': 'E', 'filesize': 3000}, + {'format_id': 'A', 'filesize': 500, 'width': 1000, 'aspect_ratio': 1.0}, + {'format_id': 'B', 'filesize': 1000, 'width': 500, 'aspect_ratio': 1.33}, + {'format_id': 'C', 'filesize': 1000, 'width': 400, 'aspect_ratio': 1.5}, + {'format_id': 'D', 'filesize': 2000, 'width': 600, 'aspect_ratio': 1.78}, + {'format_id': 'E', 'filesize': 3000, 'aspect_ratio': 0.56}, {'format_id': 'F'}, {'format_id': 'G', 'filesize': 1000000}, ] @@ -549,6 +549,31 @@ class TestFormatSelection(unittest.TestCase): ydl.process_ie_result(info_dict) self.assertEqual(ydl.downloaded_info_dicts, []) + ydl = YDL({'format': 'best[aspect_ratio=1]'}) + ydl.process_ie_result(info_dict) + downloaded = ydl.downloaded_info_dicts[0] + self.assertEqual(downloaded['format_id'], 'A') + + ydl = YDL({'format': 'all[aspect_ratio > 1.00]'}) + ydl.process_ie_result(info_dict) + downloaded_ids = [info['format_id'] for info in ydl.downloaded_info_dicts] + self.assertEqual(downloaded_ids, ['D', 'C', 'B']) + + ydl = YDL({'format': 'all[aspect_ratio < 1.00]'}) + ydl.process_ie_result(info_dict) + downloaded_ids = [info['format_id'] for info in ydl.downloaded_info_dicts] + self.assertEqual(downloaded_ids, ['E']) + + ydl = YDL({'format': 'best[aspect_ratio=1.5]'}) + ydl.process_ie_result(info_dict) + downloaded = ydl.downloaded_info_dicts[0] + self.assertEqual(downloaded['format_id'], 'C') + + ydl = YDL({'format': 'all[aspect_ratio!=1]'}) + ydl.process_ie_result(info_dict) + downloaded_ids = [info['format_id'] for info in ydl.downloaded_info_dicts] + self.assertEqual(downloaded_ids, ['E', 'D', 'C', 'B']) + @patch('yt_dlp.postprocessor.ffmpeg.FFmpegMergerPP.available', False) def test_default_format_spec_without_ffmpeg(self): ydl = YDL({}) diff --git a/yt-dlp/test/test_utils.py b/yt-dlp/test/test_utils.py index b3de14198e..8f81d0b1b7 100644 --- a/yt-dlp/test/test_utils.py +++ b/yt-dlp/test/test_utils.py @@ -249,17 +249,36 @@ class TestUtil(unittest.TestCase): self.assertEqual(sanitize_path('abc/def...'), 'abc\\def..#') self.assertEqual(sanitize_path('abc.../def'), 'abc..#\\def') self.assertEqual(sanitize_path('abc.../def...'), 'abc..#\\def..#') - - self.assertEqual(sanitize_path('../abc'), '..\\abc') - self.assertEqual(sanitize_path('../../abc'), '..\\..\\abc') - self.assertEqual(sanitize_path('./abc'), 'abc') - self.assertEqual(sanitize_path('./../abc'), '..\\abc') - - self.assertEqual(sanitize_path('\\abc'), '\\abc') - self.assertEqual(sanitize_path('C:abc'), 'C:abc') - self.assertEqual(sanitize_path('C:abc\\..\\'), 'C:..') self.assertEqual(sanitize_path('C:\\abc:%(title)s.%(ext)s'), 'C:\\abc#%(title)s.%(ext)s') + # Check with nt._path_normpath if available + try: + import nt + + nt_path_normpath = getattr(nt, '_path_normpath', None) + except Exception: + nt_path_normpath = None + + for test, expected in [ + ('C:\\', 'C:\\'), + ('../abc', '..\\abc'), + ('../../abc', '..\\..\\abc'), + ('./abc', 'abc'), + ('./../abc', '..\\abc'), + ('\\abc', '\\abc'), + ('C:abc', 'C:abc'), + ('C:abc\\..\\', 'C:'), + ('C:abc\\..\\def\\..\\..\\', 'C:..'), + ('C:\\abc\\xyz///..\\def\\', 'C:\\abc\\def'), + ('abc/../', '.'), + ('./abc/../', '.'), + ]: + result = sanitize_path(test) + assert result == expected, f'{test} was incorrectly resolved' + assert result == sanitize_path(result), f'{test} changed after sanitizing again' + if nt_path_normpath: + assert result == nt_path_normpath(test), f'{test} does not match nt._path_normpath' + def test_sanitize_url(self): self.assertEqual(sanitize_url('//foo.bar'), 'http://foo.bar') self.assertEqual(sanitize_url('httpss://foo.bar'), 'https://foo.bar') diff --git a/yt-dlp/yt_dlp/YoutubeDL.py b/yt-dlp/yt_dlp/YoutubeDL.py index f6155dd2e9..b7b19cf6e0 100644 --- a/yt-dlp/yt_dlp/YoutubeDL.py +++ b/yt-dlp/yt_dlp/YoutubeDL.py @@ -2121,7 +2121,7 @@ class YoutubeDL: m = operator_rex.fullmatch(filter_spec) if m: try: - comparison_value = int(m.group('value')) + comparison_value = float(m.group('value')) except ValueError: comparison_value = parse_filesize(m.group('value')) if comparison_value is None: diff --git a/yt-dlp/yt_dlp/extractor/_extractors.py b/yt-dlp/yt_dlp/extractor/_extractors.py index aa8344bb29..2566a791c6 100644 --- a/yt-dlp/yt_dlp/extractor/_extractors.py +++ b/yt-dlp/yt_dlp/extractor/_extractors.py @@ -441,12 +441,6 @@ from .crowdbunker import ( CrowdBunkerIE, ) from .crtvg import CrtvgIE -from .crunchyroll import ( - CrunchyrollArtistIE, - CrunchyrollBetaIE, - CrunchyrollBetaShowIE, - CrunchyrollMusicIE, -) from .cspan import ( CSpanCongressIE, CSpanIE, @@ -705,11 +699,6 @@ from .frontendmasters import ( FrontendMastersLessonIE, ) from .fujitv import FujiTVFODPlus7IE -from .funimation import ( - FunimationIE, - FunimationPageIE, - FunimationShowIE, -) from .funk import FunkIE from .funker530 import Funker530IE from .fuyintv import FuyinTVIE diff --git a/yt-dlp/yt_dlp/extractor/abematv.py b/yt-dlp/yt_dlp/extractor/abematv.py index b1343eed39..8c7131b10a 100644 --- a/yt-dlp/yt_dlp/extractor/abematv.py +++ b/yt-dlp/yt_dlp/extractor/abematv.py @@ -421,14 +421,15 @@ class AbemaTVIE(AbemaTVBaseIE): class AbemaTVTitleIE(AbemaTVBaseIE): - _VALID_URL = r'https?://abema\.tv/video/title/(?P[^?/]+)' + _VALID_URL = r'https?://abema\.tv/video/title/(?P[^?/#]+)/?(?:\?(?:[^#]+&)?s=(?P[^&#]+))?' _PAGE_SIZE = 25 _TESTS = [{ - 'url': 'https://abema.tv/video/title/90-1597', + 'url': 'https://abema.tv/video/title/90-1887', 'info_dict': { - 'id': '90-1597', + 'id': '90-1887', 'title': 'シャッフルアイランド', + 'description': 'md5:61b2425308f41a5282a926edda66f178', }, 'playlist_mincount': 2, }, { @@ -436,41 +437,54 @@ class AbemaTVTitleIE(AbemaTVBaseIE): 'info_dict': { 'id': '193-132', 'title': '真心が届く~僕とスターのオフィス・ラブ!?~', + 'description': 'md5:9b59493d1f3a792bafbc7319258e7af8', }, 'playlist_mincount': 16, }, { - 'url': 'https://abema.tv/video/title/25-102', + 'url': 'https://abema.tv/video/title/25-1nzan-whrxe', 'info_dict': { - 'id': '25-102', - 'title': 'ソードアート・オンライン アリシゼーション', + 'id': '25-1nzan-whrxe', + 'title': 'ソードアート・オンライン', + 'description': 'md5:c094904052322e6978495532bdbf06e6', }, - 'playlist_mincount': 24, + 'playlist_mincount': 25, + }, { + 'url': 'https://abema.tv/video/title/26-2mzbynr-cph?s=26-2mzbynr-cph_s40', + 'info_dict': { + 'title': '〈物語〉シリーズ', + 'id': '26-2mzbynr-cph', + 'description': 'md5:e67873de1c88f360af1f0a4b84847a52', + }, + 'playlist_count': 59, }] - def _fetch_page(self, playlist_id, series_version, page): + def _fetch_page(self, playlist_id, series_version, season_id, page): + query = { + 'seriesVersion': series_version, + 'offset': str(page * self._PAGE_SIZE), + 'order': 'seq', + 'limit': str(self._PAGE_SIZE), + } + if season_id: + query['seasonId'] = season_id programs = self._call_api( f'v1/video/series/{playlist_id}/programs', playlist_id, note=f'Downloading page {page + 1}', - query={ - 'seriesVersion': series_version, - 'offset': str(page * self._PAGE_SIZE), - 'order': 'seq', - 'limit': str(self._PAGE_SIZE), - }) + query=query) yield from ( self.url_result(f'https://abema.tv/video/episode/{x}') for x in traverse_obj(programs, ('programs', ..., 'id'))) - def _entries(self, playlist_id, series_version): + def _entries(self, playlist_id, series_version, season_id): return OnDemandPagedList( - functools.partial(self._fetch_page, playlist_id, series_version), + functools.partial(self._fetch_page, playlist_id, series_version, season_id), self._PAGE_SIZE) def _real_extract(self, url): - playlist_id = self._match_id(url) + playlist_id, season_id = self._match_valid_url(url).group('id', 'season') series_info = self._call_api(f'v1/video/series/{playlist_id}', playlist_id) return self.playlist_result( - self._entries(playlist_id, series_info['version']), playlist_id=playlist_id, + self._entries(playlist_id, series_info['version'], season_id), playlist_id=playlist_id, playlist_title=series_info.get('title'), playlist_description=series_info.get('content')) diff --git a/yt-dlp/yt_dlp/extractor/bilibili.py b/yt-dlp/yt_dlp/extractor/bilibili.py index 33d9d92a0a..42b4e2d3c2 100644 --- a/yt-dlp/yt_dlp/extractor/bilibili.py +++ b/yt-dlp/yt_dlp/extractor/bilibili.py @@ -4,7 +4,9 @@ import hashlib import itertools import json import math +import random import re +import string import time import urllib.parse import uuid @@ -32,7 +34,6 @@ from ..utils import ( parse_qs, parse_resolution, qualities, - sanitize_url, smuggle_url, srt_subtitles_timecode, str_or_none, @@ -1178,28 +1179,26 @@ class BilibiliSpaceBaseIE(BilibiliBaseIE): class BilibiliSpaceVideoIE(BilibiliSpaceBaseIE): - _VALID_URL = r'https?://space\.bilibili\.com/(?P\d+)(?P