mirror of
https://github.com/yt-dlp/yt-dlp.git
synced 2026-05-19 13:52:36 +00:00
[cleanup] Misc (#16452)
* Include `pin*` extras in lockfile * Fix and clean up `devscripts/update_requirements.py` * Improve release channel documentation * Remove false statement from `--prefer-insecure` documentation * Assorted code cleanup * Set `GH_TELEMETRY=false` in CI/CD whenever `gh` is used * Add comments about required checks in CI workflows * Run `test-workflows.yml` for every PR so its checks can be required * Verify actionlint attestation in CI * Remove zizmor version to reduce workflow maintenance burden (zizmor-action handles pinning on its end) Authored by: bashonly
This commit is contained in:
parent
27973bae5e
commit
35684c1171
4
.github/workflows/challenge-tests.yml
vendored
4
.github/workflows/challenge-tests.yml
vendored
@ -1,6 +1,7 @@
|
||||
name: Challenge Tests
|
||||
on:
|
||||
push:
|
||||
branches: ['master']
|
||||
paths:
|
||||
- .github/workflows/challenge-tests.yml
|
||||
- test/test_jsc/*.py
|
||||
@ -9,6 +10,7 @@ on:
|
||||
- yt_dlp/extractor/youtube/pot/**.py
|
||||
- yt_dlp/utils/_jsruntime.py
|
||||
pull_request:
|
||||
branches: ['**']
|
||||
paths:
|
||||
- .github/workflows/challenge-tests.yml
|
||||
- test/test_jsc/*.py
|
||||
@ -25,7 +27,7 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
name: Challenge Tests
|
||||
name: Challenge tests
|
||||
if: ${{ !contains(github.event.head_commit.message, ':ci skip') }}
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
4
.github/workflows/core.yml
vendored
4
.github/workflows/core.yml
vendored
@ -1,6 +1,7 @@
|
||||
name: Core Tests
|
||||
on:
|
||||
push:
|
||||
branches: ['master']
|
||||
paths:
|
||||
- pyproject.toml
|
||||
- .github/**
|
||||
@ -13,6 +14,7 @@ on:
|
||||
- yt_dlp/extractor/common.py
|
||||
- yt_dlp/extractor/extractors.py
|
||||
pull_request:
|
||||
branches: ['**']
|
||||
paths:
|
||||
- pyproject.toml
|
||||
- .github/**
|
||||
@ -33,7 +35,7 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
name: Core Tests
|
||||
name: Core tests
|
||||
if: ${{ !contains(github.event.head_commit.message, ':ci skip') }}
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
7
.github/workflows/issue-lockdown.yml
vendored
7
.github/workflows/issue-lockdown.yml
vendored
@ -5,9 +5,12 @@ on:
|
||||
|
||||
permissions: {}
|
||||
|
||||
env:
|
||||
GH_TELEMETRY: "false"
|
||||
|
||||
jobs:
|
||||
lockdown:
|
||||
name: Issue Lockdown
|
||||
name: Issue lockdown
|
||||
if: vars.ISSUE_LOCKDOWN
|
||||
permissions:
|
||||
issues: write # Needed to lock issues
|
||||
@ -19,4 +22,4 @@ jobs:
|
||||
ISSUE_NUMBER: ${{ github.event.issue.number }}
|
||||
REPOSITORY: ${{ github.repository }}
|
||||
run: |
|
||||
gh issue lock "${ISSUE_NUMBER}" -R "${REPOSITORY}"
|
||||
gh issue lock -R "${REPOSITORY}" "${ISSUE_NUMBER}"
|
||||
|
||||
14
.github/workflows/quick-test.yml
vendored
14
.github/workflows/quick-test.yml
vendored
@ -1,5 +1,10 @@
|
||||
name: Quick Test
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
push:
|
||||
branches: ['master']
|
||||
# This workflow contains required checks and needs to run for EVERY pull_request
|
||||
pull_request:
|
||||
branches: ['**']
|
||||
|
||||
permissions: {}
|
||||
|
||||
@ -9,7 +14,8 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
name: Core Test
|
||||
# Required check; do not change name
|
||||
name: Core test
|
||||
if: ${{ !contains(github.event.head_commit.message, ':ci skip all') }}
|
||||
permissions:
|
||||
contents: read
|
||||
@ -31,7 +37,9 @@ jobs:
|
||||
run: |
|
||||
python3 -m yt_dlp -v || true
|
||||
python3 ./devscripts/run_tests.py --pytest-args '--reruns 2 --reruns-delay 3.0' core
|
||||
|
||||
check:
|
||||
# Required check; do not change name
|
||||
name: Code check
|
||||
if: ${{ !contains(github.event.head_commit.message, ':ci skip all') }}
|
||||
permissions:
|
||||
@ -43,7 +51,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: '3.10'
|
||||
python-version: '3.14'
|
||||
- name: Install dev dependencies
|
||||
run: python ./devscripts/install_deps.py --omit-default --include-group static-analysis
|
||||
- name: Make lazy extractors
|
||||
|
||||
2
.github/workflows/release-master.yml
vendored
2
.github/workflows/release-master.yml
vendored
@ -19,7 +19,7 @@ permissions: {}
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Publish Github release
|
||||
name: Publish GitHub release
|
||||
if: vars.BUILD_MASTER
|
||||
permissions:
|
||||
contents: write # May be needed to publish release
|
||||
|
||||
2
.github/workflows/release-nightly.yml
vendored
2
.github/workflows/release-nightly.yml
vendored
@ -70,7 +70,7 @@ jobs:
|
||||
run: echo "${HEAD}" | tee .nightly_commit_hash
|
||||
|
||||
release:
|
||||
name: Publish Github release
|
||||
name: Publish GitHub release
|
||||
needs: [check_nightly]
|
||||
if: needs.check_nightly.outputs.commit
|
||||
permissions:
|
||||
|
||||
5
.github/workflows/release.yml
vendored
5
.github/workflows/release.yml
vendored
@ -65,6 +65,9 @@ on:
|
||||
|
||||
permissions: {}
|
||||
|
||||
env:
|
||||
GH_TELEMETRY: "false"
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
name: Prepare
|
||||
@ -226,7 +229,7 @@ jobs:
|
||||
verbose: true
|
||||
|
||||
publish:
|
||||
name: Publish Github release
|
||||
name: Publish GitHub release
|
||||
needs: [prepare, build]
|
||||
permissions:
|
||||
contents: write # Needed by gh to publish release to Github
|
||||
|
||||
38
.github/workflows/test-workflows.yml
vendored
38
.github/workflows/test-workflows.yml
vendored
@ -1,23 +1,10 @@
|
||||
name: Test and lint workflows
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
paths:
|
||||
- .github/*.yml
|
||||
- .github/workflows/*
|
||||
- bundle/docker/linux/*.sh
|
||||
- devscripts/setup_variables.py
|
||||
- devscripts/setup_variables_tests.py
|
||||
- devscripts/utils.py
|
||||
branches: ['master']
|
||||
# This workflow contains required checks and needs to run for EVERY pull_request
|
||||
pull_request:
|
||||
branches: [master]
|
||||
paths:
|
||||
- .github/*.yml
|
||||
- .github/workflows/*
|
||||
- bundle/docker/linux/*.sh
|
||||
- devscripts/setup_variables.py
|
||||
- devscripts/setup_variables_tests.py
|
||||
- devscripts/utils.py
|
||||
branches: ['**']
|
||||
|
||||
permissions: {}
|
||||
|
||||
@ -28,10 +15,12 @@ concurrency:
|
||||
env:
|
||||
ACTIONLINT_VERSION: "1.7.11"
|
||||
ACTIONLINT_SHA256SUM: 900919a84f2229bac68ca9cd4103ea297abc35e9689ebb842c6e34a3d1b01b0a
|
||||
ACTIONLINT_REPO: https://github.com/rhysd/actionlint
|
||||
ACTIONLINT_REPO: rhysd/actionlint
|
||||
GH_TELEMETRY: "false"
|
||||
|
||||
jobs:
|
||||
check:
|
||||
# Required check; do not change name
|
||||
name: Check workflows
|
||||
permissions:
|
||||
contents: read
|
||||
@ -45,19 +34,26 @@ jobs:
|
||||
python-version: "3.13" # Keep this in sync with release.yml's prepare job
|
||||
- name: Install requirements
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
ACTIONLINT_TARBALL: ${{ format('actionlint_{0}_linux_amd64.tar.gz', env.ACTIONLINT_VERSION) }}
|
||||
shell: bash
|
||||
run: |
|
||||
python -m devscripts.install_deps --omit-default --include-group test
|
||||
sudo apt -y install shellcheck
|
||||
python -m pip install -U pyflakes
|
||||
curl -LO "${ACTIONLINT_REPO}/releases/download/v${ACTIONLINT_VERSION}/${ACTIONLINT_TARBALL}"
|
||||
gh release download \
|
||||
--repo "${ACTIONLINT_REPO}" \
|
||||
--pattern "${ACTIONLINT_TARBALL}" \
|
||||
"v${ACTIONLINT_VERSION}"
|
||||
gh attestation verify \
|
||||
--repo "${ACTIONLINT_REPO}" \
|
||||
"${ACTIONLINT_TARBALL}"
|
||||
printf '%s %s' "${ACTIONLINT_SHA256SUM}" "${ACTIONLINT_TARBALL}" | sha256sum -c -
|
||||
tar xvzf "${ACTIONLINT_TARBALL}" actionlint
|
||||
chmod +x actionlint
|
||||
sudo install -D --mode=755 actionlint /usr/bin/
|
||||
- name: Run actionlint
|
||||
run: |
|
||||
./actionlint -color
|
||||
actionlint -color
|
||||
- name: Check Docker shell scripts
|
||||
run: |
|
||||
shellcheck bundle/docker/linux/*.sh
|
||||
@ -66,6 +62,7 @@ jobs:
|
||||
pytest -Werror --tb=short --color=yes devscripts/setup_variables_tests.py
|
||||
|
||||
zizmor:
|
||||
# Required check; do not change name
|
||||
name: Run zizmor
|
||||
permissions:
|
||||
contents: read
|
||||
@ -80,4 +77,3 @@ jobs:
|
||||
with:
|
||||
advanced-security: false
|
||||
persona: pedantic
|
||||
version: v1.23.1
|
||||
|
||||
7
Makefile
7
Makefile
@ -3,7 +3,7 @@ all-extra: lazy-extractors yt-dlp-extra doc pypi-files
|
||||
clean: clean-test clean-dist
|
||||
clean-all: clean clean-cache
|
||||
completions: completion-bash completion-fish completion-zsh
|
||||
doc: README.md CONTRIBUTING.md CONTRIBUTORS issuetemplates supportedsites
|
||||
doc: README.md CONTRIBUTORS issuetemplates supportedsites
|
||||
ot: offlinetest
|
||||
tar: yt-dlp.tar.gz
|
||||
|
||||
@ -30,7 +30,7 @@ clean-test:
|
||||
test/testdata/sigs/player-*.js test/testdata/thumbnails/empty.webp "test/testdata/thumbnails/foo %d bar/foo_%d."*
|
||||
clean-dist:
|
||||
rm -rf yt-dlp.1.temp.md yt-dlp.1 README.txt MANIFEST build/ dist/ .coverage cover/ yt-dlp.tar.gz completions/ \
|
||||
yt_dlp/extractor/lazy_extractors.py *.spec CONTRIBUTING.md.tmp yt-dlp yt-dlp.exe yt_dlp.egg-info/ AUTHORS \
|
||||
yt_dlp/extractor/lazy_extractors.py *.spec yt-dlp yt-dlp.exe yt_dlp.egg-info/ AUTHORS \
|
||||
yt-dlp.zip .ejs-* yt_dlp_ejs/
|
||||
clean-cache:
|
||||
find . \( \
|
||||
@ -132,9 +132,6 @@ yt-dlp: yt-dlp.zip
|
||||
README.md: $(PY_CODE_FILES) devscripts/make_readme.py
|
||||
COLUMNS=80 $(PYTHON) yt_dlp/__main__.py --ignore-config --help | $(PYTHON) devscripts/make_readme.py
|
||||
|
||||
CONTRIBUTING.md: README.md devscripts/make_contributing.py
|
||||
$(PYTHON) devscripts/make_contributing.py README.md CONTRIBUTING.md
|
||||
|
||||
issuetemplates: devscripts/make_issue_template.py .github/ISSUE_TEMPLATE_tmpl/1_broken_site.yml .github/ISSUE_TEMPLATE_tmpl/2_site_support_request.yml .github/ISSUE_TEMPLATE_tmpl/3_site_feature_request.yml .github/ISSUE_TEMPLATE_tmpl/4_bug_report.yml .github/ISSUE_TEMPLATE_tmpl/5_feature_request.yml yt_dlp/version.py
|
||||
$(PYTHON) devscripts/make_issue_template.py .github/ISSUE_TEMPLATE_tmpl/1_broken_site.yml .github/ISSUE_TEMPLATE/1_broken_site.yml
|
||||
$(PYTHON) devscripts/make_issue_template.py .github/ISSUE_TEMPLATE_tmpl/2_site_support_request.yml .github/ISSUE_TEMPLATE/2_site_support_request.yml
|
||||
|
||||
@ -167,9 +167,9 @@ For other third-party package managers, see [the wiki](https://github.com/yt-dlp
|
||||
|
||||
There are currently three release channels for binaries: `stable`, `nightly` and `master`.
|
||||
|
||||
* `stable` is the default channel, and many of its changes have been tested by users of the `nightly` and `master` channels.
|
||||
* The `nightly` channel has releases scheduled to build every day around midnight UTC, for a snapshot of the project's new patches and changes. This is the **recommended channel for regular users** of yt-dlp. The `nightly` releases are available from [yt-dlp/yt-dlp-nightly-builds](https://github.com/yt-dlp/yt-dlp-nightly-builds/releases) or as development releases of the `yt-dlp` PyPI package (which can be installed with pip's `--pre` flag).
|
||||
* The `master` channel features releases that are built after each push to the master branch, and these will have the very latest fixes and additions, but may also be more prone to regressions. They are available from [yt-dlp/yt-dlp-master-builds](https://github.com/yt-dlp/yt-dlp-master-builds/releases).
|
||||
* `stable` is the default channel, which offers releases published on a (mostly) monthly schedule. While it is named `stable` due to many of its changes having been tested by users of the `nightly` or `master` release channels, the latest `stable` release is often "stale" and prone to external breakage (i.e. sites changing things on their end and breaking yt-dlp).
|
||||
* The `nightly` channel offers releases that publish shortly before midnight UTC on any day that sees changes to the codebase. This channel serves as a snapshot of the project's development, and it is the **recommended channel for regular users** of yt-dlp. The `nightly` releases are available from [yt-dlp/yt-dlp-nightly-builds](https://github.com/yt-dlp/yt-dlp-nightly-builds/releases) or as development releases of the `yt-dlp` PyPI package (which can be installed with pip's `--pre` flag).
|
||||
* The `master` channel offers "canary" releases that publish after each push to the master branch. This channel will always provide the very latest fixes and features, but may be prone to bugs or regressions. The `master` releases are available from [yt-dlp/yt-dlp-master-builds](https://github.com/yt-dlp/yt-dlp-master-builds/releases).
|
||||
|
||||
When using `--update`/`-U`, a release binary will only update to its current channel.
|
||||
`--update-to CHANNEL` can be used to switch to a different channel when a newer version is available. `--update-to [CHANNEL@]TAG` can also be used to upgrade or downgrade to specific tags from a channel.
|
||||
|
||||
@ -1,32 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import optparse
|
||||
import re
|
||||
|
||||
|
||||
def main():
|
||||
return # This is unused in yt-dlp
|
||||
|
||||
parser = optparse.OptionParser(usage='%prog INFILE OUTFILE')
|
||||
_, args = parser.parse_args()
|
||||
if len(args) != 2:
|
||||
parser.error('Expected an input and an output filename')
|
||||
|
||||
infile, outfile = args
|
||||
|
||||
with open(infile, encoding='utf-8') as inf:
|
||||
readme = inf.read()
|
||||
|
||||
bug_text = re.search(
|
||||
r'(?s)#\s*BUGS\s*[^\n]*\s*(.*?)#\s*COPYRIGHT', readme).group(1)
|
||||
dev_text = re.search(
|
||||
r'(?s)(#\s*DEVELOPER INSTRUCTIONS.*?)#\s*EMBEDDING yt-dlp', readme).group(1)
|
||||
|
||||
out = bug_text + dev_text
|
||||
|
||||
with open(outfile, 'w', encoding='utf-8') as outf:
|
||||
outf.write(out)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@ -47,12 +47,6 @@ PINNED_EXTRAS = {
|
||||
'pin-deno': 'deno',
|
||||
}
|
||||
|
||||
WELLKNOWN_PACKAGES = {
|
||||
'deno': {'owner': 'denoland', 'repo': 'deno'},
|
||||
'protobug': {'owner': 'yt-dlp', 'repo': 'protobug'},
|
||||
'yt-dlp-ejs': {'owner': 'yt-dlp', 'repo': 'ejs'},
|
||||
}
|
||||
|
||||
EJS_ASSETS = {
|
||||
'yt.solver.lib.js': False,
|
||||
'yt.solver.lib.min.js': False,
|
||||
@ -120,10 +114,15 @@ PYINSTALLER_BUILDS_TARGETS = {
|
||||
'win-arm64-pyinstaller': 'win_arm64',
|
||||
}
|
||||
|
||||
PYINSTALLER_BUILDS_TMPL = '''\
|
||||
{}pyinstaller @ {} \\
|
||||
--hash={}
|
||||
'''
|
||||
WELLKNOWN_PACKAGES = {
|
||||
'deno': {'owner': 'denoland', 'repo': 'deno'},
|
||||
'protobug': {'owner': 'yt-dlp', 'repo': 'protobug'},
|
||||
'yt-dlp-ejs': {'owner': 'yt-dlp', 'repo': 'ejs'},
|
||||
**{
|
||||
f'pyinstaller[{asset_tag}]': {'owner': 'pyinstaller', 'repo': 'pyinstaller'}
|
||||
for asset_tag in PYINSTALLER_BUILDS_TARGETS.values()
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def call_pypi_api(project: str) -> dict[str, dict[str, typing.Any]]:
|
||||
@ -447,7 +446,7 @@ def parse_version_from_dist(filename: str, name: str, *, require: bool = False)
|
||||
normalized_name = re.sub(r'[-_.]+', '-', name).lower().replace('-', '_')
|
||||
|
||||
# Ref: https://packaging.python.org/en/latest/specifications/version-specifiers/#version-specifiers
|
||||
if mobj := re.match(rf'{normalized_name}-(?P<version>[^-]+)-', filename):
|
||||
if mobj := re.fullmatch(rf'{normalized_name}-(?P<version>[^-]+)(?:-.+\.whl|\.tar\.gz)', filename):
|
||||
return mobj.group('version')
|
||||
|
||||
if require:
|
||||
@ -579,27 +578,33 @@ def update_requirements(
|
||||
all_updates = package_diff_dict(old_packages, new_packages)
|
||||
|
||||
# Update Windows PyInstaller requirements; need to compare before & after .txt's for reporting
|
||||
if not upgrade_only or upgrade_only.lower() == 'pyinstaller':
|
||||
if not upgrade_only:
|
||||
info = fetch_latest_github_release('yt-dlp', 'Pyinstaller-Builds')
|
||||
for target_suffix, asset_tag in PYINSTALLER_BUILDS_TARGETS.items():
|
||||
asset_info = next(asset for asset in info['assets'] if asset_tag in asset['name'])
|
||||
pyinstaller_version = parse_version_from_dist(
|
||||
asset_info['name'], 'pyinstaller', require=True)
|
||||
pyinstaller_builds_deps = run_pip_compile(
|
||||
'--no-emit-package=pyinstaller',
|
||||
upgrade_arg,
|
||||
input_line=f'pyinstaller=={pyinstaller_version}',
|
||||
env=env)
|
||||
|
||||
requirements_path = REQUIREMENTS_PATH / REQS_OUTPUT_TMPL.format(target_suffix)
|
||||
if requirements_path.is_file():
|
||||
old_requirements_txt = requirements_path.read_text()
|
||||
else:
|
||||
old_requirements_txt = ''
|
||||
|
||||
new_requirements_txt = PYINSTALLER_BUILDS_TMPL.format(
|
||||
pyinstaller_builds_deps, asset_info['browser_download_url'], asset_info['digest'])
|
||||
requirements_path.write_text(new_requirements_txt)
|
||||
all_updates.update(evaluate_requirements_txt(old_requirements_txt, new_requirements_txt))
|
||||
run_pip_compile(
|
||||
upgrade_arg,
|
||||
input_line=f'pyinstaller @ {asset_info["browser_download_url"]}',
|
||||
output_file=requirements_path,
|
||||
env=env)
|
||||
|
||||
new_requirements_txt = requirements_path.read_text()
|
||||
if asset_info['digest'] not in new_requirements_txt:
|
||||
raise ValueError(
|
||||
f'expected pyinstaller wheel hash {asset_info["digest"]} '
|
||||
f'not found in {requirements_path}')
|
||||
|
||||
diff_dict = evaluate_requirements_txt(old_requirements_txt, new_requirements_txt)
|
||||
if pyinstaller_diff := diff_dict.get('pyinstaller'):
|
||||
# NB: this depends on 'pyinstaller[asset_tag]' keys in WELLKNOWN_PACKAGES
|
||||
all_updates.update({f'pyinstaller[{asset_tag}]': pyinstaller_diff})
|
||||
|
||||
# Export bundle requirements; any updates to these are already recorded w/ uv.lock package diff
|
||||
for target_suffix, target in BUNDLE_TARGETS.items():
|
||||
@ -640,6 +645,10 @@ def update_requirements(
|
||||
# Write the finalized pyproject.toml
|
||||
modify_and_write_pyproject(pyproject_text, table_name=EXTRAS_TABLE, table=extras)
|
||||
|
||||
# Generate/upgrade final lockfile that includes pinned extras
|
||||
print(f'Running: uv lock {upgrade_arg}', file=sys.stderr)
|
||||
run_process('uv', 'lock', upgrade_arg, env=env)
|
||||
|
||||
return all_updates
|
||||
|
||||
|
||||
@ -682,7 +691,9 @@ def generate_report(
|
||||
|
||||
if offset is not None:
|
||||
md_old = '.'.join(old_parts[:offset]) + '.***' + '.'.join(old_parts[offset:]) + '***'
|
||||
md_old = md_old.lstrip('.')
|
||||
md_new = '.'.join(new_parts[:offset]) + '.***' + '.'.join(new_parts[offset:]) + '***'
|
||||
md_new = md_new.lstrip('.')
|
||||
|
||||
compare = ''
|
||||
if github_info:
|
||||
@ -692,14 +703,15 @@ def generate_report(
|
||||
new_tag = tags_info.get(new) and tags_info[new]['name']
|
||||
github_url = 'https://github.com/{owner}/{repo}'.format(**github_info)
|
||||
if new_tag:
|
||||
md_new = f'[{md_new}]({github_url}/releases/tag/{new_tag})'
|
||||
md_new = f'[{md_new}](<{github_url}/releases/tag/{new_tag}>)'
|
||||
if old_tag:
|
||||
md_old = f'[{md_old}]({github_url}/releases/tag/{old_tag})'
|
||||
md_old = f'[{md_old}](<{github_url}/releases/tag/{old_tag}>)'
|
||||
if new_tag and old_tag:
|
||||
compare = f'[`{old_tag}...{new_tag}`]({github_url}/compare/{old_tag}...{new_tag})'
|
||||
compare = f'[`{old_tag}...{new_tag}`](<{github_url}/compare/{old_tag}...{new_tag}>)'
|
||||
|
||||
yield ' | '.join((
|
||||
f'[**`{package}`**](https://pypi.org/project/{package})',
|
||||
# Strip the [win*] tag from package in the URL (e.g. pyinstaller[win32])
|
||||
f'[**`{package}`**](<https://pypi.org/project/{package.split("[")[0]}/>)',
|
||||
md_old,
|
||||
md_new,
|
||||
compare,
|
||||
|
||||
@ -180,9 +180,6 @@ exclude = [
|
||||
path = "yt_dlp/version.py"
|
||||
pattern = "_pkg_version = '(?P<version>[^']+)'"
|
||||
|
||||
[tool.hatch.metadata]
|
||||
allow-direct-references = true
|
||||
|
||||
[tool.hatch.envs.default]
|
||||
features = [
|
||||
"curl-cffi",
|
||||
|
||||
66
uv.lock
generated
66
uv.lock
generated
@ -1166,6 +1166,42 @@ default = [
|
||||
deno = [
|
||||
{ name = "deno" },
|
||||
]
|
||||
pin = [
|
||||
{ name = "brotli", marker = "implementation_name == 'cpython' and sys_platform != 'ios'" },
|
||||
{ name = "brotlicffi", marker = "implementation_name != 'cpython'" },
|
||||
{ name = "certifi" },
|
||||
{ name = "cffi", marker = "implementation_name != 'cpython'" },
|
||||
{ name = "charset-normalizer" },
|
||||
{ name = "idna" },
|
||||
{ name = "mutagen" },
|
||||
{ name = "pycparser", marker = "implementation_name != 'PyPy' and implementation_name != 'cpython'" },
|
||||
{ name = "pycryptodomex" },
|
||||
{ name = "requests" },
|
||||
{ name = "urllib3" },
|
||||
{ name = "websockets" },
|
||||
{ name = "yt-dlp-ejs" },
|
||||
]
|
||||
pin-curl-cffi = [
|
||||
{ name = "certifi", marker = "implementation_name == 'cpython'" },
|
||||
{ name = "cffi", marker = "implementation_name == 'cpython'" },
|
||||
{ name = "curl-cffi", marker = "implementation_name == 'cpython'" },
|
||||
{ name = "markdown-it-py", marker = "implementation_name == 'cpython'" },
|
||||
{ name = "mdurl", marker = "implementation_name == 'cpython'" },
|
||||
{ name = "pycparser", marker = "implementation_name == 'cpython'" },
|
||||
{ name = "pygments", marker = "implementation_name == 'cpython'" },
|
||||
{ name = "rich", marker = "implementation_name == 'cpython'" },
|
||||
]
|
||||
pin-deno = [
|
||||
{ name = "deno" },
|
||||
]
|
||||
pin-secretstorage = [
|
||||
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
|
||||
{ name = "cryptography" },
|
||||
{ name = "jeepney" },
|
||||
{ name = "pycparser", marker = "implementation_name != 'PyPy' and platform_python_implementation != 'PyPy'" },
|
||||
{ name = "secretstorage" },
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
|
||||
]
|
||||
secretstorage = [
|
||||
{ name = "secretstorage" },
|
||||
]
|
||||
@ -1200,19 +1236,47 @@ test = [
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "brotli", marker = "implementation_name == 'cpython' and sys_platform != 'ios' and extra == 'default'" },
|
||||
{ name = "brotli", marker = "implementation_name == 'cpython' and sys_platform != 'ios' and extra == 'pin'", specifier = "==1.2.0" },
|
||||
{ name = "brotlicffi", marker = "implementation_name != 'cpython' and extra == 'default'" },
|
||||
{ name = "brotlicffi", marker = "implementation_name != 'cpython' and extra == 'pin'", specifier = "==1.2.0.1" },
|
||||
{ name = "certifi", marker = "implementation_name == 'cpython' and extra == 'pin-curl-cffi'", specifier = "==2026.2.25" },
|
||||
{ name = "certifi", marker = "extra == 'default'" },
|
||||
{ name = "certifi", marker = "extra == 'pin'", specifier = "==2026.2.25" },
|
||||
{ name = "cffi", marker = "platform_python_implementation != 'PyPy' and extra == 'pin-secretstorage'", specifier = "==2.0.0" },
|
||||
{ name = "cffi", marker = "implementation_name == 'cpython' and extra == 'pin-curl-cffi'", specifier = "==2.0.0" },
|
||||
{ name = "cffi", marker = "implementation_name != 'cpython' and extra == 'pin'", specifier = "==2.0.0" },
|
||||
{ name = "charset-normalizer", marker = "extra == 'pin'", specifier = "==3.4.6" },
|
||||
{ name = "cryptography", marker = "extra == 'pin-secretstorage'", specifier = "==46.0.6" },
|
||||
{ name = "curl-cffi", marker = "implementation_name == 'cpython' and extra == 'curl-cffi'", specifier = ">=0.5.10,!=0.6.*,!=0.7.*,!=0.8.*,!=0.9.*,<0.16" },
|
||||
{ name = "curl-cffi", marker = "implementation_name == 'cpython' and extra == 'pin-curl-cffi'", specifier = "==0.15.0" },
|
||||
{ name = "deno", marker = "extra == 'deno'", specifier = ">=2.6.6" },
|
||||
{ name = "deno", marker = "extra == 'pin-deno'", specifier = "==2.7.8" },
|
||||
{ name = "idna", marker = "extra == 'pin'", specifier = "==3.11" },
|
||||
{ name = "jeepney", marker = "extra == 'pin-secretstorage'", specifier = "==0.9.0" },
|
||||
{ name = "markdown-it-py", marker = "implementation_name == 'cpython' and extra == 'pin-curl-cffi'", specifier = "==4.0.0" },
|
||||
{ name = "mdurl", marker = "implementation_name == 'cpython' and extra == 'pin-curl-cffi'", specifier = "==0.1.2" },
|
||||
{ name = "mutagen", marker = "extra == 'default'" },
|
||||
{ name = "mutagen", marker = "extra == 'pin'", specifier = "==1.47.0" },
|
||||
{ name = "pycparser", marker = "implementation_name != 'PyPy' and platform_python_implementation != 'PyPy' and extra == 'pin-secretstorage'", specifier = "==3.0" },
|
||||
{ name = "pycparser", marker = "implementation_name != 'PyPy' and implementation_name != 'cpython' and extra == 'pin'", specifier = "==3.0" },
|
||||
{ name = "pycparser", marker = "implementation_name == 'cpython' and extra == 'pin-curl-cffi'", specifier = "==3.0" },
|
||||
{ name = "pycryptodomex", marker = "extra == 'default'" },
|
||||
{ name = "pycryptodomex", marker = "extra == 'pin'", specifier = "==3.23.0" },
|
||||
{ name = "pygments", marker = "implementation_name == 'cpython' and extra == 'pin-curl-cffi'", specifier = "==2.19.2" },
|
||||
{ name = "requests", marker = "extra == 'default'", specifier = ">=2.32.2,<3" },
|
||||
{ name = "requests", marker = "extra == 'pin'", specifier = "==2.33.0" },
|
||||
{ name = "rich", marker = "implementation_name == 'cpython' and extra == 'pin-curl-cffi'", specifier = "==14.3.3" },
|
||||
{ name = "secretstorage", marker = "extra == 'pin-secretstorage'", specifier = "==3.5.0" },
|
||||
{ name = "secretstorage", marker = "extra == 'secretstorage'" },
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.11' and extra == 'pin-secretstorage'", specifier = "==4.15.0" },
|
||||
{ name = "urllib3", marker = "extra == 'default'", specifier = ">=2.0.2,<3" },
|
||||
{ name = "urllib3", marker = "extra == 'pin'", specifier = "==2.6.3" },
|
||||
{ name = "websockets", marker = "extra == 'default'", specifier = ">=13.0" },
|
||||
{ name = "websockets", marker = "extra == 'pin'", specifier = "==16.0" },
|
||||
{ name = "yt-dlp-ejs", marker = "extra == 'default'", specifier = "==0.8.0" },
|
||||
{ name = "yt-dlp-ejs", marker = "extra == 'pin'", specifier = "==0.8.0" },
|
||||
]
|
||||
provides-extras = ["curl-cffi", "default", "deno", "secretstorage"]
|
||||
provides-extras = ["curl-cffi", "default", "deno", "pin", "pin-curl-cffi", "pin-deno", "pin-secretstorage", "secretstorage"]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
build = [
|
||||
|
||||
@ -3,9 +3,7 @@ import collections
|
||||
import contextlib
|
||||
import functools
|
||||
import getpass
|
||||
import http.client
|
||||
import http.cookiejar
|
||||
import http.cookies
|
||||
import inspect
|
||||
import itertools
|
||||
import json
|
||||
|
||||
@ -1322,7 +1322,7 @@ class PeerTubeIE(InfoExtractor):
|
||||
)
|
||||
(?P<id>{_UUID_RE})
|
||||
'''
|
||||
_EMBED_REGEX = [r'''(?x)<iframe[^>]+\bsrc=["\'](?P<url>(?:https?:)?//{_INSTANCES_RE}/videos/embed/{cls._UUID_RE})''']
|
||||
_EMBED_REGEX = [rf'''(?x)<iframe[^>]+\bsrc=["\'](?P<url>(?:https?:)?//{_INSTANCES_RE}/videos/embed/{_UUID_RE})''']
|
||||
_TESTS = [{
|
||||
'url': 'https://framatube.org/videos/watch/9c9de5e8-0a1e-484a-b099-e80766180a6d',
|
||||
'md5': '8563064d245a4be5705bddb22bb00a28',
|
||||
|
||||
@ -1180,7 +1180,7 @@ def create_parser():
|
||||
workarounds.add_option(
|
||||
'--prefer-insecure', '--prefer-unsecure',
|
||||
action='store_true', dest='prefer_insecure',
|
||||
help='Use an unencrypted connection to retrieve information about the video (Currently supported only for YouTube)')
|
||||
help='Use an unencrypted connection to retrieve information about the video')
|
||||
workarounds.add_option(
|
||||
'--user-agent',
|
||||
metavar='UA', dest='user_agent',
|
||||
|
||||
@ -41,12 +41,12 @@ def _find_exe(basename: str) -> str:
|
||||
else:
|
||||
exts = tuple(ext for ext in pathext.split(os.pathsep) if ext)
|
||||
|
||||
visited = []
|
||||
visited = set()
|
||||
for path in map(os.path.realpath, paths):
|
||||
normed = os.path.normcase(path)
|
||||
if normed in visited:
|
||||
continue
|
||||
visited.append(normed)
|
||||
visited.add(normed)
|
||||
|
||||
for ext in exts:
|
||||
binary = os.path.join(path, f'{basename}{ext}')
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user