Update On Mon May 27 20:31:45 CEST 2024

This commit is contained in:
github-action[bot]
2024-05-27 20:31:46 +02:00
parent e2b04232bd
commit 929efb55e1
335 changed files with 3431 additions and 1742 deletions

1
.github/update.log vendored
View File

@@ -659,3 +659,4 @@ Update On Thu May 23 20:29:12 CEST 2024
Update On Fri May 24 20:29:24 CEST 2024
Update On Sat May 25 20:28:13 CEST 2024
Update On Sun May 26 20:29:29 CEST 2024
Update On Mon May 27 20:31:35 CEST 2024

View File

@@ -24,11 +24,12 @@
"@nyanpasu/ui": "workspace:^",
"@tauri-apps/api": "1.5.6",
"ahooks": "3.8.0",
"allotment": "1.20.2",
"axios": "1.7.2",
"dayjs": "1.11.11",
"framer-motion": "11.2.6",
"i18next": "23.11.5",
"jotai": "2.8.1",
"jotai": "2.8.2",
"monaco-editor": "0.49.0",
"mui-color-input": "2.0.3",
"react": "18.3.1",
@@ -55,6 +56,7 @@
"@typescript-eslint/eslint-plugin": "7.10.0",
"@typescript-eslint/parser": "7.10.0",
"@vitejs/plugin-react": "4.3.0",
"babel-plugin-react-compiler": "0.0.0-experimental-592953e-20240517",
"sass": "1.77.2",
"shiki": "1.6.0",
"vite": "5.2.11",

View File

@@ -0,0 +1,128 @@
import { Edit, Add } from "@mui/icons-material";
import {
ListItemButton,
alpha,
ListItemText,
IconButton,
ListItemIcon,
useTheme,
} from "@mui/material";
import { Profile, useClash } from "@nyanpasu/interface";
import { filterProfiles } from "../utils";
import { useLockFn } from "ahooks";
import { memo } from "react";
const ChainItem = memo(function ChainItem({
name,
desc,
selected,
onClick,
onChainEdit,
}: {
name?: string;
desc?: string;
selected?: boolean;
onClick: () => void;
onChainEdit: () => void;
}) {
const { palette } = useTheme();
return (
<ListItemButton
className="!mt-2 !mb-2"
sx={{
backgroundColor: selected
? alpha(palette.primary.main, 0.3)
: alpha(palette.secondary.main, 0.1),
borderRadius: 4,
"&:hover": {
backgroundColor: selected
? alpha(palette.primary.main, 0.5)
: undefined,
},
}}
onClick={onClick}
>
<ListItemText primary={name} secondary={desc} />
<IconButton
edge="end"
color="primary"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
onChainEdit();
}}
>
<Edit />
</IconButton>
</ListItemButton>
);
});
export interface SideChainProps {
global?: boolean;
profile?: Profile.Item;
onChainEdit: (item?: Profile.Item) => void | Promise<void>;
}
export const SideChain = ({ global, profile, onChainEdit }: SideChainProps) => {
const { palette } = useTheme();
const { getProfiles, setProfilesConfig, setProfiles } = useClash();
const { scripts } = filterProfiles(getProfiles.data?.items);
const handleChainClick = useLockFn(async (uid: string) => {
const chains = global
? getProfiles.data?.chain ?? []
: profile?.chains ?? [];
const updatedChains = chains.includes(uid)
? chains.filter((chain) => chain !== uid)
: [...chains, uid];
if (global) {
await setProfilesConfig({ chain: updatedChains });
} else {
await setProfiles(uid, { chains: updatedChains });
}
});
return (
<div className="!pl-2 !pr-2 overflow-auto h-full">
{scripts?.map((item, index) => {
const selected = global
? getProfiles.data?.chain?.includes(item.uid)
: profile?.chains?.includes(item.uid);
return (
<ChainItem
key={index}
name={item.name}
desc={item.desc}
selected={selected}
onClick={() => handleChainClick(item.uid)}
onChainEdit={() => onChainEdit(item)}
/>
);
})}
<ListItemButton
className="!mt-2 !mb-2"
sx={{
backgroundColor: alpha(palette.secondary.main, 0.1),
borderRadius: 4,
}}
onClick={() => onChainEdit()}
>
<ListItemIcon>
<Add color="primary" />
</ListItemIcon>
<ListItemText primary="New Chian" />
</ListItemButton>
</div>
);
};

View File

@@ -0,0 +1,77 @@
import { RamenDining, Terminal } from "@mui/icons-material";
import { Divider } from "@mui/material";
import { useClash } from "@nyanpasu/interface";
import { memo } from "react";
import { isEmpty } from "lodash-es";
import { VList } from "virtua";
import { filterProfiles } from "../utils";
import { classNames } from "@/utils";
const LogListItem = memo(function LogListItem({
name,
item,
showDivider,
}: {
name?: string;
item?: [string, string];
showDivider?: boolean;
}) {
return (
<>
{showDivider && <Divider />}
<div className="w-full font-mono break-all">
<span className="text-red-500">[{name}]: </span>
<span>{item}</span>
</div>
</>
);
});
export interface SideLogProps {
className?: string;
}
export const SideLog = ({ className }: SideLogProps) => {
const { getRuntimeLogs, getProfiles } = useClash();
const { scripts } = filterProfiles(getProfiles.data?.items);
return (
<div className={classNames("w-full", className)}>
<div className="p-2 pl-4 flex justify-between items-center">
<div className="flex items-center gap-2">
<Terminal />
<span>Console</span>
</div>
</div>
<Divider />
<VList className="flex flex-col gap-2 p-2 overflow-auto select-text">
{!isEmpty(getRuntimeLogs.data) ? (
Object.entries(getRuntimeLogs.data).map(([uid, content]) => {
return content.map((item, index) => {
const name = scripts?.find((script) => script.uid === uid)?.name;
return (
<LogListItem
key={uid + index}
name={name}
item={item}
showDivider={index !== 0}
/>
);
});
})
) : (
<div className="w-full h-full min-h-48 flex flex-col justify-center items-center">
<RamenDining className="!size-10" />
<p>No Log</p>
</div>
)}
</VList>
</div>
);
};

View File

@@ -1,22 +1,12 @@
import { Add, Close, Edit, RamenDining, Terminal } from "@mui/icons-material";
import {
Divider,
ListItemButton,
ListItemIcon,
ListItemText,
IconButton,
List,
useTheme,
alpha,
} from "@mui/material";
import { Profile, useClash } from "@nyanpasu/interface";
import { Close } from "@mui/icons-material";
import { IconButton } from "@mui/material";
import { Profile } from "@nyanpasu/interface";
import { useState } from "react";
import { filterProfiles } from "./utils";
import { useLockFn } from "ahooks";
import { Expand, ExpandMore } from "@nyanpasu/ui";
import { isEmpty } from "lodash-es";
import { ScriptDialog } from "./script-dialog";
import { VList } from "virtua";
import { SideLog } from "./modules/side-log";
import { Allotment } from "allotment";
import "allotment/dist/style.css";
import { SideChain } from "./modules/side-chain";
export interface ProfileSideProps {
profile?: Profile.Item;
@@ -25,113 +15,45 @@ export interface ProfileSideProps {
}
export const ProfileSide = ({ profile, global, onClose }: ProfileSideProps) => {
const { palette } = useTheme();
const [open, setOpen] = useState(false);
const { getProfiles, setProfilesConfig, getRuntimeLogs, setProfiles } =
useClash();
const { scripts } = filterProfiles(getProfiles.data?.items);
const handleChainClick = useLockFn(async (uid: string) => {
const chains = global
? getProfiles.data?.chain ?? []
: profile?.chains ?? [];
const updatedChains = chains.includes(uid)
? chains.filter((chain) => chain !== uid)
: [...chains, uid];
if (global) {
await setProfilesConfig({ chain: updatedChains });
} else {
await setProfiles(uid, { chains: updatedChains });
}
});
const [item, setItem] = useState<Profile.Item>();
const handleEditChain = async (item: Profile.Item) => {
const handleEditChain = async (item?: Profile.Item) => {
setItem(item);
setOpen(true);
};
const [expand, setExpand] = useState(false);
return (
<div className="relative h-full">
<div className="flex-col gap-2">
<div className="p-4 pr-2 flex justify-between items-start">
<div>
<div className="text-xl font-bold">Proxy Chains</div>
<>
<div className="p-4 pr-2 flex justify-between items-start">
<div>
<div className="text-xl font-bold">Proxy Chains</div>
<div className="truncate">
{global ? "Global Chain" : profile?.name}
</div>
<div className="truncate">
{global ? "Global Chain" : profile?.name}
</div>
<IconButton onClick={onClose}>
<Close />
</IconButton>
</div>
<List className="!pl-2 !pr-2 overflow-auto" disablePadding>
{scripts?.map((item, index) => {
const selected = global
? getProfiles.data?.chain?.includes(item.uid)
: profile?.chains?.includes(item.uid);
<IconButton onClick={onClose}>
<Close />
</IconButton>
</div>
return (
<ListItemButton
key={index}
className="!mt-2 !mb-2"
sx={{
backgroundColor: selected
? alpha(palette.primary.main, 0.3)
: alpha(palette.grey[100], 0.1),
borderRadius: 4,
<div style={{ height: "calc(100% - 84px)" }}>
<Allotment vertical defaultSizes={[1, 0]}>
<Allotment.Pane snap>
<SideChain
global={global}
profile={profile}
onChainEdit={handleEditChain}
/>
</Allotment.Pane>
"&:hover": {
backgroundColor: selected
? alpha(palette.primary.main, 0.5)
: undefined,
},
}}
onClick={() => handleChainClick(item.uid)}
>
<ListItemText primary={item.name} secondary={item.desc} />
<IconButton
edge="end"
color="primary"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
handleEditChain(item);
}}
>
<Edit />
</IconButton>
</ListItemButton>
);
})}
<ListItemButton
className="!mt-2!mb-2"
sx={{
backgroundColor: alpha(palette.grey[100], 0.1),
borderRadius: 4,
}}
onClick={() => setOpen(true)}
>
<ListItemIcon>
<Add color="primary" />
</ListItemIcon>
<ListItemText primary="New Chian" />
</ListItemButton>
</List>
<Allotment.Pane minSize={40}>
<SideLog className="h-full" />
</Allotment.Pane>
</Allotment>
</div>
<ScriptDialog
@@ -142,58 +64,7 @@ export const ProfileSide = ({ profile, global, onClose }: ProfileSideProps) => {
setItem(undefined);
}}
/>
<div className="absolute bottom-0 z-10 w-full">
<Divider />
<div className="p-1 pl-4 flex justify-between items-center">
<div className="flex items-center gap-2">
<Terminal />
<span>Console</span>
</div>
<ExpandMore
size="small"
reverse
expand={expand}
onClick={() => setExpand(!expand)}
/>
</div>
<Expand open={expand}>
<Divider />
<VList className="flex flex-col gap-2 p-2 overflow-auto min-h-48 max-h-48">
{!isEmpty(getRuntimeLogs.data) ? (
Object.entries(getRuntimeLogs.data).map(([uid, content]) => {
return content.map((item, index) => {
const name = scripts?.find(
(script) => script.uid === uid,
)?.name;
return (
<>
{index !== 0 && <Divider />}
<div key={uid + index} className="w-full font-mono">
<span className="text-red-500">[{name}]: </span>
<span>{item}</span>
</div>
</>
);
});
})
) : (
<div className="w-full h-full min-h-48 flex flex-col justify-center items-center">
<RamenDining className="!size-10" />
<p>No Log</p>
</div>
)}
</VList>
</Expand>
</div>
</div>
</>
);
};

View File

@@ -4,7 +4,17 @@ import "monaco-editor/esm/vs/editor/editor.all.js";
// langs
import "monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution.js";
import "monaco-editor/esm/vs/basic-languages/yaml/yaml.contribution.js";
import "monaco-editor/esm/vs/basic-languages/lua/lua.contribution.js";
// language services
import "monaco-editor/esm/vs/language/typescript/monaco.contribution.js";
import * as monaco from "monaco-editor/esm/vs/editor/editor.api";
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
target: monaco.languages.typescript.ScriptTarget.ES2020,
allowNonTsExtensions: true,
allowJs: true,
});
export { monaco };

View File

@@ -48,7 +48,17 @@ export default defineConfig(({ command }) => {
react({
// jsxImportSource: "@emotion/react",
babel: {
plugins: ["@emotion/babel-plugin"],
plugins: [
"@emotion/babel-plugin",
[
"babel-plugin-react-compiler",
{
sources: (filename) => {
return filename.indexOf("src") !== -1;
},
},
],
],
},
}),
generouted(),

View File

@@ -109,9 +109,9 @@
"tsx": "4.11.0",
"typescript": "5.4.5"
},
"packageManager": "pnpm@9.1.2+sha512.127dc83b9ea10c32be65d22a8efb4a65fb952e8fefbdfded39bdc3c97efc32d31b48b00420df2c1187ace28c921c902f0cb5a134a4d032b8b5295cbfa2c681e2",
"packageManager": "pnpm@9.1.3",
"engines": {
"node": "21.7.3"
"node": "22.2.0"
},
"pnpm": {
"overrides": {

View File

@@ -206,6 +206,9 @@ importers:
ahooks:
specifier: 3.8.0
version: 3.8.0(react@19.0.0-rc-935180c7e0-20240524)
allotment:
specifier: 1.20.2
version: 1.20.2(react-dom@19.0.0-rc-935180c7e0-20240524(react@19.0.0-rc-935180c7e0-20240524))(react@19.0.0-rc-935180c7e0-20240524)
axios:
specifier: 1.7.2
version: 1.7.2
@@ -219,8 +222,8 @@ importers:
specifier: 23.11.5
version: 23.11.5
jotai:
specifier: 2.8.1
version: 2.8.1(react@19.0.0-rc-935180c7e0-20240524)(types-react@19.0.0-rc.0)
specifier: 2.8.2
version: 2.8.2(react@19.0.0-rc-935180c7e0-20240524)(types-react@19.0.0-rc.0)
monaco-editor:
specifier: 0.49.0
version: 0.49.0
@@ -294,6 +297,9 @@ importers:
'@vitejs/plugin-react':
specifier: 4.3.0
version: 4.3.0(vite@5.2.11(@types/node@20.12.12)(less@4.2.0)(sass@1.77.2)(stylus@0.62.0))
babel-plugin-react-compiler:
specifier: 0.0.0-experimental-592953e-20240517
version: 0.0.0-experimental-592953e-20240517
sass:
specifier: 1.77.2
version: 1.77.2
@@ -427,6 +433,9 @@ packages:
resolution: {integrity: sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==}
engines: {node: '>=6.9.0'}
'@babel/generator@7.2.0':
resolution: {integrity: sha512-BA75MVfRlFQG2EZgFYIwyT1r6xSkwfP2bdkY/kLZusEYWiJs4xCowab/alaEaT0wSvmVuXGqiefeBlP+7V1yKg==}
'@babel/generator@7.24.5':
resolution: {integrity: sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA==}
engines: {node: '>=6.9.0'}
@@ -1039,6 +1048,10 @@ packages:
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'}
'@jest/types@24.9.0':
resolution: {integrity: sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==}
engines: {node: '>= 6'}
'@jridgewell/gen-mapping@0.3.5':
resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
engines: {node: '>=6.0.0'}
@@ -1548,6 +1561,15 @@ packages:
'@types/http-cache-semantics@4.0.4':
resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==}
'@types/istanbul-lib-coverage@2.0.6':
resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==}
'@types/istanbul-lib-report@3.0.3':
resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==}
'@types/istanbul-reports@1.1.2':
resolution: {integrity: sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==}
'@types/js-cookie@3.0.6':
resolution: {integrity: sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==}
@@ -1605,6 +1627,12 @@ packages:
'@types/unist@3.0.2':
resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==}
'@types/yargs-parser@21.0.3':
resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==}
'@types/yargs@13.0.12':
resolution: {integrity: sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==}
'@types/yauzl@2.10.3':
resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
@@ -1709,6 +1737,12 @@ packages:
ajv@8.13.0:
resolution: {integrity: sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==}
allotment@1.20.2:
resolution: {integrity: sha512-TaCuHfYNcsJS9EPk04M7TlG5Rl3vbAdHeAyrTE9D5vbpzV+wxnRoUrulDbfnzaQcPIZKpHJNixDOoZNuzliKEA==}
peerDependencies:
react: npm:react@rc
react-dom: npm:react-dom@rc
ansi-align@2.0.0:
resolution: {integrity: sha512-TdlOggdA/zURfMYa7ABC66j+oqfMew58KpJMbUlH3bcZP1b+cBHIHDDn5uH9INsxrHBPjsqM0tDB4jPTF/vgJA==}
@@ -1720,6 +1754,10 @@ packages:
resolution: {integrity: sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==}
engines: {node: '>=4'}
ansi-regex@4.1.1:
resolution: {integrity: sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==}
engines: {node: '>=6'}
ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
@@ -1822,6 +1860,9 @@ packages:
resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==}
engines: {node: '>=10', npm: '>=6'}
babel-plugin-react-compiler@0.0.0-experimental-592953e-20240517:
resolution: {integrity: sha512-OjG1SVaeQZaJrqkMFJatg8W/MTow8Ak5rx2SI0ETQBO1XvOk/XZGMbltNCPdFJLKghBYoBjC+Y3Ap/Xr7B01mA==}
bail@2.0.2:
resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==}
@@ -1957,6 +1998,9 @@ packages:
ci-info@1.6.0:
resolution: {integrity: sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==}
classnames@2.5.1:
resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==}
cli-boxes@1.0.0:
resolution: {integrity: sha512-3Fo5wu8Ytle8q9iCzS4D2MWVL2X7JVWRiS1BnXbTFDhS9c/REkM9vd1AmabsoZoY5/dGi5TT9iKL8Kb6DeBRQg==}
engines: {node: '>=0.10.0'}
@@ -2776,6 +2820,7 @@ packages:
glob@7.2.3:
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
deprecated: Glob versions prior to v9 are no longer supported
global-agent@3.0.0:
resolution: {integrity: sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==}
@@ -2981,6 +3026,9 @@ packages:
intersection-observer@0.12.2:
resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==}
invariant@2.2.4:
resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==}
ip-address@9.0.5:
resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==}
engines: {node: '>= 12'}
@@ -3202,8 +3250,8 @@ packages:
resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==}
hasBin: true
jotai@2.8.1:
resolution: {integrity: sha512-Gmk5Y3yJL/vN5S0rQ6AaWpXH5Q+HBGHThMHXfylVzXGVuO8YxPRtZf8Y9XYvl+h7ZMQXoHNdFi37vNsJFsiszQ==}
jotai@2.8.2:
resolution: {integrity: sha512-AU+EU82YqP94izfbGYQQL3oa/06gmn+Ijf/CKx0QybAURtbqh2e4N6zA2fxeIh0JEUgASF6z5IhagJ8NicR95A==}
engines: {node: '>=12.20.0'}
peerDependencies:
'@types/react': npm:types-react@rc
@@ -3340,6 +3388,15 @@ packages:
lodash.camelcase@4.3.0:
resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==}
lodash.clamp@4.0.3:
resolution: {integrity: sha512-HvzRFWjtcguTW7yd8NJBshuNaCa8aqNFtnswdT7f/cMd/1YKy5Zzoq4W/Oxvnx9l7aeY258uSdDfM793+eLsVg==}
lodash.debounce@4.0.8:
resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
lodash.isequal@4.5.0:
resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==}
lodash.isplainobject@4.0.6:
resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
@@ -4004,6 +4061,10 @@ packages:
engines: {node: '>=14'}
hasBin: true
pretty-format@24.9.0:
resolution: {integrity: sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==}
engines: {node: '>= 6'}
progress@2.0.3:
resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
engines: {node: '>=0.4.0'}
@@ -4240,6 +4301,7 @@ packages:
rimraf@3.0.2:
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
deprecated: Rimraf versions prior to v4 are no longer supported
hasBin: true
roarr@2.15.4:
@@ -4621,6 +4683,10 @@ packages:
trim-lines@3.0.1:
resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
trim-right@1.0.1:
resolution: {integrity: sha512-WZGXGstmCWgeevgTL54hrCuw1dyMQIzWy7ZfqRJfSmJZBwklI15egmQytFP6bPidmw3M8d5yEowl1niq4vmqZw==}
engines: {node: '>=0.10.0'}
trough@2.2.0:
resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==}
@@ -4790,6 +4856,12 @@ packages:
resolution: {integrity: sha512-BVA4lR5PIviy2PMseNd2jbFQ+jwSwQGdJejf5ctd1rEXt0Ypd7yanUK9+lYechVlN5VaTJGsu2U/3MDDu6KgBA==}
engines: {node: '>=0.10.0'}
use-resize-observer@9.1.0:
resolution: {integrity: sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow==}
peerDependencies:
react: npm:react@rc
react-dom: npm:react-dom@rc
use-sync-external-store@1.2.2:
resolution: {integrity: sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==}
peerDependencies:
@@ -5010,6 +5082,15 @@ packages:
resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==}
engines: {node: '>=12.20'}
zod-validation-error@2.1.0:
resolution: {integrity: sha512-VJh93e2wb4c3tWtGgTa0OF/dTt/zoPCPzXq4V11ZjxmEAFaPi/Zss1xIZdEB5RD8GD00U0/iVXgqkF77RV7pdQ==}
engines: {node: '>=18.0.0'}
peerDependencies:
zod: ^3.18.0
zod@3.23.8:
resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==}
zwitch@2.0.4:
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
@@ -5063,6 +5144,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@babel/generator@7.2.0':
dependencies:
'@babel/types': 7.24.5
jsesc: 2.5.2
lodash: 4.17.21
source-map: 0.5.7
trim-right: 1.0.1
'@babel/generator@7.24.5':
dependencies:
'@babel/types': 7.24.5
@@ -5635,6 +5724,12 @@ snapshots:
wrap-ansi: 8.1.0
wrap-ansi-cjs: wrap-ansi@7.0.0
'@jest/types@24.9.0':
dependencies:
'@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-reports': 1.1.2
'@types/yargs': 13.0.12
'@jridgewell/gen-mapping@0.3.5':
dependencies:
'@jridgewell/set-array': 1.2.1
@@ -6107,6 +6202,17 @@ snapshots:
'@types/http-cache-semantics@4.0.4': {}
'@types/istanbul-lib-coverage@2.0.6': {}
'@types/istanbul-lib-report@3.0.3':
dependencies:
'@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-reports@1.1.2':
dependencies:
'@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-lib-report': 3.0.3
'@types/js-cookie@3.0.6': {}
'@types/json5@0.0.29': {}
@@ -6165,6 +6271,12 @@ snapshots:
'@types/unist@3.0.2': {}
'@types/yargs-parser@21.0.3': {}
'@types/yargs@13.0.12':
dependencies:
'@types/yargs-parser': 21.0.3
'@types/yauzl@2.10.3':
dependencies:
'@types/node': 20.12.12
@@ -6310,6 +6422,17 @@ snapshots:
require-from-string: 2.0.2
uri-js: 4.4.1
allotment@1.20.2(react-dom@19.0.0-rc-935180c7e0-20240524(react@19.0.0-rc-935180c7e0-20240524))(react@19.0.0-rc-935180c7e0-20240524):
dependencies:
classnames: 2.5.1
eventemitter3: 5.0.1
lodash.clamp: 4.0.3
lodash.debounce: 4.0.8
lodash.isequal: 4.5.0
react: 19.0.0-rc-935180c7e0-20240524
react-dom: 19.0.0-rc-935180c7e0-20240524(react@19.0.0-rc-935180c7e0-20240524)
use-resize-observer: 9.1.0(react-dom@19.0.0-rc-935180c7e0-20240524(react@19.0.0-rc-935180c7e0-20240524))(react@19.0.0-rc-935180c7e0-20240524)
ansi-align@2.0.0:
dependencies:
string-width: 2.1.1
@@ -6318,6 +6441,8 @@ snapshots:
ansi-regex@3.0.1: {}
ansi-regex@4.1.1: {}
ansi-regex@5.0.1: {}
ansi-regex@6.0.1: {}
@@ -6455,6 +6580,16 @@ snapshots:
cosmiconfig: 7.1.0
resolve: 1.22.8
babel-plugin-react-compiler@0.0.0-experimental-592953e-20240517:
dependencies:
'@babel/generator': 7.2.0
'@babel/types': 7.24.5
chalk: 4.1.2
invariant: 2.2.4
pretty-format: 24.9.0
zod: 3.23.8
zod-validation-error: 2.1.0(zod@3.23.8)
bail@2.0.2: {}
balanced-match@1.0.2: {}
@@ -6592,6 +6727,8 @@ snapshots:
ci-info@1.6.0: {}
classnames@2.5.1: {}
cli-boxes@1.0.0: {}
cli-cursor@4.0.0:
@@ -7815,6 +7952,10 @@ snapshots:
intersection-observer@0.12.2: {}
invariant@2.2.4:
dependencies:
loose-envify: 1.4.0
ip-address@9.0.5:
dependencies:
jsbn: 1.1.0
@@ -8004,7 +8145,7 @@ snapshots:
jiti@1.21.0: {}
jotai@2.8.1(react@19.0.0-rc-935180c7e0-20240524)(types-react@19.0.0-rc.0):
jotai@2.8.2(react@19.0.0-rc-935180c7e0-20240524)(types-react@19.0.0-rc.0):
optionalDependencies:
'@types/react': types-react@19.0.0-rc.0
react: 19.0.0-rc-935180c7e0-20240524
@@ -8138,6 +8279,12 @@ snapshots:
lodash.camelcase@4.3.0: {}
lodash.clamp@4.0.3: {}
lodash.debounce@4.0.8: {}
lodash.isequal@4.5.0: {}
lodash.isplainobject@4.0.6: {}
lodash.kebabcase@4.1.1: {}
@@ -8860,6 +9007,13 @@ snapshots:
prettier@3.2.5: {}
pretty-format@24.9.0:
dependencies:
'@jest/types': 24.9.0
ansi-regex: 4.1.1
ansi-styles: 3.2.1
react-is: 16.13.1
progress@2.0.3: {}
prop-types@15.8.1:
@@ -9606,6 +9760,8 @@ snapshots:
trim-lines@3.0.1: {}
trim-right@1.0.1: {}
trough@2.2.0: {}
ts-api-utils@1.3.0(typescript@5.4.5):
@@ -9818,6 +9974,12 @@ snapshots:
dependencies:
prepend-http: 1.0.4
use-resize-observer@9.1.0(react-dom@19.0.0-rc-935180c7e0-20240524(react@19.0.0-rc-935180c7e0-20240524))(react@19.0.0-rc-935180c7e0-20240524):
dependencies:
'@juggle/resize-observer': 3.4.0
react: 19.0.0-rc-935180c7e0-20240524
react-dom: 19.0.0-rc-935180c7e0-20240524(react@19.0.0-rc-935180c7e0-20240524)
use-sync-external-store@1.2.2(react@19.0.0-rc-935180c7e0-20240524):
dependencies:
react: 19.0.0-rc-935180c7e0-20240524
@@ -10035,4 +10197,10 @@ snapshots:
yocto-queue@1.0.0: {}
zod-validation-error@2.1.0(zod@3.23.8):
dependencies:
zod: 3.23.8
zod@3.23.8: {}
zwitch@2.0.4: {}

View File

@@ -97,7 +97,7 @@ const isValidFormat = (fileName: string): boolean => {
file: reourceMappping,
forceDocument: true,
caption: `Clash Nyanpasu Nightly Build ${GIT_SHORT_HASH}`,
workers: 10,
workers: 16,
progressCallback: (progress) => consola.start(`Uploading ${progress}`),
});
@@ -115,7 +115,7 @@ const isValidFormat = (fileName: string): boolean => {
"Check out on GitHub:",
` - https://github.com/LibNyanpasu/clash-nyanpasu/releases/tag/v${version}`,
],
workers: 10,
workers: 16,
progressCallback: (progress) => consola.start(`Uploading ${progress}`),
});

View File

@@ -36,7 +36,7 @@
"i18next": "^23.11.3",
"lodash-es": "^4.17.21",
"meta-json-schema": "1.18.5-alpha",
"monaco-editor": "^0.48.0",
"monaco-editor": "^0.47.0",
"monaco-yaml": "^5.1.1",
"types-pac": "^1.0.2",
"nanoid": "^5.0.7",

View File

@@ -62,11 +62,11 @@ importers:
specifier: 1.18.5-alpha
version: 1.18.5-alpha
monaco-editor:
specifier: ^0.48.0
version: 0.48.0
specifier: ^0.47.0
version: 0.47.0
monaco-yaml:
specifier: ^5.1.1
version: 5.1.1(monaco-editor@0.48.0)
version: 5.1.1(monaco-editor@0.47.0)
nanoid:
specifier: ^5.0.7
version: 5.0.7
@@ -172,7 +172,7 @@ importers:
version: 5.2.11(@types/node@20.12.10)(sass@1.77.0)
vite-plugin-monaco-editor:
specifier: ^1.1.0
version: 1.1.0(monaco-editor@0.48.0)
version: 1.1.0(monaco-editor@0.47.0)
vite-plugin-svgr:
specifier: ^4.2.0
version: 4.2.0(rollup@4.17.2)(typescript@5.4.5)(vite@5.2.11(@types/node@20.12.10)(sass@1.77.0))
@@ -2607,10 +2607,10 @@ packages:
engines: { node: ">=10" }
hasBin: true
monaco-editor@0.48.0:
monaco-editor@0.47.0:
resolution:
{
integrity: sha512-goSDElNqFfw7iDHMg8WDATkfcyeLTNpBHQpO8incK6p5qZt5G/1j41X0xdGzpIkGojGXM+QiRQyLjnfDVvrpwA==,
integrity: sha512-VabVvHvQ9QmMwXu4du008ZDuyLnHs9j7ThVFsiJoXSOQk18+LF89N4ADzPbFenm0W4V2bGHnFBztIRQTgBfxzw==,
}
monaco-languageserver-types@0.3.3:
@@ -5080,7 +5080,7 @@ snapshots:
mkdirp@1.0.4: {}
monaco-editor@0.48.0: {}
monaco-editor@0.47.0: {}
monaco-languageserver-types@0.3.3:
dependencies:
@@ -5094,19 +5094,19 @@ snapshots:
monaco-types@0.1.0: {}
monaco-worker-manager@2.0.1(monaco-editor@0.48.0):
monaco-worker-manager@2.0.1(monaco-editor@0.47.0):
dependencies:
monaco-editor: 0.48.0
monaco-editor: 0.47.0
monaco-yaml@5.1.1(monaco-editor@0.48.0):
monaco-yaml@5.1.1(monaco-editor@0.47.0):
dependencies:
"@types/json-schema": 7.0.15
jsonc-parser: 3.2.1
monaco-editor: 0.48.0
monaco-editor: 0.47.0
monaco-languageserver-types: 0.3.3
monaco-marker-data-provider: 1.2.2
monaco-types: 0.1.0
monaco-worker-manager: 2.0.1(monaco-editor@0.48.0)
monaco-worker-manager: 2.0.1(monaco-editor@0.47.0)
path-browserify: 1.0.1
prettier: 2.8.8
vscode-languageserver-textdocument: 1.0.11
@@ -5534,9 +5534,9 @@ snapshots:
unist-util-stringify-position: 4.0.0
vfile-message: 4.0.2
vite-plugin-monaco-editor@1.1.0(monaco-editor@0.48.0):
vite-plugin-monaco-editor@1.1.0(monaco-editor@0.47.0):
dependencies:
monaco-editor: 0.48.0
monaco-editor: 0.47.0
vite-plugin-svgr@4.2.0(rollup@4.17.2)(typescript@5.4.5)(vite@5.2.11(@types/node@20.12.10)(sass@1.77.0)):
dependencies:

View File

@@ -1,9 +1,16 @@
import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
import { useTranslation } from "react-i18next";
import { List, Button, Select, MenuItem } from "@mui/material";
import {
List,
Button,
Select,
MenuItem,
styled,
ListItem,
ListItemText,
} from "@mui/material";
import { useVerge } from "@/hooks/use-verge";
import { BaseDialog, DialogRef, Notice, Switch } from "@/components/base";
import { SettingItem } from "./setting-comp";
import { GuardState } from "./guard-state";
import { open as openDialog } from "@tauri-apps/api/dialog";
import { convertFileSrc } from "@tauri-apps/api/tauri";
@@ -77,7 +84,8 @@ export const LayoutViewer = forwardRef<DialogRef>((props, ref) => {
onCancel={() => setOpen(false)}
>
<List>
<SettingItem label={t("Traffic Graph")}>
<Item>
<ListItemText primary={t("Traffic Graph")} />
<GuardState
value={verge?.traffic_graph ?? true}
valueProps="checked"
@@ -88,9 +96,10 @@ export const LayoutViewer = forwardRef<DialogRef>((props, ref) => {
>
<Switch edge="end" />
</GuardState>
</SettingItem>
</Item>
<SettingItem label={t("Memory Usage")}>
<Item>
<ListItemText primary={t("Memory Usage")} />
<GuardState
value={verge?.enable_memory_usage ?? true}
valueProps="checked"
@@ -101,9 +110,10 @@ export const LayoutViewer = forwardRef<DialogRef>((props, ref) => {
>
<Switch edge="end" />
</GuardState>
</SettingItem>
</Item>
<SettingItem label={t("Proxy Group Icon")}>
<Item>
<ListItemText primary={t("Proxy Group Icon")} />
<GuardState
value={verge?.enable_group_icon ?? true}
valueProps="checked"
@@ -114,9 +124,10 @@ export const LayoutViewer = forwardRef<DialogRef>((props, ref) => {
>
<Switch edge="end" />
</GuardState>
</SettingItem>
</Item>
<SettingItem label={t("Menu Icon")}>
<Item>
<ListItemText primary={t("Menu Icon")} />
<GuardState
value={verge?.menu_icon ?? "monochrome"}
onCatch={onError}
@@ -130,9 +141,11 @@ export const LayoutViewer = forwardRef<DialogRef>((props, ref) => {
<MenuItem value="disable">{t("Disable")}</MenuItem>
</Select>
</GuardState>
</SettingItem>
</Item>
{OS === "macos" && (
<SettingItem label={t("Tray Icon")}>
<Item>
<ListItemText primary={t("Tray Icon")} />
<GuardState
value={verge?.tray_icon ?? "monochrome"}
onCatch={onError}
@@ -148,10 +161,11 @@ export const LayoutViewer = forwardRef<DialogRef>((props, ref) => {
<MenuItem value="colorful">{t("Colorful")}</MenuItem>
</Select>
</GuardState>
</SettingItem>
</Item>
)}
<SettingItem label={t("Common Tray Icon")}>
<Item>
<ListItemText primary={t("Common Tray Icon")} />
<GuardState
value={verge?.common_tray_icon}
onCatch={onError}
@@ -194,9 +208,10 @@ export const LayoutViewer = forwardRef<DialogRef>((props, ref) => {
{verge?.common_tray_icon ? t("Clear") : t("Browse")}
</Button>
</GuardState>
</SettingItem>
</Item>
<SettingItem label={t("System Proxy Tray Icon")}>
<Item>
<ListItemText primary={t("System Proxy Tray Icon")} />
<GuardState
value={verge?.sysproxy_tray_icon}
onCatch={onError}
@@ -239,9 +254,10 @@ export const LayoutViewer = forwardRef<DialogRef>((props, ref) => {
{verge?.sysproxy_tray_icon ? t("Clear") : t("Browse")}
</Button>
</GuardState>
</SettingItem>
</Item>
<SettingItem label={t("Tun Tray Icon")}>
<Item>
<ListItemText primary={t("Tun Tray Icon")} />
<GuardState
value={verge?.tun_tray_icon}
onCatch={onError}
@@ -282,8 +298,12 @@ export const LayoutViewer = forwardRef<DialogRef>((props, ref) => {
{verge?.tun_tray_icon ? t("Clear") : t("Browse")}
</Button>
</GuardState>
</SettingItem>
</Item>
</List>
</BaseDialog>
);
});
const Item = styled(ListItem)(() => ({
padding: "5px 2px",
}));

View File

@@ -250,19 +250,17 @@ function connect_status()
local e = {}
e.use_time = ""
local url = luci.http.formvalue("url")
local is_baidu = string.find(url, "baidu")
local pw_switch = uci:get(appname, "@global[0]", "enabled")
local baidu = string.find(url, "baidu")
local enabled = uci:get(appname, "@global[0]", "enabled")
local chn_list = uci:get(appname, "@global[0]", "chn_list")
local gfw_list = uci:get(appname, "@global[0]", "use_gfw_list") or "1"
local proxy_mode = uci:get(appname, "@global[0]", "tcp_proxy_mode")
local socks_port = uci:get(appname, "@global[0]", "tcp_node_socks_port")
if pw_switch ~= 0 then
if chn_list == "proxy" then
if is_baidu ~= nil then
url = "--socks5 127.0.0.1:" .. socks_port .. " " .. url
end
else
if is_baidu == nil then
url = "--socks5 127.0.0.1:" .. socks_port .. " " .. url
end
if enabled ~= 0 then
if (chn_list == "proxy" and gfw_list == 0 and proxy_mode ~= "proxy" and baidu ~= nil) or (chn_list == 0 and gfw_list == 0 and proxy_mode == "proxy") then
url = "--socks5 127.0.0.1:" .. socks_port .. " " .. url
elseif baidu == nil then
url = "--socks5 127.0.0.1:" .. socks_port .. " " .. url
end
end
local result = luci.sys.exec('curl --connect-timeout 3 -o /dev/null -I -sk -w "%{http_code}:%{time_appconnect}" ' .. url)

View File

@@ -466,11 +466,8 @@ luci.sys.call("uci commit " .. name)
if reboot == 1 then
if arg3 == "cron" then
local f = io.open("/var/lock/" .. name .. ".lock", "r")
if f == nil then
if not nixio.fs.access("/var/lock/" .. name .. ".lock") then
luci.sys.call("touch /tmp/lock/" .. name .. "_cron.lock")
else
f:close()
end
end

View File

@@ -1166,11 +1166,8 @@ local function update_node(manual)
end
if arg[3] == "cron" then
local f = io.open("/var/lock/" .. appname .. ".lock", "r")
if f == nil then
if not nixio.fs.access("/var/lock/" .. appname .. ".lock") then
luci.sys.call("touch /tmp/lock/" .. appname .. "_cron.lock")
else
f:close()
end
end

View File

@@ -921,9 +921,9 @@ dependencies = [
[[package]]
name = "etherparse"
version = "0.14.3"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "095ab548cf452be5813424558a18af88f0a620d0f4a3d8793aa09311a3b6fa5f"
checksum = "21696e6dfe1057a166a042c6d27b89a46aad2ee1003e6e1e03c49d54fd3270d7"
dependencies = [
"arrayvec",
]

View File

@@ -168,7 +168,7 @@ iprange = "0.6"
regex = "1.4"
tun = { version = "0.6", optional = true, features = ["async"] }
etherparse = { version = "0.14", optional = true }
etherparse = { version = "0.15", optional = true }
smoltcp = { version = "0.11", optional = true, default-features = false, features = [
"std",
"log",

View File

@@ -3,7 +3,7 @@
use std::io;
use hickory_resolver::proto::{
op::{header::MessageType, response_code::ResponseCode, Message, OpCode},
op::{header::MessageType, response_code::ResponseCode, Header, Message, OpCode},
rr::{
rdata::{A, AAAA},
DNSClass,
@@ -18,15 +18,16 @@ use super::manager::FakeDnsManager;
pub async fn handle_dns_request(req_message: &Message, manager: &FakeDnsManager) -> io::Result<Message> {
let mut rsp_message = Message::new();
rsp_message.set_id(req_message.id());
rsp_message.set_message_type(MessageType::Response);
rsp_message.set_recursion_desired(false);
rsp_message.set_recursion_available(false);
let rsp_header = Header::response_from_request(req_message.header());
rsp_message.set_header(rsp_header);
if req_message.op_code() != OpCode::Query || req_message.message_type() != MessageType::Query {
rsp_message.set_response_code(ResponseCode::NotImp);
} else {
for query in req_message.queries() {
// Copy all the queries into response.
rsp_message.add_query(query.clone());
if query.query_class() != DNSClass::IN {
let record = Record::<RData>::with(query.name().clone(), query.query_type(), 0);
rsp_message.add_answer(record);

View File

@@ -10,7 +10,7 @@ use std::{
use byteorder::{BigEndian, ByteOrder};
use bytes::{BufMut, BytesMut};
use hickory_resolver::proto::{
op::{header::MessageType, response_code::ResponseCode, Message},
op::{response_code::ResponseCode, Message},
serialize::binary::{BinEncodable, BinEncoder, EncodeMode},
};
use log::{error, trace};
@@ -139,12 +139,7 @@ impl FakeDnsTcpServer {
Err(err) => {
error!("failed to handle DNS request, error: {}", err);
let mut rsp_message = Message::new();
rsp_message.set_id(req_message.id());
rsp_message.set_message_type(MessageType::Response);
rsp_message.set_response_code(ResponseCode::ServFail);
rsp_message
Message::error_msg(req_message.id(), req_message.op_code(), ResponseCode::ServFail)
}
};

View File

@@ -2,7 +2,7 @@
use std::{io, net::SocketAddr, sync::Arc, time::Duration};
use hickory_resolver::proto::op::{header::MessageType, response_code::ResponseCode, Message};
use hickory_resolver::proto::op::{response_code::ResponseCode, Message};
use log::error;
use shadowsocks::{lookup_then, net::UdpSocket as ShadowUdpSocket, ServerAddr};
use tokio::time;
@@ -69,12 +69,7 @@ impl FakeDnsUdpServer {
Err(err) => {
error!("failed to handle DNS request, error: {}", err);
let mut rsp_message = Message::new();
rsp_message.set_id(req_message.id());
rsp_message.set_message_type(MessageType::Response);
rsp_message.set_response_code(ResponseCode::ServFail);
rsp_message
Message::error_msg(req_message.id(), req_message.op_code(), ResponseCode::ServFail)
}
};

View File

@@ -36,7 +36,6 @@ nfpms:
file_name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
builds:
- main
vendor: sagernet
homepage: https://sing-box.sagernet.org/
maintainer: nekohasekai <contact-git@sekai.icu>
description: The universal proxy platform.

View File

@@ -113,7 +113,6 @@ nfpms:
file_name_template: '{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
builds:
- main
vendor: sagernet
homepage: https://sing-box.sagernet.org/
maintainer: nekohasekai <contact-git@sekai.icu>
description: The universal proxy platform.

View File

@@ -0,0 +1,113 @@
package sniff
import (
"bytes"
"context"
"encoding/binary"
"io"
"os"
"github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant"
)
const (
trackerConnectFlag = iota
trackerAnnounceFlag
trackerScrapeFlag
trackerProtocolID = 0x41727101980
trackerConnectMinSize = 16
trackerAnnounceMinSize = 20
trackerScrapeMinSize = 8
)
// BitTorrent detects if the stream is a BitTorrent connection.
// For the BitTorrent protocol specification, see https://www.bittorrent.org/beps/bep_0003.html
func BitTorrent(_ context.Context, reader io.Reader) (*adapter.InboundContext, error) {
var first byte
err := binary.Read(reader, binary.BigEndian, &first)
if err != nil {
return nil, err
}
if first != 19 {
return nil, os.ErrInvalid
}
var protocol [19]byte
_, err = reader.Read(protocol[:])
if err != nil {
return nil, err
}
if string(protocol[:]) != "BitTorrent protocol" {
return nil, os.ErrInvalid
}
return &adapter.InboundContext{
Protocol: C.ProtocolBitTorrent,
}, nil
}
// UTP detects if the packet is a uTP connection packet.
// For the uTP protocol specification, see
// 1. https://www.bittorrent.org/beps/bep_0029.html
// 2. https://github.com/bittorrent/libutp/blob/2b364cbb0650bdab64a5de2abb4518f9f228ec44/utp_internal.cpp#L112
func UTP(_ context.Context, packet []byte) (*adapter.InboundContext, error) {
// A valid uTP packet must be at least 20 bytes long.
if len(packet) < 20 {
return nil, os.ErrInvalid
}
version := packet[0] & 0x0F
ty := packet[0] >> 4
if version != 1 || ty > 4 {
return nil, os.ErrInvalid
}
// Validate the extensions
extension := packet[1]
reader := bytes.NewReader(packet[20:])
for extension != 0 {
err := binary.Read(reader, binary.BigEndian, &extension)
if err != nil {
return nil, err
}
var length byte
err = binary.Read(reader, binary.BigEndian, &length)
if err != nil {
return nil, err
}
_, err = reader.Seek(int64(length), io.SeekCurrent)
if err != nil {
return nil, err
}
}
return &adapter.InboundContext{
Protocol: C.ProtocolBitTorrent,
}, nil
}
// UDPTracker detects if the packet is a UDP Tracker Protocol packet.
// For the UDP Tracker Protocol specification, see https://www.bittorrent.org/beps/bep_0015.html
func UDPTracker(_ context.Context, packet []byte) (*adapter.InboundContext, error) {
switch {
case len(packet) >= trackerConnectMinSize &&
binary.BigEndian.Uint64(packet[:8]) == trackerProtocolID &&
binary.BigEndian.Uint32(packet[8:12]) == trackerConnectFlag:
fallthrough
case len(packet) >= trackerAnnounceMinSize &&
binary.BigEndian.Uint32(packet[8:12]) == trackerAnnounceFlag:
fallthrough
case len(packet) >= trackerScrapeMinSize &&
binary.BigEndian.Uint32(packet[8:12]) == trackerScrapeFlag:
return &adapter.InboundContext{
Protocol: C.ProtocolBitTorrent,
}, nil
default:
return nil, os.ErrInvalid
}
}

View File

@@ -0,0 +1,81 @@
package sniff_test
import (
"bytes"
"context"
"encoding/hex"
"testing"
"github.com/sagernet/sing-box/common/sniff"
C "github.com/sagernet/sing-box/constant"
"github.com/stretchr/testify/require"
)
func TestSniffBittorrent(t *testing.T) {
t.Parallel()
packets := []string{
"13426974546f7272656e742070726f746f636f6c0000000000100000e21ea9569b69bab33c97851d0298bdfa89bc90922d5554313631302dea812fcd6a3563e3be40c1d1",
"13426974546f7272656e742070726f746f636f6c00000000001000052aa4f5a7e209e54b32803d43670971c4c8caaa052d5452333030302d653369733079647675763638",
"13426974546f7272656e742070726f746f636f6c00000000001000052aa4f5a7e209e54b32803d43670971c4c8caaa052d5452343035302d6f7a316c6e79377931716130",
}
for _, pkt := range packets {
pkt, err := hex.DecodeString(pkt)
require.NoError(t, err)
metadata, err := sniff.BitTorrent(context.TODO(), bytes.NewReader(pkt))
require.NoError(t, err)
require.Equal(t, C.ProtocolBitTorrent, metadata.Protocol)
}
}
func TestSniffUTP(t *testing.T) {
t.Parallel()
packets := []string{
"010041a282d7ee7b583afb160004000006d8318da776968f92d666f7963f32dae23ba0d2c810d8b8209cc4939f54fde9eeaa521c2c20c9ba7f43f4fb0375f28de06643b5e3ca4685ab7ac76adca99783be72ef05ed59ef4234f5712b75b4c7c0d7bee8fe2ca20ad626ba5bb0ffcc16bf06790896f888048cf72716419a07db1a3dca4550fbcea75b53e97235168a221cf3e553dfbb723961bd719fab038d86e0ecb74747f5a2cd669de1c4b9ad375f3a492d09d98cdfad745435625401315bbba98d35d32086299801377b93495a63a9efddb8d05f5b37a5c5b1c0a25e917f12007bb5e05013ada8aff544fab8cadf61d80ddb0b60f12741e44515a109d144fd53ef845acb4b5ccf0d6fc302d7003d76df3fc3423bb0237301c9e88f900c2d392a8e0fdb36d143cf7527a93fd0a2638b746e72f6699fffcd4fd15348fce780d4caa04382fd9faf1ca0ae377ca805da7536662b84f5ee18dd3ae38fcb095a7543e55f9069ae92c8cf54ae44e97b558d35e2545c66601ed2149cbc32bd6df199a2be7cf0da8b2ff137e0d23e776bc87248425013876d3a3cc31a83b424b752bd0346437f24b532978005d8f5b1b0be1a37a2489c32a18a9ad3118e3f9d30eb299bffae18e1f0677c2a5c185e62519093fe6bc2b7339299ea50a587989f726ca6443a75dd5bb936f6367c6355d80fae53ff529d740b2e5576e3eefdf1fdbfc69c3c8d8ac750512635de63e054bee1d3b689bc1b2bc3d2601e42a00b5c89066d173d4ae7ffedfd2274e5cf6d868fbe640aedb69b8246142f00b32d459974287537ddd5373460dcbc92f5cfdd7a3ed6020822ae922d947893752ca1983d0d32977374c384ac8f5ab566859019b7351526b9f13e932037a55bb052d9deb3b3c23317e0784fdc51a64f2159bfea3b069cf5caf02ee2c3c1a6b6b427bb16165713e8802d95b5c8ed77953690e994bd38c9ae113fedaf6ee7fc2b96c032ceafc2a530ad0422e84546b9c6ad8ef6ea02fa508abddd1805c38a7b42e9b7c971b1b636865ebec06ed754bb404cd6b4e6cc8cb77bd4a0c43410d5cd5ef8fe853a66d49b3b9e06cb141236cdbfdd5761601dc54d1250b86c660e0f898fe62526fdd9acf0eab60a3bbbb2151970461f28f10b31689594bea646c4b03ee197d63bdef4e5a7c22716b3bb9494a83b78ecd81b338b80ac6c09c43485b1b09ba41c74343832c78f0520c1d659ac9eb1502094141e82fb9e5e620970ebc0655514c43c294a7714cbf9a499d277daf089f556398a01589a77494bec8bfb60a108f3813b55368672b88c1af40f6b3c8b513f7c70c3e0efce85228b8b9ec67ba0393f9f7305024d8e2da6a26cf85613d14f249170ce1000089df4c9c260df7f8292aa2ecb5d5bac97656d59aa248caedea2d198e51ce87baece338716d114b458de02d65c9ff808ca5b5b73723b4d1e962d9ac2d98176544dc9984cf8554d07820ef3dd0861cfe57b478328046380de589adad94ee44743ffac73bb7361feca5d56f07cf8ce75080e261282ae30350d7882679b15cab9e7e53ddf93310b33f7390ae5d318bb53f387e6af5d0ef4f947fc9cb8e7e38b52c7f8d772ece6156b38d88796ea19df02c53723b44df7c76315a0de9462f27287e682d2b4cda1a68fe00d7e48c51ee981be44e1ca940fb5190c12655edb4a83c3a4f33e48a015692df4f0b3d61656e362aca657b5ae8c12db5a0db3db1e45135ee918b66918f40e53c4f83e9da0cddfe63f736ae751ab3837a30ae3220d8e8e311487093a7b90c7e7e40dd54ca750e19452f9193aa892aa6a6229ab493dadae988b1724f7898ee69c36d3eb7364c4adbeca811cfe2065873e78c2b6dfdf1595f7a7831c07e03cda82e4f86f76438dfb2b07c13638ce7b509cfa71b88b5102b39a203b423202088e1c2103319cb32c13c1e546ff8612fa194c95a7808ab767c265a1bd5fa0efed5c8ec1701876a00ec8",
"01001ecb68176f215d04326300100000dbcf30292d14b54e9ee2d115ee5b8ebc7fad3e882d4fcdd0c14c6b917c11cb4c6a9f410b52a33ae97c2ac77c7a2b122b8955e09af3c5c595f1b2e79ca57cfe44c44e069610773b9bc9ba223d7f6b383e3adddd03fb88a8476028e30979c2ef321ffc97c5c132bcf9ac5b410bbb5ec6cefca3c7209202a14c5ae922b6b157b0a80249d13ffe5b996af0bc8e54ba576d148372494303e7ead0602b05b9c8fc97d48508a028a04d63a1fd28b0edfcd5c51715f63188b53eefede98a76912dca98518551a8856567307a56a702cbfcc115ea0c755b418bc2c7b57721239b82f09fb24328a4b0ce0f109bcb2a64e04b8aadb1f8487585425acdf8fc4ec8ea93cfcec5ac098bb29d42ddef6e46b03f34a5de28316726699b7cb5195c33e5c48abe87d591d63f9991c84c30819d186d6e0e95fd83c8dff07aa669c4430989bcaccfeacb9bcadbdb4d8f1964dbeb9687745656edd30b21c66cc0a1d742a78717d134a19a7f02d285a4973b1a198c00cfdff4676608dc4f3e817e3463c3b4e2c80d3e8d4fbac541a58a2fb7ad6939f607f8144eff6c8b0adc28ee5609ea158987519892fb",
"21001ecb6817f2805d044fd700100000dbd03029",
"410277ef0b1fb1f60000000000040000c233000000080000000000000000",
}
for _, pkt := range packets {
pkt, err := hex.DecodeString(pkt)
require.NoError(t, err)
metadata, err := sniff.UTP(context.TODO(), pkt)
require.NoError(t, err)
require.Equal(t, C.ProtocolBitTorrent, metadata.Protocol)
}
}
func TestSniffUDPTracker(t *testing.T) {
t.Parallel()
connectPackets := []string{
// connect packets
"00000417271019800000000078e90560",
"00000417271019800000000022c5d64d",
"000004172710198000000000b3863541",
// announce packets
"3d7592ead4b8c9e300000001b871a3820000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002092f616e6e6f756e6365",
"3d7592ead4b8c9e30000000188deed1c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002092f616e6e6f756e6365",
"3d7592ead4b8c9e300000001ceb948ad0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a3362cdb7020ff920e5aa642c3d4066950dd1f01f4d00000000000000000000000000000000000000000000000000000000000000000000000000000000000002092f616e6e6f756e6365",
// scrape packets
"3d7592ead4b8c9e300000002d2f4bba5a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
"3d7592ead4b8c9e300000002441243292aae6c35c94fcfb415dbe95f408b9ce91ee846ed",
"3d7592ead4b8c9e300000002b2aa461b1ad1fa9661cf3fe45fb2504ad52ec6c67758e294",
}
for _, pkt := range connectPackets {
pkt, err := hex.DecodeString(pkt)
require.NoError(t, err)
metadata, err := sniff.UDPTracker(context.TODO(), pkt)
require.NoError(t, err)
require.Equal(t, C.ProtocolBitTorrent, metadata.Protocol)
}
}

View File

@@ -1,9 +1,10 @@
package constant
const (
ProtocolTLS = "tls"
ProtocolHTTP = "http"
ProtocolQUIC = "quic"
ProtocolDNS = "dns"
ProtocolSTUN = "stun"
ProtocolTLS = "tls"
ProtocolHTTP = "http"
ProtocolQUIC = "quic"
ProtocolDNS = "dns"
ProtocolSTUN = "stun"
ProtocolBitTorrent = "bittorrent"
)

View File

@@ -2,6 +2,28 @@
icon: material/alert-decagram
---
#### 1.10.0-alpha.1
* Add tailing comma support in JSON configuration
* Add simple auto redirect for Android **1**
* Add bittorrent sniffer **2**
**1**:
It allows you to use redirect inbound in the sing-box Android client
and automatically configures IPv4 TCP redirection via su.
This may alleviate the symptoms of some OCD patients who think that
redirect can effectively save power compared to the system HTTP Proxy.
See [Redirect](/configuration/inbound/redirect/).
**2**:
It doesn't exactly work right now. Do not use it for anything other than blocking bittorrent traffics.
See [Protocol Sniff](/configuration/route/sniff/).
### 1.9.0
* Fixes and improvements

View File

@@ -1,3 +1,11 @@
---
icon: material/new-box
---
!!! quote "Changes in sing-box 1.9.0"
:material-plus: [auto_redirect](#auto_redirect)
!!! quote ""
Only supported on Linux and macOS.
@@ -9,6 +17,11 @@
"type": "redirect",
"tag": "redirect-in",
"auto_redirect": {
"enabled": false,
"continue_on_no_permission": false
},
... // Listen Fields
}
```
@@ -16,3 +29,23 @@
### Listen Fields
See [Listen Fields](/configuration/shared/listen/) for details.
### Fields
#### `auto_redirect`
!!! question "Since sing-box 1.10.0"
!!! quote ""
Only supported on Android.
Automatically add iptables nat rules to hijack **IPv4 TCP** connections.
It is expected to run with the Android graphical client (it will attempt to su at runtime).
#### `auto_redirect.continue_on_no_permission`
!!! question "Since sing-box 1.10.0"
Ignore errors when the Android device is not rooted or is denied root access.

View File

@@ -1,3 +1,11 @@
---
icon: material/new-box
---
!!! quote "sing-box 1.10.0 中的更改"
:material-plus: [auto_redirect](#auto_redirect)
!!! quote ""
仅支持 Linux 和 macOS。
@@ -9,9 +17,35 @@
"type": "redirect",
"tag": "redirect-in",
"auto_redirect": {
"enabled": false,
"continue_on_no_permission": false
},
... // 监听字段
}
```
### 监听字段
参阅 [监听字段](/zh/configuration/shared/listen/)。
### 字段
#### `auto_redirect`
!!! question "自 sing-box 1.10.0 起"
!!! quote ""
仅支持 Android。
自动添加 iptables nat 规则以劫持 **IPv4 TCP** 连接。
它预计与 Android 图形客户端一起运行(将在运行时尝试 su
#### `auto_redirect.continue_on_no_permission`
!!! question "自 sing-box 1.10.0 起"
当 Android 设备未获得 root 权限或 root 访问权限被拒绝时,忽略错误。

View File

@@ -2,10 +2,11 @@ If enabled in the inbound, the protocol and domain name (if present) of by the c
#### Supported Protocols
| Network | Protocol | Domain Name |
|:-------:|:--------:|:-----------:|
| TCP | HTTP | Host |
| TCP | TLS | Server Name |
| UDP | QUIC | Server Name |
| UDP | STUN | / |
| TCP/UDP | DNS | / |
| Network | Protocol | Domain Name |
|:-------:|:-----------:|:-----------:|
| TCP | HTTP | Host |
| TCP | TLS | Server Name |
| UDP | QUIC | Server Name |
| UDP | STUN | / |
| TCP/UDP | DNS | / |
| TCP/UDP | BitTorrent | / |

View File

@@ -2,10 +2,11 @@
#### 支持的协议
| 网络 | 协议 | 域名 |
|:-------:|:----:|:-----------:|
| TCP | HTTP | Host |
| TCP | TLS | Server Name |
| UDP | QUIC | Server Name |
| UDP | STUN | / |
| TCP/UDP | DNS | / |
| 网络 | 协议 | 域名 |
|:-------:|:-----------:|:-----------:|
| TCP | HTTP | Host |
| TCP | TLS | Server Name |
| UDP | QUIC | Server Name |
| UDP | STUN | / |
| TCP/UDP | DNS | / |
| TCP/UDP | BitTorrent | / |

View File

@@ -26,7 +26,7 @@ require (
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f
github.com/sagernet/quic-go v0.43.1-beta.1
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
github.com/sagernet/sing v0.4.0-beta.20
github.com/sagernet/sing v0.5.0-alpha.4
github.com/sagernet/sing-dns v0.2.0-beta.18
github.com/sagernet/sing-mux v0.2.0
github.com/sagernet/sing-quic v0.2.0-beta.5

View File

@@ -106,8 +106,8 @@ github.com/sagernet/quic-go v0.43.1-beta.1/go.mod h1:BkrQYeop7Jx3hN3TW8/76CXcdhY
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
github.com/sagernet/sing v0.4.0-beta.20 h1:8rEepj4LMcR0Wd389fJIziv/jr3MBtX5qXBHsfxJ+dY=
github.com/sagernet/sing v0.4.0-beta.20/go.mod h1:PFQKbElc2Pke7faBLv8oEba5ehtKO21Ho+TkYemTI3Y=
github.com/sagernet/sing v0.5.0-alpha.4 h1:8ljxrEq1BMcDuGe8FxoeUGasPUXQsIywpUusGrlGkQ0=
github.com/sagernet/sing v0.5.0-alpha.4/go.mod h1:Xh4KO9nGdvm4K/LVg9Xn9jSxJdqe9KcXbAzNC1S2qfw=
github.com/sagernet/sing-dns v0.2.0-beta.18 h1:6vzXZThRdA7YUzBOpSbUT48XRumtl/KIpIHFSOP0za8=
github.com/sagernet/sing-dns v0.2.0-beta.18/go.mod h1:k/dmFcQpg6+m08gC1yQBy+13+QkuLqpKr4bIreq4U24=
github.com/sagernet/sing-mux v0.2.0 h1:4C+vd8HztJCWNYfufvgL49xaOoOHXty2+EAjnzN3IYo=

View File

@@ -19,7 +19,7 @@ func New(ctx context.Context, router adapter.Router, logger log.ContextLogger, o
case C.TypeTun:
return NewTun(ctx, router, logger, options.Tag, options.TunOptions, platformInterface)
case C.TypeRedirect:
return NewRedirect(ctx, router, logger, options.Tag, options.RedirectOptions), nil
return NewRedirect(ctx, router, logger, options.Tag, options.RedirectOptions)
case C.TypeTProxy:
return NewTProxy(ctx, router, logger, options.Tag, options.TProxyOptions), nil
case C.TypeDirect:

View File

@@ -2,25 +2,36 @@ package inbound
import (
"context"
"errors"
"net"
"net/netip"
"os"
"os/exec"
"strings"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/redir"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/control"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
)
type Redirect struct {
myInboundAdapter
autoRedirect option.AutoRedirectOptions
needSu bool
suPath string
}
func NewRedirect(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.RedirectInboundOptions) *Redirect {
func NewRedirect(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.RedirectInboundOptions) (*Redirect, error) {
redirect := &Redirect{
myInboundAdapter{
myInboundAdapter: myInboundAdapter{
protocol: C.TypeRedirect,
network: []string{N.NetworkTCP},
ctx: ctx,
@@ -29,9 +40,27 @@ func NewRedirect(ctx context.Context, router adapter.Router, logger log.ContextL
tag: tag,
listenOptions: options.ListenOptions,
},
autoRedirect: common.PtrValueOrDefault(options.AutoRedirect),
}
if redirect.autoRedirect.Enabled {
if !C.IsAndroid {
return nil, E.New("auto redirect is only supported on Android")
}
userId := os.Getuid()
if userId != 0 {
suPath, err := exec.LookPath("/bin/su")
if err == nil {
redirect.needSu = true
redirect.suPath = suPath
} else if redirect.autoRedirect.ContinueOnNoPermission {
redirect.autoRedirect.Enabled = false
} else {
return nil, E.Extend(E.Cause(err, "root permission is required for auto redirect"), os.Getenv("PATH"))
}
}
}
redirect.connHandler = redirect
return redirect
return redirect, nil
}
func (r *Redirect) NewConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
@@ -42,3 +71,72 @@ func (r *Redirect) NewConnection(ctx context.Context, conn net.Conn, metadata ad
metadata.Destination = M.SocksaddrFromNetIP(destination)
return r.newConnection(ctx, conn, metadata)
}
func (r *Redirect) Start() error {
err := r.myInboundAdapter.Start()
if err != nil {
return err
}
if r.autoRedirect.Enabled {
r.cleanupRedirect()
err = r.setupRedirect()
if err != nil {
var exitError *exec.ExitError
if errors.As(err, &exitError) && exitError.ExitCode() == 13 && r.autoRedirect.ContinueOnNoPermission {
r.logger.Error(E.Cause(err, "setup auto redirect"))
return nil
}
r.cleanupRedirect()
return E.Cause(err, "setup auto redirect")
}
}
return nil
}
func (r *Redirect) Close() error {
if r.autoRedirect.Enabled {
r.cleanupRedirect()
}
return r.myInboundAdapter.Close()
}
func (r *Redirect) setupRedirect() error {
myUid := os.Getuid()
tcpPort := M.AddrPortFromNet(r.tcpListener.Addr()).Port()
interfaceRules := common.FlatMap(r.router.(adapter.Router).InterfaceFinder().Interfaces(), func(it control.Interface) []string {
return common.Map(common.Filter(it.Addresses, func(it netip.Prefix) bool { return it.Addr().Is4() }), func(it netip.Prefix) string {
return "iptables -t nat -A sing-box -p tcp -j RETURN -d " + it.String()
})
})
return r.runAndroidShell(`
set -e -o pipefail
iptables -t nat -N sing-box
` + strings.Join(interfaceRules, "\n") + `
iptables -t nat -A sing-box -j RETURN -m owner --uid-owner ` + F.ToString(myUid) + `
iptables -t nat -A sing-box -p tcp -j REDIRECT --to-ports ` + F.ToString(tcpPort) + `
iptables -t nat -A OUTPUT -p tcp -j sing-box
`)
}
func (r *Redirect) cleanupRedirect() {
_ = r.runAndroidShell(`
iptables -t nat -D OUTPUT -p tcp -j sing-box
iptables -t nat -F sing-box
iptables -t nat -X sing-box
`)
}
func (r *Redirect) runAndroidShell(content string) error {
var command *exec.Cmd
if r.needSu {
command = exec.Command(r.suPath, "-c", "sh")
} else {
command = exec.Command("sh")
}
command.Stdin = strings.NewReader(content)
combinedOutput, err := command.CombinedOutput()
if err != nil {
return E.Extend(err, string(combinedOutput))
}
return nil
}

View File

@@ -2,6 +2,12 @@ package option
type RedirectInboundOptions struct {
ListenOptions
AutoRedirect *AutoRedirectOptions `json:"auto_redirect,omitempty"`
}
type AutoRedirectOptions struct {
Enabled bool `json:"enabled,omitempty"`
ContinueOnNoPermission bool `json:"continue_on_no_permission,omitempty"`
}
type TProxyInboundOptions struct {

View File

@@ -850,7 +850,16 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
if metadata.InboundOptions.SniffEnabled {
buffer := buf.NewPacket()
sniffMetadata, err := sniff.PeekStream(ctx, conn, buffer, time.Duration(metadata.InboundOptions.SniffTimeout), sniff.StreamDomainNameQuery, sniff.TLSClientHello, sniff.HTTPHost)
sniffMetadata, err := sniff.PeekStream(
ctx,
conn,
buffer,
time.Duration(metadata.InboundOptions.SniffTimeout),
sniff.StreamDomainNameQuery,
sniff.TLSClientHello,
sniff.HTTPHost,
sniff.BitTorrent,
)
if sniffMetadata != nil {
metadata.Protocol = sniffMetadata.Protocol
metadata.Domain = sniffMetadata.Domain
@@ -977,7 +986,15 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
metadata.Destination = destination
}
if metadata.InboundOptions.SniffEnabled {
sniffMetadata, _ := sniff.PeekPacket(ctx, buffer.Bytes(), sniff.DomainNameQuery, sniff.QUICClientHello, sniff.STUNMessage)
sniffMetadata, _ := sniff.PeekPacket(
ctx,
buffer.Bytes(),
sniff.DomainNameQuery,
sniff.QUICClientHello,
sniff.STUNMessage,
sniff.UTP,
sniff.UDPTracker,
)
if sniffMetadata != nil {
metadata.Protocol = sniffMetadata.Protocol
metadata.Domain = sniffMetadata.Domain

View File

@@ -250,19 +250,17 @@ function connect_status()
local e = {}
e.use_time = ""
local url = luci.http.formvalue("url")
local is_baidu = string.find(url, "baidu")
local pw_switch = uci:get(appname, "@global[0]", "enabled")
local baidu = string.find(url, "baidu")
local enabled = uci:get(appname, "@global[0]", "enabled")
local chn_list = uci:get(appname, "@global[0]", "chn_list")
local gfw_list = uci:get(appname, "@global[0]", "use_gfw_list") or "1"
local proxy_mode = uci:get(appname, "@global[0]", "tcp_proxy_mode")
local socks_port = uci:get(appname, "@global[0]", "tcp_node_socks_port")
if pw_switch ~= 0 then
if chn_list == "proxy" then
if is_baidu ~= nil then
url = "--socks5 127.0.0.1:" .. socks_port .. " " .. url
end
else
if is_baidu == nil then
url = "--socks5 127.0.0.1:" .. socks_port .. " " .. url
end
if enabled ~= 0 then
if (chn_list == "proxy" and gfw_list == 0 and proxy_mode ~= "proxy" and baidu ~= nil) or (chn_list == 0 and gfw_list == 0 and proxy_mode == "proxy") then
url = "--socks5 127.0.0.1:" .. socks_port .. " " .. url
elseif baidu == nil then
url = "--socks5 127.0.0.1:" .. socks_port .. " " .. url
end
end
local result = luci.sys.exec('curl --connect-timeout 3 -o /dev/null -I -sk -w "%{http_code}:%{time_appconnect}" ' .. url)

View File

@@ -466,11 +466,8 @@ luci.sys.call("uci commit " .. name)
if reboot == 1 then
if arg3 == "cron" then
local f = io.open("/var/lock/" .. name .. ".lock", "r")
if f == nil then
if not nixio.fs.access("/var/lock/" .. name .. ".lock") then
luci.sys.call("touch /tmp/lock/" .. name .. "_cron.lock")
else
f:close()
end
end

View File

@@ -1166,11 +1166,8 @@ local function update_node(manual)
end
if arg[3] == "cron" then
local f = io.open("/var/lock/" .. appname .. ".lock", "r")
if f == nil then
if not nixio.fs.access("/var/lock/" .. appname .. ".lock") then
luci.sys.call("touch /tmp/lock/" .. appname .. "_cron.lock")
else
f:close()
end
end

View File

@@ -6,12 +6,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=sing-box
PKG_VERSION:=1.8.14
PKG_VERSION:=1.9.0
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/SagerNet/sing-box/tar.gz/v$(PKG_VERSION)?
PKG_HASH:=2ba7cfa097f5963ba304d47606e7a6b61bf881eb86cbed78fa6e4efae44a0a5f
PKG_HASH:=cb1d91e362f4dd7c35f7bb040514414861a045a76301af8257134c65f7a45c36
PKG_LICENSE:=GPL-3.0-or-later
PKG_LICENSE_FILES:=LICENSE

View File

@@ -30,7 +30,7 @@ define Download/geosite
HASH:=25d6120b009498ac83ae723e9751a19ff545fac4800dad53ab6e2592c3407533
endef
GEOSITE_IRAN_VER:=202405201648
GEOSITE_IRAN_VER:=202405270027
GEOSITE_IRAN_FILE:=iran.dat.$(GEOSITE_IRAN_VER)
define Download/geosite-ir
URL:=https://github.com/bootmortis/iran-hosted-domains/releases/download/$(GEOSITE_IRAN_VER)/

View File

@@ -45,15 +45,6 @@ jobs:
# unshallow must come first otherwise submodule may be get unshallowed
git fetch --tags --unshallow
git submodule update --init --depth 1
- name: Cache clang, nasm and wixtoolset 3
id: clang-cache
uses: actions/cache@v4
with:
path: |
third_party/nasm
third_party/llvm-build/Release+Asserts
third_party/wix311
key: ${{ runner.os }}-toolchain-${{ hashFiles('CLANG_REVISION') }}-v${{ env.CACHE_EPOCH }}
- name: Cache mingw64 toolchain
id: mingw64-cache
uses: actions/cache@v4
@@ -74,10 +65,6 @@ jobs:
run: |
choco upgrade -y llvm --version=17.0.6
clang -v
- name: "Download dependency: clang, nasm and wixtoolset 3"
if: ${{ steps.clang-cache.outputs.cache-hit != 'true' }}
run: |
scripts\download-toolchain.bat
- name: "Download dependency: mingw64"
if: ${{ steps.mingw64-cache.outputs.cache-hit != 'true' }}
run: |
@@ -87,6 +74,10 @@ jobs:
shell: bash
run: |
./scripts/download-curl-mingw.sh ${{ matrix.arch }}
- name: "Download dependency: nasm (chocolatey)"
run: |
choco install -y nasm
"C:\Program Files\NASM\nasm" --version
- name: "Download dependency: cmake (chocolatey)"
run: |
choco install -y cmake.portable --version=3.28.5
@@ -116,7 +107,7 @@ jobs:
set "CXXFLAGS=-I %CD%\third_party\${{ matrix.mingw_dir }}\lib\gcc\${{ matrix.arch }}-w64-mingw32\${{ matrix.gcc_ver }}\include"
set "LDFLAGS=-Wl,-L%CD%\third_party\${{ matrix.mingw_dir }}\lib -Wl,-L%CD%\third_party\${{ matrix.mingw_dir }}\lib\gcc\${{ matrix.arch }}-w64-mingw32\${{ matrix.gcc_ver }}"
set "Path=%CD%\third_party\${{ matrix.mingw_dir }}\bin;%Path%"
set "Path=%CD%\third_party\nasm;%Path%"
set "Path=C:\Program Files\NASM\nasm;%Path%"
REM for clang.exe and clang++.exe
REM set "Path=C:\Program Files\LLVM\bin;%Path%"

View File

@@ -47,6 +47,7 @@ jobs:
- 'centos8'
- 'fedora39'
- 'alpine320'
- 'i386-alpine320'
- 'opensuse15'
# - 'centos9'
runs-on: ubuntu-22.04
@@ -162,14 +163,13 @@ jobs:
strategy:
fail-fast: false
matrix:
container:
- 'alpine320'
runs-on: ubuntu-22.04
include:
- container: 'alpine320'
arch: amd64
- container: 'i386-alpine320'
arch: i386
runs-on: ubuntu-20.04
needs: docker_publish
container:
image: 'ghcr.io/chilledheart/${{ matrix.container }}'
env:
DISTRO: ${{ matrix.container }}
steps:
- uses: actions/checkout@v4
- name: Checkout with shallow submodules
@@ -179,6 +179,16 @@ jobs:
# unshallow must come first otherwise submodule may be get unshallowed
git fetch --tags --unshallow
git submodule update --init --depth 1
- name: Set SDK_ROOT
run: |
echo "SDK_ROOT=${{ github.workspace }}/${{ matrix.arch }}-alpine320-sysroot" >> $GITHUB_ENV
- name: Cache clang
id: clang-cache
uses: actions/cache@v4
with:
path: |
third_party/llvm-build/Release+Asserts
key: ${{ runner.os }}-toolchain-${{ hashFiles('CLANG_REVISION') }}-v${{ env.CACHE_EPOCH }}
- name: Cache golang
uses: actions/cache@v4
with:
@@ -188,20 +198,50 @@ jobs:
key: ${{ runner.os }}-go-docker-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-docker-
- name: Populate sysroot from docker image
run: |
docker pull ghcr.io/chilledheart/${{ matrix.container }}
mkdir -p "${{ env.SDK_ROOT }}"
docker export $(docker create ghcr.io/chilledheart/${{ matrix.container }}) | tar -C "${{ env.SDK_ROOT }}" -xf -
- name: Build build tool
run: |
cd tools
go build
- name: "Install dependency: prebuilt clang and clang-tidy binaries"
if: ${{ steps.clang-cache.outputs.cache-hit != 'true' }}
run: |
./scripts/download-clang-prebuilt-binaries.py
rm -f third_party/llvm-build/Release+Asserts/*.tgz
- name: Change ubuntu mirror
run: |
sudo sed -i 's/azure.archive.ubuntu.com/azure.archive.ubuntu.com/g' /etc/apt/sources.list
sudo apt-get update -qq
- name: Populate depedencies
run: |
sudo apt-get update -qq
sudo apt-get install -y cmake ninja-build pkg-config gettext bubblewrap
# libc6-i386 interferes with x86 build
sudo apt remove libc6-i386
- name: Populate dependencie (cmake, overwrite)
run: |
curl -L -O https://github.com/Kitware/CMake/releases/download/v3.28.5/cmake-3.28.5-linux-x86_64.tar.gz
sudo tar -C /usr/local --strip-components=1 -xf cmake-3.28.5-linux-x86_64.tar.gz
cmake --version
rm -f *.tar.gz
- name: Build TGZ packages
run: |
export CC=clang
export CXX=clang++
./tools/build --arch amd64 --system linux --subsystem musl -build-benchmark -build-test \
--cmake-build-type MinSizeRel
- name: Run tests (amd64)
./tools/build --arch ${{ matrix.arch }} --system linux --subsystem musl \
--sysroot "${{ env.SDK_ROOT }}" \
-build-test --cmake-build-type MinSizeRel
- name: Run tests
run: |
./build-linux-musl-amd64/yass_test
./build-linux-musl-amd64/yass_benchmark
bwrap --die-with-parent --bind "${{ env.SDK_ROOT }}" / \
--ro-bind /sys /sys \
--ro-bind /etc/resolv.conf /etc/resolv.conf \
--proc /proc --dev /dev \
--unshare-all --share-net \
--bind $PWD/build-linux-musl-${{ matrix.arch }} /tmp \
/tmp/yass_test
- name: Upload dist tarball (including debuginfo)
if: ${{ github.event_name == 'release' }}
env:

2
yass/.gitignore vendored
View File

@@ -89,3 +89,5 @@ local.properties
/rustc-1.77.2-src
/rustc-1.78.0-src
/third_party/rust-ohos
/i386-alpine320-sysroot
/amd64-alpine320-sysroot

View File

@@ -590,6 +590,7 @@ if (CMAKE_CROSSCOMPILING AND (CMAKE_SYSROOT OR CMAKE_OSX_SYSROOT))
if (NOT EXISTS ${_SYSROOT})
message(FATAL_ERROR "Invalid sysroot ${_SYSROOT}")
endif()
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(ENV{PKG_CONFIG_DIR} "")
set(ENV{PKG_CONFIG_LIBDIR} "${_SYSROOT}/usr/lib64/pkgconfig:${_SYSROOT}/usr/lib/pkgconfig:${_SYSROOT}/lib/pkgconfig:${_SYSROOT}/usr/share/pkgconfig")
if (CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
@@ -614,6 +615,10 @@ if (UNIX AND CMAKE_CROSSCOMPILING AND CMAKE_SYSROOT AND COMPILER_CLANG AND NOT C
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv32")
file(GLOB _GCC_SYSROOT "${CMAKE_SYSROOT}/../lib/gcc/*/*.*.*")
endif()
# fix up for raw sysroot from docker
if (NOT _GCC_SYSROOT)
file(GLOB _GCC_SYSROOT "${CMAKE_SYSROOT}/usr/lib/gcc/*/*.*.*")
endif()
if (_GCC_SYSROOT)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --gcc-install-dir=${_GCC_SYSROOT}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --gcc-install-dir=${_GCC_SYSROOT}")

View File

@@ -1,9 +1,2 @@
FROM alpine:3.20
RUN apk add --no-cache git bash perl curl go tar && \
apk add --no-cache build-base binutils-gold linux-headers cmake ninja-build curl-dev && \
apk add --no-cache gtk+3.0-dev gettext && \
apk add --no-cache llvm clang lld && \
ln -sf /usr/lib/ninja-build/bin/ninja /usr/bin/ninja
# https://github.com/cli/cli/blob/trunk/docs/install_linux.md
RUN echo "@community http://dl-cdn.alpinelinux.org/alpine/edge/community" >> /etc/apk/repositories
RUN apk add --no-cache github-cli@community
RUN apk add --no-cache bash tar build-base linux-headers curl-dev gtk+3.0-dev

View File

@@ -0,0 +1,2 @@
FROM i386/alpine:3.20
RUN apk add --no-cache bash tar build-base linux-headers curl-dev gtk+3.0-dev

View File

@@ -8,15 +8,15 @@ REM Usage: download-toolchain.bat
echo "Install dependency: prebuilt nasm"
cd third_party
curl -C - -L -O https://www.nasm.us/pub/nasm/releasebuilds/2.15.05/win64/nasm-2.15.05-win64.zip
curl -C - -L -O https://www.nasm.us/pub/nasm/releasebuilds/2.16.03/win64/nasm-2.16.03-win64.zip
if %ERRORLEVEL% NEQ 0 exit /B %ERRORLEVEL%
"C:\Program Files\7-Zip\7z.exe" x nasm-2.15.05-win64.zip -aoa
"C:\Program Files\7-Zip\7z.exe" x nasm-2.16.03-win64.zip -aoa
if %ERRORLEVEL% NEQ 0 exit /B %ERRORLEVEL%
rmdir /s /q nasm
rename nasm-2.15.05 nasm
rename nasm-2.16.03 nasm
if %ERRORLEVEL% NEQ 0 exit /B %ERRORLEVEL%
cd ..
del /s /q third_party\nasm-2.15*.zip
del /s /q third_party\nasm-2*.zip
echo "Install dependency: prebuilt clang and clang-tidy binaries"

View File

@@ -28,7 +28,6 @@ Fixes #
### Before submitting a *pull request* make sure you have:
- [ ] At least skimmed through [contributing guidelines](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#developer-instructions) including [yt-dlp coding conventions](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#yt-dlp-coding-conventions)
- [ ] [Searched](https://github.com/yt-dlp/yt-dlp/search?q=is%3Apr&type=Issues) the bugtracker for similar pull requests
- [ ] Checked the code with [flake8](https://pypi.python.org/pypi/flake8) and [ran relevant tests](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#developer-instructions)
### In order to be accepted and merged into yt-dlp each piece of code must be in public domain or released under [Unlicense](http://unlicense.org/). Check all of the following options that apply:
- [ ] I am the original author of this code and I am willing to release it under [Unlicense](http://unlicense.org/)

View File

@@ -53,7 +53,7 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
- name: Install test requirements
run: python3 ./devscripts/install_deps.py --include dev --include curl-cffi
run: python3 ./devscripts/install_deps.py --include test --include curl-cffi
- name: Run tests
continue-on-error: False
run: |

View File

@@ -15,13 +15,13 @@ jobs:
with:
python-version: '3.8'
- name: Install test requirements
run: python3 ./devscripts/install_deps.py --include dev
run: python3 ./devscripts/install_deps.py --include test
- name: Run tests
run: |
python3 -m yt_dlp -v || true
python3 ./devscripts/run_tests.py core
flake8:
name: Linter
check:
name: Code check
if: "!contains(github.event.head_commit.message, 'ci skip all')"
runs-on: ubuntu-latest
steps:
@@ -29,9 +29,11 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: '3.8'
- name: Install flake8
run: python3 ./devscripts/install_deps.py -o --include dev
- name: Install dev dependencies
run: python3 ./devscripts/install_deps.py -o --include static-analysis
- name: Make lazy extractors
run: python3 ./devscripts/make_lazy_extractors.py
- name: Run flake8
run: flake8 .
- name: Run ruff
run: ruff check --output-format github .
- name: Run autopep8
run: autopep8 --diff .

2
yt-dlp/.gitignore vendored
View File

@@ -67,7 +67,7 @@ cookies
# Python
*.pyc
*.pyo
.pytest_cache
.*_cache
wine-py2exe/
py2exe.log
build/

View File

@@ -0,0 +1,14 @@
repos:
- repo: local
hooks:
- id: linter
name: Apply linter fixes
entry: ruff check --fix .
language: system
types: [python]
require_serial: true
- id: format
name: Apply formatting fixes
entry: autopep8 --in-place .
language: system
types: [python]

View File

@@ -0,0 +1,9 @@
repos:
- repo: local
hooks:
- id: fix
name: Apply code fixes
entry: hatch fmt
language: system
types: [python]
require_serial: true

View File

@@ -134,18 +134,53 @@ We follow [youtube-dl's policy](https://github.com/ytdl-org/youtube-dl#can-you-a
# DEVELOPER INSTRUCTIONS
Most users do not need to build yt-dlp and can [download the builds](https://github.com/yt-dlp/yt-dlp/releases) or get them via [the other installation methods](README.md#installation).
Most users do not need to build yt-dlp and can [download the builds](https://github.com/yt-dlp/yt-dlp/releases), get them via [the other installation methods](README.md#installation) or directly run it using `python -m yt_dlp`.
To run yt-dlp as a developer, you don't need to build anything either. Simply execute
`yt-dlp` uses [`hatch`](<https://hatch.pypa.io>) as a project management tool.
You can easily install it using [`pipx`](<https://pipx.pypa.io>) via `pipx install hatch`, or else via `pip` or your package manager of choice. Make sure you are using at least version `1.10.0`, otherwise some functionality might not work as expected.
python3 -m yt_dlp
If you plan on contributing to `yt-dlp`, best practice is to start by running the following command:
To run all the available core tests, use:
```shell
$ hatch run setup
```
python3 devscripts/run_tests.py
The above command will install a `pre-commit` hook so that required checks/fixes (linting, formatting) will run automatically before each commit. If any code needs to be linted or formatted, then the commit will be blocked and the necessary changes will be made; you should review all edits and re-commit the fixed version.
After this you can use `hatch shell` to enable a virtual environment that has `yt-dlp` and its development dependencies installed.
In addition, the following script commands can be used to run simple tasks such as linting or testing (without having to run `hatch shell` first):
* `hatch fmt`: Automatically fix linter violations and apply required code formatting changes
* See `hatch fmt --help` for more info
* `hatch test`: Run extractor or core tests
* See `hatch test --help` for more info
See item 6 of [new extractor tutorial](#adding-support-for-a-new-site) for how to run extractor specific test cases.
While it is strongly recommended to use `hatch` for yt-dlp development, if you are unable to do so, alternatively you can manually create a virtual environment and use the following commands:
```shell
# To only install development dependencies:
$ python -m devscripts.install_deps --include dev
# Or, for an editable install plus dev dependencies:
$ python -m pip install -e ".[default,dev]"
# To setup the pre-commit hook:
$ pre-commit install
# To be used in place of `hatch test`:
$ python -m devscripts.run_tests
# To be used in place of `hatch fmt`:
$ ruff check --fix .
$ autopep8 --in-place .
# To only check code instead of applying fixes:
$ ruff check .
$ autopep8 --diff .
```
If you want to create a build of yt-dlp yourself, you can follow the instructions [here](README.md#compile).
@@ -165,12 +200,16 @@ After you have ensured this site is distributing its content legally, you can fo
1. [Fork this repository](https://github.com/yt-dlp/yt-dlp/fork)
1. Check out the source code with:
git clone git@github.com:YOUR_GITHUB_USERNAME/yt-dlp.git
```shell
$ git clone git@github.com:YOUR_GITHUB_USERNAME/yt-dlp.git
```
1. Start a new git branch with
cd yt-dlp
git checkout -b yourextractor
```shell
$ cd yt-dlp
$ git checkout -b yourextractor
```
1. Start with this simple template and save it to `yt_dlp/extractor/yourextractor.py`:
@@ -217,21 +256,27 @@ After you have ensured this site is distributing its content legally, you can fo
# TODO more properties (see yt_dlp/extractor/common.py)
}
```
1. Add an import in [`yt_dlp/extractor/_extractors.py`](yt_dlp/extractor/_extractors.py). Note that the class name must end with `IE`.
1. Run `python3 devscripts/run_tests.py YourExtractor`. This *may fail* at first, but you can continually re-run it until you're done. Upon failure, it will output the missing fields and/or correct values which you can copy. If you decide to add more than one test, the tests will then be named `YourExtractor`, `YourExtractor_1`, `YourExtractor_2`, etc. Note that tests with an `only_matching` key in the test's dict are not included in the count. You can also run all the tests in one go with `YourExtractor_all`
1. Add an import in [`yt_dlp/extractor/_extractors.py`](yt_dlp/extractor/_extractors.py). Note that the class name must end with `IE`. Also note that when adding a parenthesized import group, the last import in the group must have a trailing comma in order for this formatting to be respected by our code formatter.
1. Run `hatch test YourExtractor`. This *may fail* at first, but you can continually re-run it until you're done. Upon failure, it will output the missing fields and/or correct values which you can copy. If you decide to add more than one test, the tests will then be named `YourExtractor`, `YourExtractor_1`, `YourExtractor_2`, etc. Note that tests with an `only_matching` key in the test's dict are not included in the count. You can also run all the tests in one go with `YourExtractor_all`
1. Make sure you have at least one test for your extractor. Even if all videos covered by the extractor are expected to be inaccessible for automated testing, tests should still be added with a `skip` parameter indicating why the particular test is disabled from running.
1. Have a look at [`yt_dlp/extractor/common.py`](yt_dlp/extractor/common.py) for possible helper methods and a [detailed description of what your extractor should and may return](yt_dlp/extractor/common.py#L119-L440). Add tests and code for as many as you want.
1. Make sure your code follows [yt-dlp coding conventions](#yt-dlp-coding-conventions) and check the code with [flake8](https://flake8.pycqa.org/en/latest/index.html#quickstart):
1. Make sure your code follows [yt-dlp coding conventions](#yt-dlp-coding-conventions), passes [ruff](https://docs.astral.sh/ruff/tutorial/#getting-started) code checks and is properly formatted:
$ flake8 yt_dlp/extractor/yourextractor.py
```shell
$ hatch fmt --check
```
You can use `hatch fmt` to automatically fix problems.
1. Make sure your code works under all [Python](https://www.python.org/) versions supported by yt-dlp, namely CPython and PyPy for Python 3.8 and above. Backward compatibility is not required for even older versions of Python.
1. When the tests pass, [add](https://git-scm.com/docs/git-add) the new files, [commit](https://git-scm.com/docs/git-commit) them and [push](https://git-scm.com/docs/git-push) the result, like this:
$ git add yt_dlp/extractor/_extractors.py
$ git add yt_dlp/extractor/yourextractor.py
$ git commit -m '[yourextractor] Add extractor'
$ git push origin yourextractor
```shell
$ git add yt_dlp/extractor/_extractors.py
$ git add yt_dlp/extractor/yourextractor.py
$ git commit -m '[yourextractor] Add extractor'
$ git push origin yourextractor
```
1. Finally, [create a pull request](https://help.github.com/articles/creating-a-pull-request). We'll then review and merge it.

View File

@@ -610,3 +610,23 @@ Offert4324
sta1us
Tomoka1
trwstin
alexhuot1
clienthax
DaPotato69
emqi
hugohaa
imanoreotwe
JakeFinley96
lostfictions
minamotorin
ocococococ
Podiumnoche
RasmusAntons
roeniss
shoxie007
Szpachlarz
The-MAGI
TuxCoder
voidful
vtexier
WyohKnott

View File

@@ -4,6 +4,116 @@
# To create a release, dispatch the https://github.com/yt-dlp/yt-dlp/actions/workflows/release.yml workflow on master
-->
### 2024.05.26
#### Core changes
- [Better warning when requested subs format not found](https://github.com/yt-dlp/yt-dlp/commit/7e4259dff0b681a3f0e8a930799ce0394328c86e) ([#9873](https://github.com/yt-dlp/yt-dlp/issues/9873)) by [DaPotato69](https://github.com/DaPotato69)
- [Merged with youtube-dl a08f2b7](https://github.com/yt-dlp/yt-dlp/commit/a4da9db87b6486b270c15dfa07ab5bfedc83f6bd) by [bashonly](https://github.com/bashonly), [Grub4K](https://github.com/Grub4K)
- [Warn if lack of ffmpeg alters format selection](https://github.com/yt-dlp/yt-dlp/commit/96da9525043f78aca4544d01761b13b2140e9ae6) ([#9805](https://github.com/yt-dlp/yt-dlp/issues/9805)) by [pukkandan](https://github.com/pukkandan), [seproDev](https://github.com/seproDev)
- **cookies**
- [Add `--cookies-from-browser` support for Whale](https://github.com/yt-dlp/yt-dlp/commit/dd9ad97b1fbdd36c086b8ba82328a4d954f78f8e) ([#9649](https://github.com/yt-dlp/yt-dlp/issues/9649)) by [roeniss](https://github.com/roeniss)
- [Get chrome session cookies with `--cookies-from-browser`](https://github.com/yt-dlp/yt-dlp/commit/f1f158976e38d38a260762accafe7bbe6d451151) ([#9747](https://github.com/yt-dlp/yt-dlp/issues/9747)) by [StefanLobbenmeier](https://github.com/StefanLobbenmeier)
- **windows**: [Improve shell quoting and tests](https://github.com/yt-dlp/yt-dlp/commit/64766459e37451b665c1464073c28361fbcf1c25) ([#9802](https://github.com/yt-dlp/yt-dlp/issues/9802)) by [Grub4K](https://github.com/Grub4K) (With fixes in [7e26bd5](https://github.com/yt-dlp/yt-dlp/commit/7e26bd53f9c5893518fde81dfd0079ec08dd841e))
#### Extractor changes
- [Add POST data hash to `--write-pages` filenames](https://github.com/yt-dlp/yt-dlp/commit/61b17437dc14a1c7e90ff48a6198df77828c6df4) ([#9879](https://github.com/yt-dlp/yt-dlp/issues/9879)) by [minamotorin](https://github.com/minamotorin) (With fixes in [c999bac](https://github.com/yt-dlp/yt-dlp/commit/c999bac02c5a4f755b2a82488a975e91c988ffd8) by [bashonly](https://github.com/bashonly))
- [Make `_search_nextjs_data` non fatal](https://github.com/yt-dlp/yt-dlp/commit/3ee1194288981c4f2c4abd8315326de0c424d2ce) ([#8937](https://github.com/yt-dlp/yt-dlp/issues/8937)) by [Grub4K](https://github.com/Grub4K)
- **afreecatv**: live: [Add `cdn` extractor-arg](https://github.com/yt-dlp/yt-dlp/commit/315b3544296bb83012e20ee3af9d3cbf5600dd1c) ([#9666](https://github.com/yt-dlp/yt-dlp/issues/9666)) by [bashonly](https://github.com/bashonly)
- **alura**: [Fix extractor](https://github.com/yt-dlp/yt-dlp/commit/fc2879ecb05aaad36869609d154e4321362c1f63) ([#9658](https://github.com/yt-dlp/yt-dlp/issues/9658)) by [hugohaa](https://github.com/hugohaa)
- **artetv**: [Label forced subtitles](https://github.com/yt-dlp/yt-dlp/commit/7b5674949fd03a33b47b67b31d56a5adf1c48c91) ([#9945](https://github.com/yt-dlp/yt-dlp/issues/9945)) by [vtexier](https://github.com/vtexier)
- **bbc**: [Fix and extend extraction](https://github.com/yt-dlp/yt-dlp/commit/7975ddf245d22af034d5b983eeb1c5ec6c2ce053) ([#9705](https://github.com/yt-dlp/yt-dlp/issues/9705)) by [dirkf](https://github.com/dirkf), [kylegustavo](https://github.com/kylegustavo), [pukkandan](https://github.com/pukkandan)
- **bilibili**: [Fix `--geo-verification-proxy` support](https://github.com/yt-dlp/yt-dlp/commit/2338827072dacab0f15348b70aec8685feefc8d1) ([#9817](https://github.com/yt-dlp/yt-dlp/issues/9817)) by [fireattack](https://github.com/fireattack)
- **bilibilispacevideo**
- [Better error message](https://github.com/yt-dlp/yt-dlp/commit/06d52c87314e0bbc16c43c405090843885577b88) ([#9839](https://github.com/yt-dlp/yt-dlp/issues/9839)) by [fireattack](https://github.com/fireattack)
- [Fix extraction](https://github.com/yt-dlp/yt-dlp/commit/4cc99d7b6cce8b39506ead01407445d576b63ee4) ([#9905](https://github.com/yt-dlp/yt-dlp/issues/9905)) by [c-basalt](https://github.com/c-basalt)
- **boosty**: [Add cookies support](https://github.com/yt-dlp/yt-dlp/commit/145dc6f6563e80d2da1b3e9aea2ffa795b71622c) ([#9522](https://github.com/yt-dlp/yt-dlp/issues/9522)) by [RasmusAntons](https://github.com/RasmusAntons)
- **brilliantpala**: [Fix login](https://github.com/yt-dlp/yt-dlp/commit/eead3bbc01f6529862bdad1f0b2adeabda4f006e) ([#9788](https://github.com/yt-dlp/yt-dlp/issues/9788)) by [pzhlkj6612](https://github.com/pzhlkj6612)
- **canalalpha**: [Fix extractor](https://github.com/yt-dlp/yt-dlp/commit/00a9f2e1f7fa69499221f2e8dd73a08efeef79bc) ([#9675](https://github.com/yt-dlp/yt-dlp/issues/9675)) by [kclauhk](https://github.com/kclauhk)
- **cbc.ca**: player: [Improve `_VALID_URL`](https://github.com/yt-dlp/yt-dlp/commit/c8bf48f3a8fa29587e7c73ef5a7710385a5ea725) ([#9866](https://github.com/yt-dlp/yt-dlp/issues/9866)) by [carusocr](https://github.com/carusocr)
- **cda**: [Fix age-gated web extraction](https://github.com/yt-dlp/yt-dlp/commit/6d8a53d870ff6795f509085bfbf3981417999038) ([#9939](https://github.com/yt-dlp/yt-dlp/issues/9939)) by [dirkf](https://github.com/dirkf), [emqi](https://github.com/emqi), [Podiumnoche](https://github.com/Podiumnoche), [Szpachlarz](https://github.com/Szpachlarz)
- **commonmistakes**: [Raise error on blob URLs](https://github.com/yt-dlp/yt-dlp/commit/98d71d8c5e5dab08b561ee6f137e968d2a004262) ([#9897](https://github.com/yt-dlp/yt-dlp/issues/9897)) by [seproDev](https://github.com/seproDev)
- **crunchyroll**
- [Always make metadata available](https://github.com/yt-dlp/yt-dlp/commit/cb2fb4a643949322adba561ca73bcba3221ec0c5) ([#9772](https://github.com/yt-dlp/yt-dlp/issues/9772)) by [bashonly](https://github.com/bashonly)
- [Fix auth and remove cookies support](https://github.com/yt-dlp/yt-dlp/commit/ff38a011d57b763f3a69bebd25a5dc9044a717ce) ([#9749](https://github.com/yt-dlp/yt-dlp/issues/9749)) by [bashonly](https://github.com/bashonly)
- [Fix stream extraction](https://github.com/yt-dlp/yt-dlp/commit/f2816634e3be88fe158b342ee33918de3c272a54) ([#10005](https://github.com/yt-dlp/yt-dlp/issues/10005)) by [bashonly](https://github.com/bashonly)
- [Support browser impersonation](https://github.com/yt-dlp/yt-dlp/commit/5904853ae5788509fdc4892cb7ecdfa9ae7f78e6) ([#9857](https://github.com/yt-dlp/yt-dlp/issues/9857)) by [bashonly](https://github.com/bashonly)
- **dangalplay**: [Add extractors](https://github.com/yt-dlp/yt-dlp/commit/0d067e77c3f5527946fb0c22ee1c7011994cba40) ([#10021](https://github.com/yt-dlp/yt-dlp/issues/10021)) by [bashonly](https://github.com/bashonly)
- **discoveryplus**: [Fix dmax.de and related extractors](https://github.com/yt-dlp/yt-dlp/commit/90d2da311bbb5dc06f385ee428c7e4590936e995) ([#10020](https://github.com/yt-dlp/yt-dlp/issues/10020)) by [bashonly](https://github.com/bashonly)
- **eplus**: [Handle URLs without videos](https://github.com/yt-dlp/yt-dlp/commit/351dc0bc334c4e1b5f00c152818c3ec0ed71f788) ([#9855](https://github.com/yt-dlp/yt-dlp/issues/9855)) by [pzhlkj6612](https://github.com/pzhlkj6612)
- **europarlwebstream**: [Support new URL format](https://github.com/yt-dlp/yt-dlp/commit/800a43983e5fb719526ce4cb3956216085c63268) ([#9647](https://github.com/yt-dlp/yt-dlp/issues/9647)) by [seproDev](https://github.com/seproDev), [voidful](https://github.com/voidful)
- **facebook**: [Fix DASH formats extraction](https://github.com/yt-dlp/yt-dlp/commit/e3b42d8b1b8bcfff7ba146c19fc3f6f6ba843cea) ([#9734](https://github.com/yt-dlp/yt-dlp/issues/9734)) by [bashonly](https://github.com/bashonly)
- **godresource**: [Add extractor](https://github.com/yt-dlp/yt-dlp/commit/65e709d23530959075816e966c42179ad46e8e3b) ([#9629](https://github.com/yt-dlp/yt-dlp/issues/9629)) by [HobbyistDev](https://github.com/HobbyistDev)
- **googledrive**: [Fix formats extraction](https://github.com/yt-dlp/yt-dlp/commit/85ec2a337ac325cf6427cbafd56f0a034c1a5218) ([#9908](https://github.com/yt-dlp/yt-dlp/issues/9908)) by [WyohKnott](https://github.com/WyohKnott)
- **hearthisat**: [Improve `_VALID_URL`](https://github.com/yt-dlp/yt-dlp/commit/5bbfdb7c999b22f1aeca0c3489c167d6eb73013b) ([#9949](https://github.com/yt-dlp/yt-dlp/issues/9949)) by [bohwaz](https://github.com/bohwaz), [seproDev](https://github.com/seproDev)
- **hytale**: [Use `CloudflareStreamIE` explicitly](https://github.com/yt-dlp/yt-dlp/commit/31b417e1d1ccc67d5c027bf8878f483dc34cb118) ([#9672](https://github.com/yt-dlp/yt-dlp/issues/9672)) by [llamasblade](https://github.com/llamasblade)
- **instagram**: [Support `/reels/` URLs](https://github.com/yt-dlp/yt-dlp/commit/06cb0638392b607b47d3c2ac48eb2ebecb0f060d) ([#9539](https://github.com/yt-dlp/yt-dlp/issues/9539)) by [amir16yp](https://github.com/amir16yp)
- **jiocinema**: [Add extractors](https://github.com/yt-dlp/yt-dlp/commit/1463945ae5fb05986a0bd1aa02e41d1a08d93a02) ([#10026](https://github.com/yt-dlp/yt-dlp/issues/10026)) by [bashonly](https://github.com/bashonly)
- **jiosaavn**: [Extract via API and fix playlists](https://github.com/yt-dlp/yt-dlp/commit/0c21c53885cf03f4040467ae8c44d7ff51016116) ([#9656](https://github.com/yt-dlp/yt-dlp/issues/9656)) by [bashonly](https://github.com/bashonly)
- **lci**: [Fix extractor](https://github.com/yt-dlp/yt-dlp/commit/5a2eebc76770fca91ffabeff658d560f716fec80) ([#10025](https://github.com/yt-dlp/yt-dlp/issues/10025)) by [ocococococ](https://github.com/ocococococ)
- **mixch**: [Extract comments](https://github.com/yt-dlp/yt-dlp/commit/b38018b781b062d5169d104ab430489aef8e7f1e) ([#9860](https://github.com/yt-dlp/yt-dlp/issues/9860)) by [pzhlkj6612](https://github.com/pzhlkj6612)
- **moviepilot**: [Fix extractor](https://github.com/yt-dlp/yt-dlp/commit/296df0da1d38a44d34c99b60a18066c301774537) ([#9366](https://github.com/yt-dlp/yt-dlp/issues/9366)) by [panatexxa](https://github.com/panatexxa)
- **netease**: program: [Improve `--no-playlist` message](https://github.com/yt-dlp/yt-dlp/commit/73f12119b52d98281804b0c072b2ed6aa841ec88) ([#9488](https://github.com/yt-dlp/yt-dlp/issues/9488)) by [pzhlkj6612](https://github.com/pzhlkj6612)
- **nfb**: [Fix extractors](https://github.com/yt-dlp/yt-dlp/commit/0a1a8e3005f66c44bf67633dccd4df19c3fccd1a) ([#9650](https://github.com/yt-dlp/yt-dlp/issues/9650)) by [rrgomes](https://github.com/rrgomes)
- **ntslive**: [Add extractor](https://github.com/yt-dlp/yt-dlp/commit/be7db1a5a8c483726c511c30ea4689cbb8b27962) ([#9641](https://github.com/yt-dlp/yt-dlp/issues/9641)) by [lostfictions](https://github.com/lostfictions)
- **orf**: on: [Improve extraction](https://github.com/yt-dlp/yt-dlp/commit/0dd53faeca2ba0ce138e4092d07b5f2dbf2422f9) ([#9677](https://github.com/yt-dlp/yt-dlp/issues/9677)) by [TuxCoder](https://github.com/TuxCoder)
- **orftvthek**: [Remove extractor](https://github.com/yt-dlp/yt-dlp/commit/3779f2a307ba3ef1d28e107cdd71b221dfb4eb36) ([#10011](https://github.com/yt-dlp/yt-dlp/issues/10011)) by [seproDev](https://github.com/seproDev)
- **patreon**
- [Extract multiple embeds](https://github.com/yt-dlp/yt-dlp/commit/036e0d92c6052465673d459678322ea03e61483d) ([#9850](https://github.com/yt-dlp/yt-dlp/issues/9850)) by [bashonly](https://github.com/bashonly)
- [Fix Vimeo embed extraction](https://github.com/yt-dlp/yt-dlp/commit/c9ce57d9bf51541da2381d99bc096a9d0ddf1f27) ([#9712](https://github.com/yt-dlp/yt-dlp/issues/9712)) by [bashonly](https://github.com/bashonly)
- **piapro**: [Fix extractor](https://github.com/yt-dlp/yt-dlp/commit/3ba8de62d61d782256f5c1e9939a0762039657de) ([#9311](https://github.com/yt-dlp/yt-dlp/issues/9311)) by [FinnRG](https://github.com/FinnRG), [seproDev](https://github.com/seproDev)
- **pornhub**: [Fix login by email address](https://github.com/yt-dlp/yt-dlp/commit/518c1afc1592cae3e4eb39dc646b5bc059333112) ([#9914](https://github.com/yt-dlp/yt-dlp/issues/9914)) by [feederbox826](https://github.com/feederbox826)
- **qub**: [Fix extractor](https://github.com/yt-dlp/yt-dlp/commit/6b54cccdcb892bca3e55993480d8b86f1c7e6da6) ([#7019](https://github.com/yt-dlp/yt-dlp/issues/7019)) by [alexhuot1](https://github.com/alexhuot1), [dirkf](https://github.com/dirkf)
- **reddit**: [Fix subtitles extraction](https://github.com/yt-dlp/yt-dlp/commit/82f4f4444e26daf35b7302c406fe2312f78f619e) ([#10006](https://github.com/yt-dlp/yt-dlp/issues/10006)) by [kclauhk](https://github.com/kclauhk)
- **soundcloud**
- [Add `formats` extractor-arg](https://github.com/yt-dlp/yt-dlp/commit/beaf832c7a9d57833f365ce18f6115b88071b296) ([#10004](https://github.com/yt-dlp/yt-dlp/issues/10004)) by [bashonly](https://github.com/bashonly), [Grub4K](https://github.com/Grub4K)
- [Extract `genres`](https://github.com/yt-dlp/yt-dlp/commit/231c2eacc41b06b65c63edf94c0d04768a5da607) ([#9821](https://github.com/yt-dlp/yt-dlp/issues/9821)) by [bashonly](https://github.com/bashonly)
- **taptap**: [Add extractors](https://github.com/yt-dlp/yt-dlp/commit/63b569bc5e7d461753637a20ad84a575adee4c0a) ([#9776](https://github.com/yt-dlp/yt-dlp/issues/9776)) by [c-basalt](https://github.com/c-basalt)
- **tele5**: [Overhaul extractor](https://github.com/yt-dlp/yt-dlp/commit/c92e4e625e9e6bbbbf8e3b20c3e7ebe57c16072d) ([#10024](https://github.com/yt-dlp/yt-dlp/issues/10024)) by [bashonly](https://github.com/bashonly)
- **theatercomplextown**: [Fix extractors](https://github.com/yt-dlp/yt-dlp/commit/8056a3026ed6ec6a6d0ed56fdd7ebcd16e928341) ([#9754](https://github.com/yt-dlp/yt-dlp/issues/9754)) by [bashonly](https://github.com/bashonly)
- **tiktok**
- [Add `device_id` extractor-arg](https://github.com/yt-dlp/yt-dlp/commit/3584b8390bd21c0393a3079eeee71aed56a1c1d8) ([#9951](https://github.com/yt-dlp/yt-dlp/issues/9951)) by [bashonly](https://github.com/bashonly)
- [Extract all web formats](https://github.com/yt-dlp/yt-dlp/commit/4ccd73fea0f6f4be343e1ec7f22dd03799addcf8) ([#9960](https://github.com/yt-dlp/yt-dlp/issues/9960)) by [bashonly](https://github.com/bashonly)
- [Extract via mobile API only if extractor-arg is passed](https://github.com/yt-dlp/yt-dlp/commit/41ba4a808b597a3afed78c89675a30deb6844450) ([#9938](https://github.com/yt-dlp/yt-dlp/issues/9938)) by [bashonly](https://github.com/bashonly)
- [Fix subtitles extraction](https://github.com/yt-dlp/yt-dlp/commit/eef1e9f44ff14c5e65b759bb1eafa3946cdaf719) ([#9961](https://github.com/yt-dlp/yt-dlp/issues/9961)) by [bashonly](https://github.com/bashonly)
- collection: [Add extractor](https://github.com/yt-dlp/yt-dlp/commit/119d41f27061d220d276a2d38cfc8d873437452a) ([#9986](https://github.com/yt-dlp/yt-dlp/issues/9986)) by [bashonly](https://github.com/bashonly), [imanoreotwe](https://github.com/imanoreotwe)
- user: [Fix extractor](https://github.com/yt-dlp/yt-dlp/commit/347f13dd9bccc2b4db3ea25689410d45d8370ed4) ([#9661](https://github.com/yt-dlp/yt-dlp/issues/9661)) by [bashonly](https://github.com/bashonly)
- **tv5monde**: [Fix extractor](https://github.com/yt-dlp/yt-dlp/commit/6db96268c521e945d42649607db1574f5d92e082) ([#9143](https://github.com/yt-dlp/yt-dlp/issues/9143)) by [alard](https://github.com/alard), [seproDev](https://github.com/seproDev)
- **twitter**
- [Fix auth for x.com migration](https://github.com/yt-dlp/yt-dlp/commit/3e35aa32c74bc108375be8c8b6b3bfc90dfff1b4) ([#9952](https://github.com/yt-dlp/yt-dlp/issues/9952)) by [bashonly](https://github.com/bashonly)
- [Support x.com URLs](https://github.com/yt-dlp/yt-dlp/commit/4813173e4544f125d6f2afc31e600727d761b8dd) ([#9926](https://github.com/yt-dlp/yt-dlp/issues/9926)) by [bashonly](https://github.com/bashonly)
- **vk**: [Improve format extraction](https://github.com/yt-dlp/yt-dlp/commit/df5c9e733aaba703cf285c0372b6d61629330c82) ([#9885](https://github.com/yt-dlp/yt-dlp/issues/9885)) by [seproDev](https://github.com/seproDev)
- **wrestleuniverse**: [Avoid partial stream formats](https://github.com/yt-dlp/yt-dlp/commit/c4853655cb9a793129280806af643de43c48f4d5) ([#9800](https://github.com/yt-dlp/yt-dlp/issues/9800)) by [bashonly](https://github.com/bashonly)
- **xiaohongshu**: [Add extractor](https://github.com/yt-dlp/yt-dlp/commit/a2e9031605d87c469be9ce98dbbdf4960b727338) ([#9646](https://github.com/yt-dlp/yt-dlp/issues/9646)) by [HobbyistDev](https://github.com/HobbyistDev)
- **xvideos**: quickies: [Fix extractor](https://github.com/yt-dlp/yt-dlp/commit/b207d26f83fb8ab0ce56df74dff43ff583a3264f) ([#9834](https://github.com/yt-dlp/yt-dlp/issues/9834)) by [JakeFinley96](https://github.com/JakeFinley96)
- **youporn**: [Fix extractor](https://github.com/yt-dlp/yt-dlp/commit/351368cb9a6731b886a58f5a10fd6b302bbe47be) ([#8827](https://github.com/yt-dlp/yt-dlp/issues/8827)) by [The-MAGI](https://github.com/The-MAGI)
- **youtube**
- [Add `mediaconnect` client](https://github.com/yt-dlp/yt-dlp/commit/cf212d0a331aba05c32117573f760cdf3af8c62f) ([#9546](https://github.com/yt-dlp/yt-dlp/issues/9546)) by [clienthax](https://github.com/clienthax)
- [Extract upload timestamp if available](https://github.com/yt-dlp/yt-dlp/commit/96a134dea6397a5f2131947c427aac52c8b4e677) ([#9856](https://github.com/yt-dlp/yt-dlp/issues/9856)) by [coletdjnz](https://github.com/coletdjnz)
- [Fix comments extraction](https://github.com/yt-dlp/yt-dlp/commit/8e15177b4113c355989881e4e030f695a9b59c3a) ([#9775](https://github.com/yt-dlp/yt-dlp/issues/9775)) by [bbilly1](https://github.com/bbilly1), [jakeogh](https://github.com/jakeogh), [minamotorin](https://github.com/minamotorin), [shoxie007](https://github.com/shoxie007)
- [Remove `android` from default clients](https://github.com/yt-dlp/yt-dlp/commit/12d8ea8246fa901de302ff5cc748caddadc82f41) ([#9553](https://github.com/yt-dlp/yt-dlp/issues/9553)) by [bashonly](https://github.com/bashonly), [coletdjnz](https://github.com/coletdjnz)
- **zenyandex**: [Fix extractor](https://github.com/yt-dlp/yt-dlp/commit/c4b87dd885ee5391e5f481e7c8bd550a7c543623) ([#9813](https://github.com/yt-dlp/yt-dlp/issues/9813)) by [src-tinkerer](https://github.com/src-tinkerer)
#### Networking changes
- [Add `extensions` attribute to `Response`](https://github.com/yt-dlp/yt-dlp/commit/bec9a59e8ec82c18e3bf9268eaa436793dd52e35) ([#9756](https://github.com/yt-dlp/yt-dlp/issues/9756)) by [bashonly](https://github.com/bashonly)
- **Request Handler**
- requests
- [Patch support for `requests` 2.32.2+](https://github.com/yt-dlp/yt-dlp/commit/3f7999533ebe41c2a579d91b4e4cb211cfcd3bc0) ([#9992](https://github.com/yt-dlp/yt-dlp/issues/9992)) by [Grub4K](https://github.com/Grub4K)
- [Update to `requests` 2.32.0](https://github.com/yt-dlp/yt-dlp/commit/c36513f1be2ef3d3cec864accbffda1afaa06ffd) ([#9980](https://github.com/yt-dlp/yt-dlp/issues/9980)) by [coletdjnz](https://github.com/coletdjnz)
#### Misc. changes
- [Add `hatch`, `ruff`, `pre-commit` and improve dev docs](https://github.com/yt-dlp/yt-dlp/commit/e897bd8292a41999cf51dba91b390db5643c72db) ([#7409](https://github.com/yt-dlp/yt-dlp/issues/7409)) by [bashonly](https://github.com/bashonly), [Grub4K](https://github.com/Grub4K), [seproDev](https://github.com/seproDev)
- **build**
- [Migrate `linux_exe` to static musl builds](https://github.com/yt-dlp/yt-dlp/commit/ac817bc83efd939dca3e40c4b527d0ccfc77172b) ([#9811](https://github.com/yt-dlp/yt-dlp/issues/9811)) by [bashonly](https://github.com/bashonly), [Grub4K](https://github.com/Grub4K)
- [Normalize `curl_cffi` group to `curl-cffi`](https://github.com/yt-dlp/yt-dlp/commit/02483bea1c4dbe1bace8ca4d19700104fbb8a00f) ([#9698](https://github.com/yt-dlp/yt-dlp/issues/9698)) by [bashonly](https://github.com/bashonly) (With fixes in [89f535e](https://github.com/yt-dlp/yt-dlp/commit/89f535e2656964b4061c25a7739d4d6ba0a30568))
- [Run `macos_legacy` job on `macos-12`](https://github.com/yt-dlp/yt-dlp/commit/1a366403d9c26b992faa77e00f4d02ead57559e3) ([#9804](https://github.com/yt-dlp/yt-dlp/issues/9804)) by [bashonly](https://github.com/bashonly)
- [`macos` job requires `setuptools<70`](https://github.com/yt-dlp/yt-dlp/commit/78c57cc0e0998b8ed90e4306f410aa4be4115cd7) ([#9993](https://github.com/yt-dlp/yt-dlp/issues/9993)) by [bashonly](https://github.com/bashonly)
- **cleanup**
- [Remove questionable extractors](https://github.com/yt-dlp/yt-dlp/commit/01395a34345d1c6ba1b73ca92f94dd200dc45341) ([#9911](https://github.com/yt-dlp/yt-dlp/issues/9911)) by [seproDev](https://github.com/seproDev)
- Miscellaneous: [5c019f6](https://github.com/yt-dlp/yt-dlp/commit/5c019f6328ad40d66561eac3c4de0b3cd070d0f6), [ae2af11](https://github.com/yt-dlp/yt-dlp/commit/ae2af1104f80caf2f47544763a33db2c17a3e1de) by [bashonly](https://github.com/bashonly), [Grub4K](https://github.com/Grub4K), [seproDev](https://github.com/seproDev)
- **test**
- [Add HTTP proxy tests](https://github.com/yt-dlp/yt-dlp/commit/3c7a287e281d9f9a353dce8902ff78a84c24a040) ([#9578](https://github.com/yt-dlp/yt-dlp/issues/9578)) by [coletdjnz](https://github.com/coletdjnz)
- [Fix connect timeout test](https://github.com/yt-dlp/yt-dlp/commit/53b4d44f55cca66ac33dab092ef2a30b1164b684) ([#9906](https://github.com/yt-dlp/yt-dlp/issues/9906)) by [coletdjnz](https://github.com/coletdjnz)
### 2024.04.09
#### Important changes

View File

@@ -27,7 +27,7 @@ clean-dist:
yt_dlp/extractor/lazy_extractors.py *.spec CONTRIBUTING.md.tmp yt-dlp yt-dlp.exe yt_dlp.egg-info/ AUTHORS
clean-cache:
find . \( \
-type d -name .pytest_cache -o -type d -name __pycache__ -o -name "*.pyc" -o -name "*.class" \
-type d -name ".*_cache" -o -type d -name __pycache__ -o -name "*.pyc" -o -name "*.class" \
\) -prune -exec rm -rf {} \;
completion-bash: completions/bash/yt-dlp
@@ -70,14 +70,15 @@ uninstall:
rm -f $(DESTDIR)$(SHAREDIR)/fish/vendor_completions.d/yt-dlp.fish
codetest:
flake8 .
ruff check .
autopep8 --diff .
test:
$(PYTHON) -m pytest
$(PYTHON) -m pytest -Werror
$(MAKE) codetest
offlinetest: codetest
$(PYTHON) -m pytest -k "not download"
$(PYTHON) -m pytest -Werror -m "not download"
CODE_FOLDERS_CMD = find yt_dlp -type f -name '__init__.py' | sed 's,/__init__.py,,' | grep -v '/__' | sort
CODE_FOLDERS != $(CODE_FOLDERS_CMD)
@@ -151,7 +152,7 @@ yt-dlp.tar.gz: all
--exclude '*.pyo' \
--exclude '*~' \
--exclude '__pycache__' \
--exclude '.pytest_cache' \
--exclude '.*_cache' \
--exclude '.git' \
-- \
README.md supportedsites.md Changelog.md LICENSE \

View File

@@ -108,7 +108,6 @@ File|Description
[yt-dlp_x86.exe](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_x86.exe)|Windows (Win7 SP1+) standalone x86 (32-bit) binary
[yt-dlp_min.exe](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_min.exe)|Windows (Win7 SP1+) standalone x64 binary built with `py2exe`<br/> ([Not recommended](#standalone-py2exe-builds-windows))
[yt-dlp_linux](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_linux)|Linux standalone x64 binary
[yt-dlp_linux.zip](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_linux.zip)|Unpackaged Linux executable (no auto-update)
[yt-dlp_linux_armv7l](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_linux_armv7l)|Linux standalone armv7l (32-bit) binary
[yt-dlp_linux_aarch64](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_linux_aarch64)|Linux standalone aarch64 (64-bit) binary
[yt-dlp_win.zip](https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_win.zip)|Unpackaged Windows executable (no auto-update)
@@ -170,7 +169,7 @@ Example usage:
yt-dlp --update-to nightly
# To install nightly with pip:
python3 -m pip install -U --pre yt-dlp[default]
python3 -m pip install -U --pre "yt-dlp[default]"
```
## DEPENDENCIES
@@ -202,7 +201,7 @@ While all the other dependencies are optional, `ffmpeg` and `ffprobe` are highly
The following provide support for impersonating browser requests. This may be required for some sites that employ TLS fingerprinting.
* [**curl_cffi**](https://github.com/yifeikong/curl_cffi) (recommended) - Python binding for [curl-impersonate](https://github.com/lwthiker/curl-impersonate). Provides impersonation targets for Chrome, Edge and Safari. Licensed under [MIT](https://github.com/yifeikong/curl_cffi/blob/main/LICENSE)
* Can be installed with the `curl-cffi` group, e.g. `pip install yt-dlp[default,curl-cffi]`
* Can be installed with the `curl-cffi` group, e.g. `pip install "yt-dlp[default,curl-cffi]"`
* Currently only included in `yt-dlp.exe` and `yt-dlp_macos` builds
@@ -666,16 +665,17 @@ If you fork the project on GitHub, you can run your fork's [build workflow](.git
The name of the browser to load cookies
from. Currently supported browsers are:
brave, chrome, chromium, edge, firefox,
opera, safari, vivaldi, whale. Optionally, the
KEYRING used for decrypting Chromium cookies
on Linux, the name/path of the PROFILE to
load cookies from, and the CONTAINER name
(if Firefox) ("none" for no container) can
be given with their respective seperators.
By default, all containers of the most
recently accessed profile are used.
Currently supported keyrings are: basictext,
gnomekeyring, kwallet, kwallet5, kwallet6
opera, safari, vivaldi, whale. Optionally,
the KEYRING used for decrypting Chromium
cookies on Linux, the name/path of the
PROFILE to load cookies from, and the
CONTAINER name (if Firefox) ("none" for no
container) can be given with their
respective seperators. By default, all
containers of the most recently accessed
profile are used. Currently supported
keyrings are: basictext, gnomekeyring,
kwallet, kwallet5, kwallet6
--no-cookies-from-browser Do not load cookies from browser (default)
--cache-dir DIR Location in the filesystem where yt-dlp can
store some downloaded information (such as
@@ -1751,7 +1751,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=android_embedded,web;include_live_dash" --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=android_embedded,web;formats=incomplete" --extractor-args "funimation:version=uncut"`
Note: In CLI, `ARG` can use `-` instead of `_`; e.g. `youtube:player-client"` becomes `youtube:player_client"`
@@ -2123,7 +2123,7 @@ with yt_dlp.YoutubeDL(ydl_opts) as ydl:
### New features
* Forked from [**yt-dlc@f9401f2**](https://github.com/blackjack4494/yt-dlc/commit/f9401f2a91987068139c5f757b12fc711d4c0cee) and merged with [**youtube-dl@be008e6**](https://github.com/ytdl-org/youtube-dl/commit/be008e657d79832642e2158557c899249c9e31cd) ([exceptions](https://github.com/yt-dlp/yt-dlp/issues/21))
* Forked from [**yt-dlc@f9401f2**](https://github.com/blackjack4494/yt-dlc/commit/f9401f2a91987068139c5f757b12fc711d4c0cee) and merged with [**youtube-dl@a08f2b7**](https://github.com/ytdl-org/youtube-dl/commit/a08f2b7e4567cdc50c0614ee0a4ffdff49b8b6e6) ([exceptions](https://github.com/yt-dlp/yt-dlp/issues/21))
* **[SponsorBlock Integration](#sponsorblock-options)**: You can mark/remove sponsor sections in YouTube videos by utilizing the [SponsorBlock](https://sponsor.ajay.app) API
@@ -2333,6 +2333,7 @@ These options may no longer work as intended
--write-annotations No supported site has annotations now
--no-write-annotations Default
--compat-options seperate-video-versions No longer needed
--compat-options no-youtube-prefer-utc-upload-date No longer supported
#### Removed
These options were deprecated since 2014 and have now been entirely removed

View File

@@ -147,5 +147,27 @@
"action": "add",
"when": "9590cc6b4768e190183d7d071a6c78170889116a",
"short": "[priority] Security: [[CVE-2024-22423](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-22423)] [Prevent RCE when using `--exec` with `%q` on Windows](https://github.com/yt-dlp/yt-dlp/security/advisories/GHSA-hjq6-52gw-2g7p)\n - The shell escape function now properly escapes `%`, `\\` and `\\n`.\n - `utils.Popen` has been patched accordingly."
},
{
"action": "change",
"when": "41ba4a808b597a3afed78c89675a30deb6844450",
"short": "[ie/tiktok] Extract via mobile API only if extractor-arg is passed (#9938)",
"authors": ["bashonly"]
},
{
"action": "remove",
"when": "6e36d17f404556f0e3a43f441c477a71a91877d9"
},
{
"action": "change",
"when": "beaf832c7a9d57833f365ce18f6115b88071b296",
"short": "[ie/soundcloud] Add `formats` extractor-arg (#10004)",
"authors": ["bashonly", "Grub4K"]
},
{
"action": "change",
"when": "5c019f6328ad40d66561eac3c4de0b3cd070d0f6",
"short": "[cleanup] Misc (#9765)",
"authors": ["bashonly", "Grub4K", "seproDev"]
}
]

View File

@@ -42,17 +42,25 @@ def parse_args():
def main():
args = parse_args()
project_table = parse_toml(read_file(args.input))['project']
recursive_pattern = re.compile(rf'{project_table["name"]}\[(?P<group_name>[\w-]+)\]')
optional_groups = project_table['optional-dependencies']
excludes = args.exclude or []
def yield_deps(group):
for dep in group:
if mobj := recursive_pattern.fullmatch(dep):
yield from optional_groups.get(mobj.group('group_name'), [])
else:
yield dep
targets = []
if not args.only_optional: # `-o` should exclude 'dependencies' and the 'default' group
targets.extend(project_table['dependencies'])
if 'default' not in excludes: # `--exclude default` should exclude entire 'default' group
targets.extend(optional_groups['default'])
targets.extend(yield_deps(optional_groups['default']))
for include in filter(None, map(optional_groups.get, args.include or [])):
targets.extend(include)
targets.extend(yield_deps(include))
targets = [t for t in targets if re.match(r'[\w-]+', t).group(0).lower() not in excludes]

View File

@@ -1,4 +0,0 @@
@echo off
>&2 echo run_tests.bat is deprecated. Please use `devscripts/run_tests.py` instead
python %~dp0run_tests.py %~1

View File

@@ -4,6 +4,7 @@ import argparse
import functools
import os
import re
import shlex
import subprocess
import sys
from pathlib import Path
@@ -18,6 +19,8 @@ def parse_args():
'test', help='a extractor tests, or one of "core" or "download"', nargs='*')
parser.add_argument(
'-k', help='run a test matching EXPRESSION. Same as "pytest -k"', metavar='EXPRESSION')
parser.add_argument(
'--pytest-args', help='arguments to passthrough to pytest')
return parser.parse_args()
@@ -26,15 +29,16 @@ def run_tests(*tests, pattern=None, ci=False):
run_download = 'download' in tests
tests = list(map(fix_test_name, tests))
arguments = ['pytest', '-Werror', '--tb=short']
pytest_args = args.pytest_args or os.getenv('HATCH_TEST_ARGS', '')
arguments = ['pytest', '-Werror', '--tb=short', *shlex.split(pytest_args)]
if ci:
arguments.append('--color=yes')
if pattern:
arguments.extend(['-k', pattern])
if run_core:
arguments.extend(['-m', 'not download'])
elif run_download:
arguments.extend(['-m', 'download'])
elif pattern:
arguments.extend(['-k', pattern])
else:
arguments.extend(
f'test/test_download.py::TestDownload::test_{test}' for test in tests)
@@ -46,13 +50,13 @@ def run_tests(*tests, pattern=None, ci=False):
pass
arguments = [sys.executable, '-Werror', '-m', 'unittest']
if pattern:
arguments.extend(['-k', pattern])
if run_core:
print('"pytest" needs to be installed to run core tests', file=sys.stderr, flush=True)
return 1
elif run_download:
arguments.append('test.test_download')
elif pattern:
arguments.extend(['-k', pattern])
else:
arguments.extend(
f'test.test_download.TestDownload.test_{test}' for test in tests)

View File

@@ -1,4 +0,0 @@
#!/usr/bin/env sh
>&2 echo 'run_tests.sh is deprecated. Please use `devscripts/run_tests.py` instead'
python3 devscripts/run_tests.py "$1"

View File

@@ -1,17 +0,0 @@
#!/usr/bin/env python3
# Allow execution from anywhere
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import warnings
from bundle.pyinstaller import main
warnings.warn(DeprecationWarning('`pyinst.py` is deprecated and will be removed in a future version. '
'Use `bundle.pyinstaller` instead'))
if __name__ == '__main__':
main()

View File

@@ -66,9 +66,16 @@ build = [
"wheel",
]
dev = [
"flake8",
"isort",
"pytest",
"pre-commit",
"yt-dlp[static-analysis]",
"yt-dlp[test]",
]
static-analysis = [
"autopep8~=2.0",
"ruff~=0.4.4",
]
test = [
"pytest~=8.1",
]
pyinstaller = [
"pyinstaller>=6.3; sys_platform!='darwin'",
@@ -126,3 +133,146 @@ artifacts = ["/yt_dlp/extractor/lazy_extractors.py"]
[tool.hatch.version]
path = "yt_dlp/version.py"
pattern = "_pkg_version = '(?P<version>[^']+)'"
[tool.hatch.envs.default]
features = ["curl-cffi", "default"]
dependencies = ["pre-commit"]
path = ".venv"
installer = "uv"
[tool.hatch.envs.default.scripts]
setup = "pre-commit install --config .pre-commit-hatch.yaml"
yt-dlp = "python -Werror -Xdev -m yt_dlp {args}"
[tool.hatch.envs.hatch-static-analysis]
detached = true
features = ["static-analysis"]
dependencies = [] # override hatch ruff version
config-path = "pyproject.toml"
[tool.hatch.envs.hatch-static-analysis.scripts]
format-check = "autopep8 --diff {args:.}"
format-fix = "autopep8 --in-place {args:.}"
lint-check = "ruff check {args:.}"
lint-fix = "ruff check --fix {args:.}"
[tool.hatch.envs.hatch-test]
features = ["test"]
dependencies = [
"pytest-randomly~=3.15",
"pytest-rerunfailures~=14.0",
"pytest-xdist[psutil]~=3.5",
]
[tool.hatch.envs.hatch-test.scripts]
run = "python -m devscripts.run_tests {args}"
run-cov = "echo Code coverage not implemented && exit 1"
[[tool.hatch.envs.hatch-test.matrix]]
python = [
"3.8",
"3.9",
"3.10",
"3.11",
"3.12",
"pypy3.8",
"pypy3.9",
"pypy3.10",
]
[tool.ruff]
line-length = 120
[tool.ruff.lint]
ignore = [
"E402", # module level import not at top of file
"E501", # line too long
"E731", # do not assign a lambda expression, use a def
"E741", # ambiguous variable name
]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # import order
]
[tool.ruff.lint.per-file-ignores]
"devscripts/lazy_load_template.py" = ["F401"]
"!yt_dlp/extractor/**.py" = ["I"]
[tool.ruff.lint.isort]
known-first-party = [
"bundle",
"devscripts",
"test",
]
relative-imports-order = "closest-to-furthest"
[tool.autopep8]
max_line_length = 120
recursive = true
exit-code = true
jobs = 0
select = [
"E101",
"E112",
"E113",
"E115",
"E116",
"E117",
"E121",
"E122",
"E123",
"E124",
"E125",
"E126",
"E127",
"E128",
"E129",
"E131",
"E201",
"E202",
"E203",
"E211",
"E221",
"E222",
"E223",
"E224",
"E225",
"E226",
"E227",
"E228",
"E231",
"E241",
"E242",
"E251",
"E252",
"E261",
"E262",
"E265",
"E266",
"E271",
"E272",
"E273",
"E274",
"E275",
"E301",
"E302",
"E303",
"E304",
"E305",
"E306",
"E502",
"E701",
"E702",
"E704",
"W391",
"W504",
]
[tool.pytest.ini_options]
addopts = "-ra -v --strict-markers"
markers = [
"download",
]

View File

@@ -14,12 +14,6 @@ remove-duplicate-keys = true
remove-unused-variables = true
[tool:pytest]
addopts = -ra -v --strict-markers
markers =
download
[tox:tox]
skipsdist = true
envlist = py{38,39,310,311,312},pypy{38,39,310}

View File

@@ -1,36 +0,0 @@
#!/usr/bin/env python3
# Allow execution from anywhere
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import warnings
if sys.argv[1:2] == ['py2exe']:
warnings.warn(DeprecationWarning('`setup.py py2exe` is deprecated and will be removed in a future version. '
'Use `bundle.py2exe` instead'))
import bundle.py2exe
bundle.py2exe.main()
elif 'build_lazy_extractors' in sys.argv:
warnings.warn(DeprecationWarning('`setup.py build_lazy_extractors` is deprecated and will be removed in a future version. '
'Use `devscripts.make_lazy_extractors` instead'))
import subprocess
os.chdir(sys.path[0])
print('running build_lazy_extractors')
subprocess.run([sys.executable, 'devscripts/make_lazy_extractors.py'])
else:
print(
'ERROR: Building by calling `setup.py` is deprecated. '
'Use a build frontend like `build` instead. ',
'Refer to https://build.pypa.io for more info', file=sys.stderr)
sys.exit(1)

View File

@@ -14,7 +14,6 @@
- **6play**
- **7plus**
- **8tracks**
- **91porn**
- **9c9media**
- **9gag**: 9GAG
- **9News**
@@ -220,7 +219,7 @@
- **BusinessInsider**
- **BuzzFeed**
- **BYUtv**: (**Currently broken**)
- **CableAV**
- **CaffeineTV**
- **Callin**
- **Caltrans**
- **CAM4**
@@ -333,6 +332,8 @@
- **DailyWirePodcast**
- **damtomo:record**
- **damtomo:video**
- **dangalplay**: [*dangalplay*](## "netrc machine")
- **dangalplay:season**: [*dangalplay*](## "netrc machine")
- **daum.net**
- **daum.net:clip**
- **daum.net:playlist**
@@ -396,7 +397,6 @@
- **EinsUndEinsTV**: [*1und1tv*](## "netrc machine")
- **EinsUndEinsTVLive**: [*1und1tv*](## "netrc machine")
- **EinsUndEinsTVRecordings**: [*1und1tv*](## "netrc machine")
- **Einthusan**
- **eitb.tv**
- **ElementorEmbed**
- **Elonet**
@@ -498,6 +498,7 @@
- **GameStar**
- **Gaskrank**
- **Gazeta**: (**Currently broken**)
- **GBNews**: GB News clips, features and live streams
- **GDCVault**: [*gdcvault*](## "netrc machine") (**Currently broken**)
- **GediDigital**
- **gem.cbc.ca**: [*cbcgem*](## "netrc machine")
@@ -527,6 +528,7 @@
- **GMANetworkVideo**
- **Go**
- **GoDiscovery**
- **GodResource**
- **GodTube**: (**Currently broken**)
- **Gofile**
- **Golem**
@@ -630,11 +632,11 @@
- **iwara:user**: [*iwara*](## "netrc machine")
- **Ixigua**
- **Izlesene**
- **Jable**
- **JablePlaylist**
- **Jamendo**
- **JamendoAlbum**
- **JeuxVideo**: (**Currently broken**)
- **jiocinema**: [*jiocinema*](## "netrc machine")
- **jiocinema:series**: [*jiocinema*](## "netrc machine")
- **jiosaavn:album**
- **jiosaavn:playlist**
- **jiosaavn:song**
@@ -974,6 +976,7 @@
- **NRKTVSeason**
- **NRKTVSeries**
- **NRLTV**: (**Currently broken**)
- **nts.live**
- **ntv.ru**
- **NubilesPorn**: [*nubiles-porn*](## "netrc machine")
- **nuum:live**
@@ -1015,7 +1018,6 @@
- **orf:on**
- **orf:podcast**
- **orf:radio**
- **orf:tvthek**: ORF TVthek
- **OsnatelTV**: [*osnateltv*](## "netrc machine")
- **OsnatelTVLive**: [*osnateltv*](## "netrc machine")
- **OsnatelTVRecordings**: [*osnateltv*](## "netrc machine")
@@ -1394,6 +1396,10 @@
- **SztvHu**
- **t-online.de**: (**Currently broken**)
- **Tagesschau**: (**Currently broken**)
- **TapTapApp**
- **TapTapAppIntl**
- **TapTapMoment**
- **TapTapPostIntl**
- **Tass**: (**Currently broken**)
- **TBS**
- **TBSJPEpisode**
@@ -1412,7 +1418,7 @@
- **TedSeries**
- **TedTalk**
- **Tele13**
- **Tele5**: (**Currently broken**)
- **Tele5**
- **TeleBruxelles**
- **TelecaribePlay**
- **Telecinco**: telecinco.es, cuatro.com and mediaset.es
@@ -1452,11 +1458,12 @@
- **ThreeSpeak**
- **ThreeSpeakUser**
- **TikTok**
- **tiktok:collection**
- **tiktok:effect**: (**Currently broken**)
- **tiktok:live**
- **tiktok:sound**: (**Currently broken**)
- **tiktok:tag**: (**Currently broken**)
- **tiktok:user**: (**Currently broken**)
- **tiktok:user**
- **TLC**
- **TMZ**
- **TNAFlix**
@@ -1501,7 +1508,7 @@
- **tv2play.hu**
- **tv2playseries.hu**
- **TV4**: tv4.se and tv4play.se
- **TV5MondePlus**: TV5MONDE+
- **TV5MONDE**
- **tv5unis**
- **tv5unis:video**
- **tv8.it**
@@ -1639,8 +1646,6 @@
- **voicy**: (**Currently broken**)
- **voicy:channel**: (**Currently broken**)
- **VolejTV**
- **Voot**: [*voot*](## "netrc machine") (**Currently broken**)
- **VootSeries**: [*voot*](## "netrc machine") (**Currently broken**)
- **VoxMedia**
- **VoxMediaVolume**
- **vpro**: npo.nl, ntr.nl, omroepwnl.nl, zapp.nl and npo3.nl
@@ -1715,10 +1720,10 @@
- **wykop:post:comment**
- **Xanimu**
- **XboxClips**
- **XFileShare**: XFileShare based sites: Aparat, ClipWatching, GoUnlimited, GoVid, HolaVid, Streamty, TheVideoBee, Uqload, VidBom, vidlo, VidLocker, VidShare, VUp, WolfStream, XVideoSharing
- **XHamster**
- **XHamsterEmbed**
- **XHamsterUser**
- **XiaoHongShu**: 小红书
- **ximalaya**: 喜马拉雅FM
- **ximalaya:album**: 喜马拉雅FM 专辑
- **xinpianchang**: xinpianchang.com (**Currently broken**)
@@ -1749,8 +1754,12 @@
- **YouNowLive**
- **YouNowMoment**
- **YouPorn**
- **YourPorn**
- **YourUpload**
- **YouPornCategory**: YouPorn category, with sorting, filtering and pagination
- **YouPornChannel**: YouPorn channel, with sorting and pagination
- **YouPornCollection**: YouPorn collection (user playlist), with sorting and pagination
- **YouPornStar**: YouPorn Pornstar, with description, sorting and pagination
- **YouPornTag**: YouPorn tag (porntags), with sorting, filtering and pagination
- **YouPornVideos**: YouPorn video (browse) playlists, with sorting, filtering and pagination
- **youtube**: YouTube
- **youtube:clip**
- **youtube:favorites**: YouTube liked videos; ":ytfav" keyword (requires cookies)

View File

@@ -1912,7 +1912,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/
self.assertEqual(self.ie._search_nextjs_data('', None, fatal=False), {})
self.assertEqual(self.ie._search_nextjs_data('', None, default=None), None)
self.assertEqual(self.ie._search_nextjs_data('', None, default={}), {})
with self.assertRaises(DeprecationWarning):
with self.assertWarns(DeprecationWarning):
self.assertEqual(self.ie._search_nextjs_data('', None, default='{}'), {})

View File

@@ -93,6 +93,7 @@ if urllib3:
This allows us to chain multiple TLS connections.
"""
def __init__(self, socket, ssl_context, server_hostname=None, suppress_ragged_eofs=True, server_side=False):
self.incoming = ssl.MemoryBIO()
self.outgoing = ssl.MemoryBIO()

View File

@@ -5,6 +5,7 @@ import os
import sys
import unittest
import warnings
import datetime as dt
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
@@ -27,6 +28,7 @@ from yt_dlp.utils import (
ExtractorError,
InAdvancePagedList,
LazyList,
NO_DEFAULT,
OnDemandPagedList,
Popen,
age_restricted,
@@ -768,6 +770,11 @@ class TestUtil(unittest.TestCase):
def test_parse_iso8601(self):
self.assertEqual(parse_iso8601('2014-03-23T23:04:26+0100'), 1395612266)
self.assertEqual(parse_iso8601('2014-03-23T23:04:26-07:00'), 1395641066)
self.assertEqual(parse_iso8601('2014-03-23T23:04:26', timezone=dt.timedelta(hours=-7)), 1395641066)
self.assertEqual(parse_iso8601('2014-03-23T23:04:26', timezone=NO_DEFAULT), None)
# default does not override timezone in date_str
self.assertEqual(parse_iso8601('2014-03-23T23:04:26-07:00', timezone=dt.timedelta(hours=-10)), 1395641066)
self.assertEqual(parse_iso8601('2014-03-23T22:04:26+0000'), 1395612266)
self.assertEqual(parse_iso8601('2014-03-23T22:04:26Z'), 1395612266)
self.assertEqual(parse_iso8601('2014-03-23T22:04:26.1234Z'), 1395612266)

File diff suppressed because it is too large Load Diff

View File

@@ -6,10 +6,10 @@ import time
from .common import InfoExtractor
from ..compat import compat_str
from ..utils import (
dict_get,
ExtractorError,
js_to_json,
dict_get,
int_or_none,
js_to_json,
parse_iso8601,
str_or_none,
traverse_obj,

View File

@@ -12,20 +12,21 @@ import urllib.parse
import urllib.request
import urllib.response
import uuid
from ..utils.networking import clean_proxies
from .common import InfoExtractor
from ..aes import aes_ecb_decrypt
from ..utils import (
ExtractorError,
OnDemandPagedList,
bytes_to_intlist,
decode_base_n,
int_or_none,
intlist_to_bytes,
OnDemandPagedList,
time_seconds,
traverse_obj,
update_url_query,
)
from ..utils.networking import clean_proxies
def add_opener(ydl, handler): # FIXME: Create proper API in .networking

View File

@@ -3,10 +3,10 @@ from ..utils import (
float_or_none,
format_field,
int_or_none,
str_or_none,
traverse_obj,
parse_codecs,
parse_qs,
str_or_none,
traverse_obj,
)

View File

@@ -10,18 +10,18 @@ from ..aes import aes_cbc_decrypt_bytes, unpad_pkcs7
from ..compat import compat_b64decode
from ..networking.exceptions import HTTPError
from ..utils import (
ExtractorError,
ass_subtitles_timecode,
bytes_to_intlist,
bytes_to_long,
ExtractorError,
float_or_none,
int_or_none,
intlist_to_bytes,
long_to_bytes,
parse_iso8601,
pkcs1pad,
strip_or_none,
str_or_none,
strip_or_none,
try_get,
unified_strdate,
urlencode_postdata,

View File

@@ -4,11 +4,11 @@ import re
from .common import InfoExtractor
from ..compat import compat_str
from ..utils import (
ISO639Utils,
OnDemandPagedList,
float_or_none,
int_or_none,
ISO639Utils,
join_nonempty,
OnDemandPagedList,
parse_duration,
str_or_none,
str_to_int,

View File

@@ -5,7 +5,7 @@ from ..utils import (
int_or_none,
mimetype2ext,
parse_iso8601,
traverse_obj
traverse_obj,
)

View File

@@ -12,7 +12,6 @@ from ..utils import (
)
from ..utils.traversal import traverse_obj
_FIELDS = '''
_id
clipImageSource

View File

@@ -1,9 +1,9 @@
from .common import InfoExtractor
from ..utils import (
parse_iso8601,
int_or_none,
parse_duration,
parse_filesize,
int_or_none,
parse_iso8601,
)

View File

@@ -1,17 +1,13 @@
import re
from .common import InfoExtractor
from ..compat import (
compat_urlparse,
)
from ..compat import compat_urlparse
from ..utils import (
ExtractorError,
clean_html,
int_or_none,
urlencode_postdata,
urljoin,
int_or_none,
clean_html,
ExtractorError
)

View File

@@ -1,6 +1,6 @@
from .common import InfoExtractor
from .youtube import YoutubeIE
from .vimeo import VimeoIE
from .youtube import YoutubeIE
from ..utils import (
int_or_none,
parse_iso8601,

View File

@@ -1,7 +1,7 @@
from .common import InfoExtractor
from ..utils import (
determine_ext,
ExtractorError,
determine_ext,
int_or_none,
mimetype2ext,
parse_iso8601,

View File

@@ -5,7 +5,7 @@ from ..utils import (
int_or_none,
str_or_none,
traverse_obj,
unified_timestamp
unified_timestamp,
)

View File

@@ -1,7 +1,7 @@
import re
from .common import InfoExtractor
from ..utils import url_or_none, merge_dicts
from ..utils import merge_dicts, url_or_none
class AngelIE(InfoExtractor):

View File

@@ -1,8 +1,5 @@
from .common import InfoExtractor
from ..utils import (
str_to_int,
ExtractorError
)
from ..utils import ExtractorError, str_to_int
class AppleConnectIE(InfoExtractor):

View File

@@ -1,5 +1,5 @@
import re
import json
import re
from .common import InfoExtractor
from ..compat import compat_urlparse

View File

@@ -4,8 +4,8 @@ from ..compat import (
compat_urllib_parse_urlparse,
)
from ..utils import (
format_field,
float_or_none,
format_field,
int_or_none,
parse_iso8601,
remove_start,

View File

@@ -2,10 +2,10 @@ import datetime as dt
from .common import InfoExtractor
from ..utils import (
ExtractorError,
float_or_none,
jwt_encode_hs256,
try_get,
ExtractorError,
)

View File

@@ -2,8 +2,8 @@ import base64
from .common import InfoExtractor
from ..compat import (
compat_urllib_parse_urlencode,
compat_str,
compat_urllib_parse_urlencode,
)
from ..utils import (
format_field,

View File

@@ -2,12 +2,12 @@ import math
from .common import InfoExtractor
from ..compat import (
compat_urllib_parse_urlparse,
compat_parse_qs,
compat_urllib_parse_urlparse,
)
from ..utils import (
format_field,
InAdvancePagedList,
format_field,
traverse_obj,
unified_timestamp,
)

View File

@@ -2,11 +2,11 @@ import json
from .common import InfoExtractor
from ..utils import (
try_get,
int_or_none,
url_or_none,
float_or_none,
int_or_none,
try_get,
unified_timestamp,
url_or_none,
)

View File

@@ -1,5 +1,4 @@
from .common import InfoExtractor
from ..utils import (
int_or_none,
str_or_none,

View File

@@ -1,5 +1,5 @@
from .common import InfoExtractor
from .amp import AMPIE
from .common import InfoExtractor
from ..utils import (
ExtractorError,
int_or_none,

View File

@@ -1,3 +1,4 @@
from .common import InfoExtractor
from ..utils import (
mimetype2ext,
parse_duration,
@@ -5,7 +6,6 @@ from ..utils import (
str_or_none,
traverse_obj,
)
from .common import InfoExtractor
class BloggerIE(InfoExtractor):

View File

@@ -1,7 +1,6 @@
import re
from .common import InfoExtractor
from ..utils import (
extract_attributes,
)

View File

@@ -1,9 +1,5 @@
from .common import InfoExtractor
from ..utils import (
js_to_json,
traverse_obj,
unified_timestamp
)
from ..utils import js_to_json, traverse_obj, unified_timestamp
class BoxCastVideoIE(InfoExtractor):

View File

@@ -6,7 +6,7 @@ from ..utils import (
classproperty,
int_or_none,
traverse_obj,
urljoin
urljoin,
)

Some files were not shown because too many files have changed in this diff Show More