mirror of https://github.com/yt-dlp/yt-dlp
Merge branch 'master' into a3player-fixes
commit
8e753e9633
@ -0,0 +1,8 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[**.py]
|
||||||
|
charset = utf-8
|
||||||
|
indent_size = 4
|
||||||
|
indent_style = space
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
@ -1,70 +0,0 @@
|
|||||||
---
|
|
||||||
name: Broken site support
|
|
||||||
about: Report broken or misfunctioning site
|
|
||||||
title: "[Broken]"
|
|
||||||
labels: Broken
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!--
|
|
||||||
|
|
||||||
######################################################################
|
|
||||||
WARNING!
|
|
||||||
IGNORING THE FOLLOWING TEMPLATE WILL RESULT IN ISSUE CLOSED AS INCOMPLETE
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
## Checklist
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of yt-dlp:
|
|
||||||
- First of, make sure you are using the latest version of yt-dlp. Run `yt-dlp --version` and ensure your version is 2021.09.25. If it's not, see https://github.com/yt-dlp/yt-dlp on how to update. Issues with outdated version will be REJECTED.
|
|
||||||
- Make sure that all provided video/audio/playlist URLs (if any) are alive and playable in a browser.
|
|
||||||
- Make sure that all URLs and arguments with special characters are properly quoted or escaped as explained in https://github.com/yt-dlp/yt-dlp.
|
|
||||||
- Search the bugtracker for similar issues: https://github.com/yt-dlp/yt-dlp. DO NOT post duplicates.
|
|
||||||
- Finally, put x into all relevant boxes like this [x] (Dont forget to delete the empty space)
|
|
||||||
-->
|
|
||||||
|
|
||||||
- [ ] I'm reporting a broken site support
|
|
||||||
- [ ] I've verified that I'm running yt-dlp version **2021.09.25**
|
|
||||||
- [ ] I've checked that all provided URLs are alive and playable in a browser
|
|
||||||
- [ ] I've checked that all URLs and arguments with special characters are properly quoted or escaped
|
|
||||||
- [ ] I've searched the bugtracker for similar issues including closed ones
|
|
||||||
|
|
||||||
|
|
||||||
## Verbose log
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Provide the complete verbose output of yt-dlp that clearly demonstrates the problem.
|
|
||||||
Add the `-v` flag to your command line you run yt-dlp with (`yt-dlp -v <your command line>`), copy the WHOLE output and insert it below. It should look similar to this:
|
|
||||||
[debug] System config: []
|
|
||||||
[debug] User config: []
|
|
||||||
[debug] Command-line args: [u'-v', u'http://www.youtube.com/watch?v=BaW_jenozKc']
|
|
||||||
[debug] Encodings: locale cp1251, fs mbcs, out cp866, pref cp1251
|
|
||||||
[debug] yt-dlp version 2021.09.25
|
|
||||||
[debug] Python version 2.7.11 - Windows-2003Server-5.2.3790-SP2
|
|
||||||
[debug] exe versions: ffmpeg N-75573-g1d0487f, ffprobe N-75573-g1d0487f, rtmpdump 2.4
|
|
||||||
[debug] Proxy map: {}
|
|
||||||
<more lines>
|
|
||||||
-->
|
|
||||||
|
|
||||||
```
|
|
||||||
PASTE VERBOSE LOG HERE
|
|
||||||
|
|
||||||
```
|
|
||||||
<!--
|
|
||||||
Do not remove the above ```
|
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Provide an explanation of your issue in an arbitrary form. Provide any additional information, suggested solution and as much context and examples as possible.
|
|
||||||
If work on your issue requires account credentials please provide them or explain how one can obtain them.
|
|
||||||
-->
|
|
||||||
|
|
||||||
WRITE DESCRIPTION HERE
|
|
@ -0,0 +1,80 @@
|
|||||||
|
name: Broken site support
|
||||||
|
description: Report issue with yt-dlp on a supported site
|
||||||
|
labels: [triage, site-bug]
|
||||||
|
body:
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: DO NOT REMOVE OR SKIP THE ISSUE TEMPLATE
|
||||||
|
description: Fill all fields even if you think it is irrelevant for the issue
|
||||||
|
options:
|
||||||
|
- label: I understand that I will be **blocked** if I *intentionally* remove or skip any mandatory\* field
|
||||||
|
required: true
|
||||||
|
- type: checkboxes
|
||||||
|
id: checklist
|
||||||
|
attributes:
|
||||||
|
label: Checklist
|
||||||
|
description: |
|
||||||
|
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of yt-dlp:
|
||||||
|
options:
|
||||||
|
- label: I'm reporting that yt-dlp is broken on a **supported** site
|
||||||
|
required: true
|
||||||
|
- label: I've verified that I'm running yt-dlp version **2023.10.13** ([update instructions](https://github.com/yt-dlp/yt-dlp#update)) or later (specify commit)
|
||||||
|
required: true
|
||||||
|
- label: I've checked that all provided URLs are playable in a browser with the same IP and same login details
|
||||||
|
required: true
|
||||||
|
- label: I've checked that all URLs and arguments with special characters are [properly quoted or escaped](https://github.com/yt-dlp/yt-dlp/wiki/FAQ#video-url-contains-an-ampersand--and-im-getting-some-strange-output-1-2839-or-v-is-not-recognized-as-an-internal-or-external-command)
|
||||||
|
required: true
|
||||||
|
- label: I've searched [known issues](https://github.com/yt-dlp/yt-dlp/issues/3766) and the [bugtracker](https://github.com/yt-dlp/yt-dlp/issues?q=) for similar issues **including closed ones**. DO NOT post duplicates
|
||||||
|
required: true
|
||||||
|
- label: I've read the [guidelines for opening an issue](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#opening-an-issue)
|
||||||
|
required: true
|
||||||
|
- label: I've read about [sharing account credentials](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#are-you-willing-to-share-account-details-if-needed) and I'm willing to share it if required
|
||||||
|
- type: input
|
||||||
|
id: region
|
||||||
|
attributes:
|
||||||
|
label: Region
|
||||||
|
description: Enter the country/region that the site is accessible from
|
||||||
|
placeholder: India
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Provide a description that is worded well enough to be understood
|
||||||
|
description: See [is-the-description-of-the-issue-itself-sufficient](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#is-the-description-of-the-issue-itself-sufficient)
|
||||||
|
placeholder: Provide any additional information, any suggested solutions, and as much context and examples as possible
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: checkboxes
|
||||||
|
id: verbose
|
||||||
|
attributes:
|
||||||
|
label: Provide verbose output that clearly demonstrates the problem
|
||||||
|
options:
|
||||||
|
- label: Run **your** yt-dlp command with **-vU** flag added (`yt-dlp -vU <your command line>`)
|
||||||
|
required: true
|
||||||
|
- label: "If using API, add `'verbose': True` to `YoutubeDL` params instead"
|
||||||
|
required: false
|
||||||
|
- label: Copy the WHOLE output (starting with `[debug] Command-line config`) and insert it below
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: log
|
||||||
|
attributes:
|
||||||
|
label: Complete Verbose Output
|
||||||
|
description: |
|
||||||
|
It should start like this:
|
||||||
|
placeholder: |
|
||||||
|
[debug] Command-line config: ['-vU', 'test:youtube']
|
||||||
|
[debug] Portable config "yt-dlp.conf": ['-i']
|
||||||
|
[debug] Encodings: locale cp65001, fs utf-8, pref cp65001, out utf-8, error utf-8, screen utf-8
|
||||||
|
[debug] yt-dlp version 2023.10.13 [9d339c4] (win32_exe)
|
||||||
|
[debug] Python 3.8.10 (CPython 64bit) - Windows-10-10.0.22000-SP0
|
||||||
|
[debug] Checking exe version: ffmpeg -bsfs
|
||||||
|
[debug] Checking exe version: ffprobe -bsfs
|
||||||
|
[debug] exe versions: ffmpeg N-106550-g072101bd52-20220410 (fdk,setts), ffprobe N-106624-g391ce570c8-20220415, phantomjs 2.1.1
|
||||||
|
[debug] Optional libraries: Cryptodome-3.15.0, brotli-1.0.9, certifi-2022.06.15, mutagen-1.45.1, sqlite3-2.6.0, websockets-10.3
|
||||||
|
[debug] Proxy map: {}
|
||||||
|
[debug] Fetching release info: https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest
|
||||||
|
Latest version: 2023.10.13, Current version: 2023.10.13
|
||||||
|
yt-dlp is up to date (2023.10.13)
|
||||||
|
<more lines>
|
||||||
|
render: shell
|
||||||
|
validations:
|
||||||
|
required: true
|
@ -1,57 +0,0 @@
|
|||||||
---
|
|
||||||
name: Site support request
|
|
||||||
about: Request support for a new site
|
|
||||||
title: "[Site Request]"
|
|
||||||
labels: Request
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!--
|
|
||||||
|
|
||||||
######################################################################
|
|
||||||
WARNING!
|
|
||||||
IGNORING THE FOLLOWING TEMPLATE WILL RESULT IN ISSUE CLOSED AS INCOMPLETE
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
## Checklist
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of yt-dlp:
|
|
||||||
- First of, make sure you are using the latest version of yt-dlp. Run `yt-dlp --version` and ensure your version is 2021.09.25. If it's not, see https://github.com/yt-dlp/yt-dlp on how to update. Issues with outdated version will be REJECTED.
|
|
||||||
- Make sure that all provided video/audio/playlist URLs (if any) are alive and playable in a browser.
|
|
||||||
- Make sure that site you are requesting is not dedicated to copyright infringement, see https://github.com/yt-dlp/yt-dlp. yt-dlp does not support such sites. In order for site support request to be accepted all provided example URLs should not violate any copyrights.
|
|
||||||
- Search the bugtracker for similar site support requests: https://github.com/yt-dlp/yt-dlp. DO NOT post duplicates.
|
|
||||||
- Finally, put x into all relevant boxes like this [x] (Dont forget to delete the empty space)
|
|
||||||
-->
|
|
||||||
|
|
||||||
- [ ] I'm reporting a new site support request
|
|
||||||
- [ ] I've verified that I'm running yt-dlp version **2021.09.25**
|
|
||||||
- [ ] I've checked that all provided URLs are alive and playable in a browser
|
|
||||||
- [ ] I've checked that none of provided URLs violate any copyrights
|
|
||||||
- [ ] The provided URLs do not contain any DRM to the best of my knowledge
|
|
||||||
- [ ] I've searched the bugtracker for similar site support requests including closed ones
|
|
||||||
|
|
||||||
|
|
||||||
## Example URLs
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Provide all kinds of example URLs support for which should be included. Replace following example URLs by yours.
|
|
||||||
-->
|
|
||||||
|
|
||||||
- Single video: https://www.youtube.com/watch?v=BaW_jenozKc
|
|
||||||
- Single video: https://youtu.be/BaW_jenozKc
|
|
||||||
- Playlist: https://www.youtube.com/playlist?list=PL4lCao7KL_QFVb7Iudeipvc2BCavECqzc
|
|
||||||
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Provide any additional information.
|
|
||||||
If work on your issue requires account credentials please provide them or explain how one can obtain them.
|
|
||||||
-->
|
|
||||||
|
|
||||||
WRITE DESCRIPTION HERE
|
|
@ -0,0 +1,92 @@
|
|||||||
|
name: Site support request
|
||||||
|
description: Request support for a new site
|
||||||
|
labels: [triage, site-request]
|
||||||
|
body:
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: DO NOT REMOVE OR SKIP THE ISSUE TEMPLATE
|
||||||
|
description: Fill all fields even if you think it is irrelevant for the issue
|
||||||
|
options:
|
||||||
|
- label: I understand that I will be **blocked** if I *intentionally* remove or skip any mandatory\* field
|
||||||
|
required: true
|
||||||
|
- type: checkboxes
|
||||||
|
id: checklist
|
||||||
|
attributes:
|
||||||
|
label: Checklist
|
||||||
|
description: |
|
||||||
|
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of yt-dlp:
|
||||||
|
options:
|
||||||
|
- label: I'm reporting a new site support request
|
||||||
|
required: true
|
||||||
|
- label: I've verified that I'm running yt-dlp version **2023.10.13** ([update instructions](https://github.com/yt-dlp/yt-dlp#update)) or later (specify commit)
|
||||||
|
required: true
|
||||||
|
- label: I've checked that all provided URLs are playable in a browser with the same IP and same login details
|
||||||
|
required: true
|
||||||
|
- label: I've checked that none of provided URLs [violate any copyrights](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#is-the-website-primarily-used-for-piracy) or contain any [DRM](https://en.wikipedia.org/wiki/Digital_rights_management) to the best of my knowledge
|
||||||
|
required: true
|
||||||
|
- label: I've searched [known issues](https://github.com/yt-dlp/yt-dlp/issues/3766) and the [bugtracker](https://github.com/yt-dlp/yt-dlp/issues?q=) for similar issues **including closed ones**. DO NOT post duplicates
|
||||||
|
required: true
|
||||||
|
- label: I've read the [guidelines for opening an issue](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#opening-an-issue)
|
||||||
|
required: true
|
||||||
|
- label: I've read about [sharing account credentials](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#are-you-willing-to-share-account-details-if-needed) and am willing to share it if required
|
||||||
|
- type: input
|
||||||
|
id: region
|
||||||
|
attributes:
|
||||||
|
label: Region
|
||||||
|
description: Enter the country/region that the site is accessible from
|
||||||
|
placeholder: India
|
||||||
|
- type: textarea
|
||||||
|
id: example-urls
|
||||||
|
attributes:
|
||||||
|
label: Example URLs
|
||||||
|
description: |
|
||||||
|
Provide all kinds of example URLs for which support should be added
|
||||||
|
placeholder: |
|
||||||
|
- Single video: https://www.youtube.com/watch?v=BaW_jenozKc
|
||||||
|
- Single video: https://youtu.be/BaW_jenozKc
|
||||||
|
- Playlist: https://www.youtube.com/playlist?list=PL4lCao7KL_QFVb7Iudeipvc2BCavECqzc
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Provide a description that is worded well enough to be understood
|
||||||
|
description: See [is-the-description-of-the-issue-itself-sufficient](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#is-the-description-of-the-issue-itself-sufficient)
|
||||||
|
placeholder: Provide any additional information, any suggested solutions, and as much context and examples as possible
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: checkboxes
|
||||||
|
id: verbose
|
||||||
|
attributes:
|
||||||
|
label: Provide verbose output that clearly demonstrates the problem
|
||||||
|
options:
|
||||||
|
- label: Run **your** yt-dlp command with **-vU** flag added (`yt-dlp -vU <your command line>`)
|
||||||
|
required: true
|
||||||
|
- label: "If using API, add `'verbose': True` to `YoutubeDL` params instead"
|
||||||
|
required: false
|
||||||
|
- label: Copy the WHOLE output (starting with `[debug] Command-line config`) and insert it below
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: log
|
||||||
|
attributes:
|
||||||
|
label: Complete Verbose Output
|
||||||
|
description: |
|
||||||
|
It should start like this:
|
||||||
|
placeholder: |
|
||||||
|
[debug] Command-line config: ['-vU', 'test:youtube']
|
||||||
|
[debug] Portable config "yt-dlp.conf": ['-i']
|
||||||
|
[debug] Encodings: locale cp65001, fs utf-8, pref cp65001, out utf-8, error utf-8, screen utf-8
|
||||||
|
[debug] yt-dlp version 2023.10.13 [9d339c4] (win32_exe)
|
||||||
|
[debug] Python 3.8.10 (CPython 64bit) - Windows-10-10.0.22000-SP0
|
||||||
|
[debug] Checking exe version: ffmpeg -bsfs
|
||||||
|
[debug] Checking exe version: ffprobe -bsfs
|
||||||
|
[debug] exe versions: ffmpeg N-106550-g072101bd52-20220410 (fdk,setts), ffprobe N-106624-g391ce570c8-20220415, phantomjs 2.1.1
|
||||||
|
[debug] Optional libraries: Cryptodome-3.15.0, brotli-1.0.9, certifi-2022.06.15, mutagen-1.45.1, sqlite3-2.6.0, websockets-10.3
|
||||||
|
[debug] Proxy map: {}
|
||||||
|
[debug] Fetching release info: https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest
|
||||||
|
Latest version: 2023.10.13, Current version: 2023.10.13
|
||||||
|
yt-dlp is up to date (2023.10.13)
|
||||||
|
<more lines>
|
||||||
|
render: shell
|
||||||
|
validations:
|
||||||
|
required: true
|
@ -1,40 +0,0 @@
|
|||||||
---
|
|
||||||
name: Site feature request
|
|
||||||
about: Request a new functionality for a site
|
|
||||||
title: "[Site Request]"
|
|
||||||
labels: Request
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!--
|
|
||||||
|
|
||||||
######################################################################
|
|
||||||
WARNING!
|
|
||||||
IGNORING THE FOLLOWING TEMPLATE WILL RESULT IN ISSUE CLOSED AS INCOMPLETE
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
## Checklist
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of yt-dlp:
|
|
||||||
- First of, make sure you are using the latest version of yt-dlp. Run `yt-dlp --version` and ensure your version is 2021.09.25. If it's not, see https://github.com/yt-dlp/yt-dlp on how to update. Issues with outdated version will be REJECTED.
|
|
||||||
- Search the bugtracker for similar site feature requests: https://github.com/yt-dlp/yt-dlp. DO NOT post duplicates.
|
|
||||||
- Finally, put x into all relevant boxes like this [x] (Dont forget to delete the empty space)
|
|
||||||
-->
|
|
||||||
|
|
||||||
- [ ] I'm reporting a site feature request
|
|
||||||
- [ ] I've verified that I'm running yt-dlp version **2021.09.25**
|
|
||||||
- [ ] I've searched the bugtracker for similar site feature requests including closed ones
|
|
||||||
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Provide an explanation of your site feature request in an arbitrary form. Please make sure the description is worded well enough to be understood, see https://github.com/ytdl-org/youtube-dl#is-the-description-of-the-issue-itself-sufficient. Provide any additional information, suggested solution and as much context and examples as possible.
|
|
||||||
-->
|
|
||||||
|
|
||||||
WRITE DESCRIPTION HERE
|
|
@ -0,0 +1,88 @@
|
|||||||
|
name: Site feature request
|
||||||
|
description: Request a new functionality for a supported site
|
||||||
|
labels: [triage, site-enhancement]
|
||||||
|
body:
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: DO NOT REMOVE OR SKIP THE ISSUE TEMPLATE
|
||||||
|
description: Fill all fields even if you think it is irrelevant for the issue
|
||||||
|
options:
|
||||||
|
- label: I understand that I will be **blocked** if I *intentionally* remove or skip any mandatory\* field
|
||||||
|
required: true
|
||||||
|
- type: checkboxes
|
||||||
|
id: checklist
|
||||||
|
attributes:
|
||||||
|
label: Checklist
|
||||||
|
description: |
|
||||||
|
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of yt-dlp:
|
||||||
|
options:
|
||||||
|
- label: I'm requesting a site-specific feature
|
||||||
|
required: true
|
||||||
|
- label: I've verified that I'm running yt-dlp version **2023.10.13** ([update instructions](https://github.com/yt-dlp/yt-dlp#update)) or later (specify commit)
|
||||||
|
required: true
|
||||||
|
- label: I've checked that all provided URLs are playable in a browser with the same IP and same login details
|
||||||
|
required: true
|
||||||
|
- label: I've searched [known issues](https://github.com/yt-dlp/yt-dlp/issues/3766) and the [bugtracker](https://github.com/yt-dlp/yt-dlp/issues?q=) for similar issues **including closed ones**. DO NOT post duplicates
|
||||||
|
required: true
|
||||||
|
- label: I've read the [guidelines for opening an issue](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#opening-an-issue)
|
||||||
|
required: true
|
||||||
|
- label: I've read about [sharing account credentials](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#are-you-willing-to-share-account-details-if-needed) and I'm willing to share it if required
|
||||||
|
- type: input
|
||||||
|
id: region
|
||||||
|
attributes:
|
||||||
|
label: Region
|
||||||
|
description: Enter the country/region that the site is accessible from
|
||||||
|
placeholder: India
|
||||||
|
- type: textarea
|
||||||
|
id: example-urls
|
||||||
|
attributes:
|
||||||
|
label: Example URLs
|
||||||
|
description: |
|
||||||
|
Example URLs that can be used to demonstrate the requested feature
|
||||||
|
placeholder: |
|
||||||
|
https://www.youtube.com/watch?v=BaW_jenozKc
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Provide a description that is worded well enough to be understood
|
||||||
|
description: See [is-the-description-of-the-issue-itself-sufficient](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#is-the-description-of-the-issue-itself-sufficient)
|
||||||
|
placeholder: Provide any additional information, any suggested solutions, and as much context and examples as possible
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: checkboxes
|
||||||
|
id: verbose
|
||||||
|
attributes:
|
||||||
|
label: Provide verbose output that clearly demonstrates the problem
|
||||||
|
options:
|
||||||
|
- label: Run **your** yt-dlp command with **-vU** flag added (`yt-dlp -vU <your command line>`)
|
||||||
|
required: true
|
||||||
|
- label: "If using API, add `'verbose': True` to `YoutubeDL` params instead"
|
||||||
|
required: false
|
||||||
|
- label: Copy the WHOLE output (starting with `[debug] Command-line config`) and insert it below
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: log
|
||||||
|
attributes:
|
||||||
|
label: Complete Verbose Output
|
||||||
|
description: |
|
||||||
|
It should start like this:
|
||||||
|
placeholder: |
|
||||||
|
[debug] Command-line config: ['-vU', 'test:youtube']
|
||||||
|
[debug] Portable config "yt-dlp.conf": ['-i']
|
||||||
|
[debug] Encodings: locale cp65001, fs utf-8, pref cp65001, out utf-8, error utf-8, screen utf-8
|
||||||
|
[debug] yt-dlp version 2023.10.13 [9d339c4] (win32_exe)
|
||||||
|
[debug] Python 3.8.10 (CPython 64bit) - Windows-10-10.0.22000-SP0
|
||||||
|
[debug] Checking exe version: ffmpeg -bsfs
|
||||||
|
[debug] Checking exe version: ffprobe -bsfs
|
||||||
|
[debug] exe versions: ffmpeg N-106550-g072101bd52-20220410 (fdk,setts), ffprobe N-106624-g391ce570c8-20220415, phantomjs 2.1.1
|
||||||
|
[debug] Optional libraries: Cryptodome-3.15.0, brotli-1.0.9, certifi-2022.06.15, mutagen-1.45.1, sqlite3-2.6.0, websockets-10.3
|
||||||
|
[debug] Proxy map: {}
|
||||||
|
[debug] Fetching release info: https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest
|
||||||
|
Latest version: 2023.10.13, Current version: 2023.10.13
|
||||||
|
yt-dlp is up to date (2023.10.13)
|
||||||
|
<more lines>
|
||||||
|
render: shell
|
||||||
|
validations:
|
||||||
|
required: true
|
@ -1,73 +0,0 @@
|
|||||||
---
|
|
||||||
name: Bug report
|
|
||||||
about: Report a bug unrelated to any particular site or extractor
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!--
|
|
||||||
|
|
||||||
######################################################################
|
|
||||||
WARNING!
|
|
||||||
IGNORING THE FOLLOWING TEMPLATE WILL RESULT IN ISSUE CLOSED AS INCOMPLETE
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
## Checklist
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of yt-dlp:
|
|
||||||
- First of, make sure you are using the latest version of yt-dlp. Run `yt-dlp --version` and ensure your version is 2021.09.25. If it's not, see https://github.com/yt-dlp/yt-dlp on how to update. Issues with outdated version will be REJECTED.
|
|
||||||
- Make sure that all provided video/audio/playlist URLs (if any) are alive and playable in a browser.
|
|
||||||
- Make sure that all URLs and arguments with special characters are properly quoted or escaped as explained in https://github.com/yt-dlp/yt-dlp.
|
|
||||||
- Search the bugtracker for similar issues: https://github.com/yt-dlp/yt-dlp. DO NOT post duplicates.
|
|
||||||
- Read bugs section in FAQ: https://github.com/yt-dlp/yt-dlp
|
|
||||||
- Finally, put x into all relevant boxes like this [x] (Dont forget to delete the empty space)
|
|
||||||
-->
|
|
||||||
|
|
||||||
- [ ] I'm reporting a bug unrelated to a specific site
|
|
||||||
- [ ] I've verified that I'm running yt-dlp version **2021.09.25**
|
|
||||||
- [ ] I've checked that all provided URLs are alive and playable in a browser
|
|
||||||
- [ ] The provided URLs do not contain any DRM to the best of my knowledge
|
|
||||||
- [ ] I've checked that all URLs and arguments with special characters are properly quoted or escaped
|
|
||||||
- [ ] I've searched the bugtracker for similar bug reports including closed ones
|
|
||||||
- [ ] I've read bugs section in FAQ
|
|
||||||
|
|
||||||
|
|
||||||
## Verbose log
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Provide the complete verbose output of yt-dlp that clearly demonstrates the problem.
|
|
||||||
Add the `-v` flag to your command line you run yt-dlp with (`yt-dlp -v <your command line>`), copy the WHOLE output and insert it below. It should look similar to this:
|
|
||||||
[debug] System config: []
|
|
||||||
[debug] User config: []
|
|
||||||
[debug] Command-line args: [u'-v', u'http://www.youtube.com/watch?v=BaW_jenozKc']
|
|
||||||
[debug] Encodings: locale cp1251, fs mbcs, out cp866, pref cp1251
|
|
||||||
[debug] yt-dlp version 2021.09.25
|
|
||||||
[debug] Python version 2.7.11 - Windows-2003Server-5.2.3790-SP2
|
|
||||||
[debug] exe versions: ffmpeg N-75573-g1d0487f, ffprobe N-75573-g1d0487f, rtmpdump 2.4
|
|
||||||
[debug] Proxy map: {}
|
|
||||||
<more lines>
|
|
||||||
-->
|
|
||||||
|
|
||||||
```
|
|
||||||
PASTE VERBOSE LOG HERE
|
|
||||||
|
|
||||||
```
|
|
||||||
<!--
|
|
||||||
Do not remove the above ```
|
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Provide an explanation of your issue in an arbitrary form. Please make sure the description is worded well enough to be understood, see https://github.com/ytdl-org/youtube-dl#is-the-description-of-the-issue-itself-sufficient. Provide any additional information, suggested solution and as much context and examples as possible.
|
|
||||||
If work on your issue requires account credentials please provide them or explain how one can obtain them.
|
|
||||||
-->
|
|
||||||
|
|
||||||
WRITE DESCRIPTION HERE
|
|
@ -0,0 +1,73 @@
|
|||||||
|
name: Core bug report
|
||||||
|
description: Report a bug unrelated to any particular site or extractor
|
||||||
|
labels: [triage, bug]
|
||||||
|
body:
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: DO NOT REMOVE OR SKIP THE ISSUE TEMPLATE
|
||||||
|
description: Fill all fields even if you think it is irrelevant for the issue
|
||||||
|
options:
|
||||||
|
- label: I understand that I will be **blocked** if I *intentionally* remove or skip any mandatory\* field
|
||||||
|
required: true
|
||||||
|
- type: checkboxes
|
||||||
|
id: checklist
|
||||||
|
attributes:
|
||||||
|
label: Checklist
|
||||||
|
description: |
|
||||||
|
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of yt-dlp:
|
||||||
|
options:
|
||||||
|
- label: I'm reporting a bug unrelated to a specific site
|
||||||
|
required: true
|
||||||
|
- label: I've verified that I'm running yt-dlp version **2023.10.13** ([update instructions](https://github.com/yt-dlp/yt-dlp#update)) or later (specify commit)
|
||||||
|
required: true
|
||||||
|
- label: I've checked that all provided URLs are playable in a browser with the same IP and same login details
|
||||||
|
required: true
|
||||||
|
- label: I've checked that all URLs and arguments with special characters are [properly quoted or escaped](https://github.com/yt-dlp/yt-dlp/wiki/FAQ#video-url-contains-an-ampersand--and-im-getting-some-strange-output-1-2839-or-v-is-not-recognized-as-an-internal-or-external-command)
|
||||||
|
required: true
|
||||||
|
- label: I've searched [known issues](https://github.com/yt-dlp/yt-dlp/issues/3766) and the [bugtracker](https://github.com/yt-dlp/yt-dlp/issues?q=) for similar issues **including closed ones**. DO NOT post duplicates
|
||||||
|
required: true
|
||||||
|
- label: I've read the [guidelines for opening an issue](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#opening-an-issue)
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Provide a description that is worded well enough to be understood
|
||||||
|
description: See [is-the-description-of-the-issue-itself-sufficient](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#is-the-description-of-the-issue-itself-sufficient)
|
||||||
|
placeholder: Provide any additional information, any suggested solutions, and as much context and examples as possible
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: checkboxes
|
||||||
|
id: verbose
|
||||||
|
attributes:
|
||||||
|
label: Provide verbose output that clearly demonstrates the problem
|
||||||
|
options:
|
||||||
|
- label: Run **your** yt-dlp command with **-vU** flag added (`yt-dlp -vU <your command line>`)
|
||||||
|
required: true
|
||||||
|
- label: "If using API, add `'verbose': True` to `YoutubeDL` params instead"
|
||||||
|
required: false
|
||||||
|
- label: Copy the WHOLE output (starting with `[debug] Command-line config`) and insert it below
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: log
|
||||||
|
attributes:
|
||||||
|
label: Complete Verbose Output
|
||||||
|
description: |
|
||||||
|
It should start like this:
|
||||||
|
placeholder: |
|
||||||
|
[debug] Command-line config: ['-vU', 'test:youtube']
|
||||||
|
[debug] Portable config "yt-dlp.conf": ['-i']
|
||||||
|
[debug] Encodings: locale cp65001, fs utf-8, pref cp65001, out utf-8, error utf-8, screen utf-8
|
||||||
|
[debug] yt-dlp version 2023.10.13 [9d339c4] (win32_exe)
|
||||||
|
[debug] Python 3.8.10 (CPython 64bit) - Windows-10-10.0.22000-SP0
|
||||||
|
[debug] Checking exe version: ffmpeg -bsfs
|
||||||
|
[debug] Checking exe version: ffprobe -bsfs
|
||||||
|
[debug] exe versions: ffmpeg N-106550-g072101bd52-20220410 (fdk,setts), ffprobe N-106624-g391ce570c8-20220415, phantomjs 2.1.1
|
||||||
|
[debug] Optional libraries: Cryptodome-3.15.0, brotli-1.0.9, certifi-2022.06.15, mutagen-1.45.1, sqlite3-2.6.0, websockets-10.3
|
||||||
|
[debug] Proxy map: {}
|
||||||
|
[debug] Fetching release info: https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest
|
||||||
|
Latest version: 2023.10.13, Current version: 2023.10.13
|
||||||
|
yt-dlp is up to date (2023.10.13)
|
||||||
|
<more lines>
|
||||||
|
render: shell
|
||||||
|
validations:
|
||||||
|
required: true
|
@ -1,40 +0,0 @@
|
|||||||
---
|
|
||||||
name: Feature request
|
|
||||||
about: Request a new functionality unrelated to any particular site or extractor
|
|
||||||
title: "[Feature Request]"
|
|
||||||
labels: Request
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!--
|
|
||||||
|
|
||||||
######################################################################
|
|
||||||
WARNING!
|
|
||||||
IGNORING THE FOLLOWING TEMPLATE WILL RESULT IN ISSUE CLOSED AS INCOMPLETE
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
## Checklist
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of yt-dlp:
|
|
||||||
- First of, make sure you are using the latest version of yt-dlp. Run `yt-dlp --version` and ensure your version is 2021.09.25. If it's not, see https://github.com/yt-dlp/yt-dlp on how to update. Issues with outdated version will be REJECTED.
|
|
||||||
- Search the bugtracker for similar feature requests: https://github.com/yt-dlp/yt-dlp. DO NOT post duplicates.
|
|
||||||
- Finally, put x into all relevant boxes like this [x] (Dont forget to delete the empty space)
|
|
||||||
-->
|
|
||||||
|
|
||||||
- [ ] I'm reporting a feature request
|
|
||||||
- [ ] I've verified that I'm running yt-dlp version **2021.09.25**
|
|
||||||
- [ ] I've searched the bugtracker for similar feature requests including closed ones
|
|
||||||
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Provide an explanation of your issue in an arbitrary form. Please make sure the description is worded well enough to be understood, see https://github.com/ytdl-org/youtube-dl#is-the-description-of-the-issue-itself-sufficient. Provide any additional information, suggested solution and as much context and examples as possible.
|
|
||||||
-->
|
|
||||||
|
|
||||||
WRITE DESCRIPTION HERE
|
|
@ -0,0 +1,67 @@
|
|||||||
|
name: Feature request
|
||||||
|
description: Request a new functionality unrelated to any particular site or extractor
|
||||||
|
labels: [triage, enhancement]
|
||||||
|
body:
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: DO NOT REMOVE OR SKIP THE ISSUE TEMPLATE
|
||||||
|
description: Fill all fields even if you think it is irrelevant for the issue
|
||||||
|
options:
|
||||||
|
- label: I understand that I will be **blocked** if I *intentionally* remove or skip any mandatory\* field
|
||||||
|
required: true
|
||||||
|
- type: checkboxes
|
||||||
|
id: checklist
|
||||||
|
attributes:
|
||||||
|
label: Checklist
|
||||||
|
description: |
|
||||||
|
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of yt-dlp:
|
||||||
|
options:
|
||||||
|
- label: I'm requesting a feature unrelated to a specific site
|
||||||
|
required: true
|
||||||
|
- label: I've looked through the [README](https://github.com/yt-dlp/yt-dlp#readme)
|
||||||
|
required: true
|
||||||
|
- label: I've verified that I'm running yt-dlp version **2023.10.13** ([update instructions](https://github.com/yt-dlp/yt-dlp#update)) or later (specify commit)
|
||||||
|
required: true
|
||||||
|
- label: I've searched [known issues](https://github.com/yt-dlp/yt-dlp/issues/3766) and the [bugtracker](https://github.com/yt-dlp/yt-dlp/issues?q=) for similar issues **including closed ones**. DO NOT post duplicates
|
||||||
|
required: true
|
||||||
|
- label: I've read the [guidelines for opening an issue](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#opening-an-issue)
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Provide a description that is worded well enough to be understood
|
||||||
|
description: See [is-the-description-of-the-issue-itself-sufficient](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#is-the-description-of-the-issue-itself-sufficient)
|
||||||
|
placeholder: Provide any additional information, any suggested solutions, and as much context and examples as possible
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: checkboxes
|
||||||
|
id: verbose
|
||||||
|
attributes:
|
||||||
|
label: Provide verbose output that clearly demonstrates the problem
|
||||||
|
options:
|
||||||
|
- label: Run **your** yt-dlp command with **-vU** flag added (`yt-dlp -vU <your command line>`)
|
||||||
|
- label: "If using API, add `'verbose': True` to `YoutubeDL` params instead"
|
||||||
|
required: false
|
||||||
|
- label: Copy the WHOLE output (starting with `[debug] Command-line config`) and insert it below
|
||||||
|
- type: textarea
|
||||||
|
id: log
|
||||||
|
attributes:
|
||||||
|
label: Complete Verbose Output
|
||||||
|
description: |
|
||||||
|
It should start like this:
|
||||||
|
placeholder: |
|
||||||
|
[debug] Command-line config: ['-vU', 'test:youtube']
|
||||||
|
[debug] Portable config "yt-dlp.conf": ['-i']
|
||||||
|
[debug] Encodings: locale cp65001, fs utf-8, pref cp65001, out utf-8, error utf-8, screen utf-8
|
||||||
|
[debug] yt-dlp version 2023.10.13 [9d339c4] (win32_exe)
|
||||||
|
[debug] Python 3.8.10 (CPython 64bit) - Windows-10-10.0.22000-SP0
|
||||||
|
[debug] Checking exe version: ffmpeg -bsfs
|
||||||
|
[debug] Checking exe version: ffprobe -bsfs
|
||||||
|
[debug] exe versions: ffmpeg N-106550-g072101bd52-20220410 (fdk,setts), ffprobe N-106624-g391ce570c8-20220415, phantomjs 2.1.1
|
||||||
|
[debug] Optional libraries: Cryptodome-3.15.0, brotli-1.0.9, certifi-2022.06.15, mutagen-1.45.1, sqlite3-2.6.0, websockets-10.3
|
||||||
|
[debug] Proxy map: {}
|
||||||
|
[debug] Fetching release info: https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest
|
||||||
|
Latest version: 2023.10.13, Current version: 2023.10.13
|
||||||
|
yt-dlp is up to date (2023.10.13)
|
||||||
|
<more lines>
|
||||||
|
render: shell
|
@ -1,40 +0,0 @@
|
|||||||
---
|
|
||||||
name: Ask question
|
|
||||||
about: Ask yt-dlp related question
|
|
||||||
title: "[Question]"
|
|
||||||
labels: question
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!--
|
|
||||||
|
|
||||||
######################################################################
|
|
||||||
WARNING!
|
|
||||||
IGNORING THE FOLLOWING TEMPLATE WILL RESULT IN ISSUE CLOSED AS INCOMPLETE
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
## Checklist
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of yt-dlp:
|
|
||||||
- Look through the README (https://github.com/yt-dlp/yt-dlp) and FAQ (https://github.com/yt-dlp/yt-dlp) for similar questions
|
|
||||||
- Search the bugtracker for similar questions: https://github.com/yt-dlp/yt-dlp
|
|
||||||
- Finally, put x into all relevant boxes like this [x] (Dont forget to delete the empty space)
|
|
||||||
-->
|
|
||||||
|
|
||||||
- [ ] I'm asking a question
|
|
||||||
- [ ] I've looked through the README and FAQ for similar questions
|
|
||||||
- [ ] I've searched the bugtracker for similar questions including closed ones
|
|
||||||
|
|
||||||
|
|
||||||
## Question
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Ask your question in an arbitrary form. Please make sure it's worded well enough to be understood, see https://github.com/yt-dlp/yt-dlp.
|
|
||||||
-->
|
|
||||||
|
|
||||||
WRITE QUESTION HERE
|
|
@ -0,0 +1,73 @@
|
|||||||
|
name: Ask question
|
||||||
|
description: Ask yt-dlp related question
|
||||||
|
labels: [question]
|
||||||
|
body:
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: DO NOT REMOVE OR SKIP THE ISSUE TEMPLATE
|
||||||
|
description: Fill all fields even if you think it is irrelevant for the issue
|
||||||
|
options:
|
||||||
|
- label: I understand that I will be **blocked** if I *intentionally* remove or skip any mandatory\* field
|
||||||
|
required: true
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
### Make sure you are **only** asking a question and not reporting a bug or requesting a feature.
|
||||||
|
If your question contains "isn't working" or "can you add", this is most likely the wrong template.
|
||||||
|
If you are in doubt whether this is the right template, **USE ANOTHER TEMPLATE**!
|
||||||
|
- type: checkboxes
|
||||||
|
id: checklist
|
||||||
|
attributes:
|
||||||
|
label: Checklist
|
||||||
|
description: |
|
||||||
|
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of yt-dlp:
|
||||||
|
options:
|
||||||
|
- label: I'm asking a question and **not** reporting a bug or requesting a feature
|
||||||
|
required: true
|
||||||
|
- label: I've looked through the [README](https://github.com/yt-dlp/yt-dlp#readme)
|
||||||
|
required: true
|
||||||
|
- label: I've verified that I'm running yt-dlp version **2023.10.13** ([update instructions](https://github.com/yt-dlp/yt-dlp#update)) or later (specify commit)
|
||||||
|
required: true
|
||||||
|
- label: I've searched [known issues](https://github.com/yt-dlp/yt-dlp/issues/3766) and the [bugtracker](https://github.com/yt-dlp/yt-dlp/issues?q=) for similar questions **including closed ones**. DO NOT post duplicates
|
||||||
|
required: true
|
||||||
|
- label: I've read the [guidelines for opening an issue](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#opening-an-issue)
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: question
|
||||||
|
attributes:
|
||||||
|
label: Please make sure the question is worded well enough to be understood
|
||||||
|
description: See [is-the-description-of-the-issue-itself-sufficient](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#is-the-description-of-the-issue-itself-sufficient)
|
||||||
|
placeholder: Provide any additional information and as much context and examples as possible
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: checkboxes
|
||||||
|
id: verbose
|
||||||
|
attributes:
|
||||||
|
label: Provide verbose output that clearly demonstrates the problem
|
||||||
|
options:
|
||||||
|
- label: Run **your** yt-dlp command with **-vU** flag added (`yt-dlp -vU <your command line>`)
|
||||||
|
- label: "If using API, add `'verbose': True` to `YoutubeDL` params instead"
|
||||||
|
required: false
|
||||||
|
- label: Copy the WHOLE output (starting with `[debug] Command-line config`) and insert it below
|
||||||
|
- type: textarea
|
||||||
|
id: log
|
||||||
|
attributes:
|
||||||
|
label: Complete Verbose Output
|
||||||
|
description: |
|
||||||
|
It should start like this:
|
||||||
|
placeholder: |
|
||||||
|
[debug] Command-line config: ['-vU', 'test:youtube']
|
||||||
|
[debug] Portable config "yt-dlp.conf": ['-i']
|
||||||
|
[debug] Encodings: locale cp65001, fs utf-8, pref cp65001, out utf-8, error utf-8, screen utf-8
|
||||||
|
[debug] yt-dlp version 2023.10.13 [9d339c4] (win32_exe)
|
||||||
|
[debug] Python 3.8.10 (CPython 64bit) - Windows-10-10.0.22000-SP0
|
||||||
|
[debug] Checking exe version: ffmpeg -bsfs
|
||||||
|
[debug] Checking exe version: ffprobe -bsfs
|
||||||
|
[debug] exe versions: ffmpeg N-106550-g072101bd52-20220410 (fdk,setts), ffprobe N-106624-g391ce570c8-20220415, phantomjs 2.1.1
|
||||||
|
[debug] Optional libraries: Cryptodome-3.15.0, brotli-1.0.9, certifi-2022.06.15, mutagen-1.45.1, sqlite3-2.6.0, websockets-10.3
|
||||||
|
[debug] Proxy map: {}
|
||||||
|
[debug] Fetching release info: https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest
|
||||||
|
Latest version: 2023.10.13, Current version: 2023.10.13
|
||||||
|
yt-dlp is up to date (2023.10.13)
|
||||||
|
<more lines>
|
||||||
|
render: shell
|
@ -0,0 +1,8 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: Get help from the community on Discord
|
||||||
|
url: https://discord.gg/H5MNcFW63r
|
||||||
|
about: Join the yt-dlp Discord for community-powered support!
|
||||||
|
- name: Matrix Bridge to the Discord server
|
||||||
|
url: https://matrix.to/#/#yt-dlp:matrix.org
|
||||||
|
about: For those who do not want to use Discord
|
@ -1,70 +0,0 @@
|
|||||||
---
|
|
||||||
name: Broken site support
|
|
||||||
about: Report broken or misfunctioning site
|
|
||||||
title: "[Broken]"
|
|
||||||
labels: Broken
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!--
|
|
||||||
|
|
||||||
######################################################################
|
|
||||||
WARNING!
|
|
||||||
IGNORING THE FOLLOWING TEMPLATE WILL RESULT IN ISSUE CLOSED AS INCOMPLETE
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
## Checklist
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of yt-dlp:
|
|
||||||
- First of, make sure you are using the latest version of yt-dlp. Run `yt-dlp --version` and ensure your version is %(version)s. If it's not, see https://github.com/yt-dlp/yt-dlp on how to update. Issues with outdated version will be REJECTED.
|
|
||||||
- Make sure that all provided video/audio/playlist URLs (if any) are alive and playable in a browser.
|
|
||||||
- Make sure that all URLs and arguments with special characters are properly quoted or escaped as explained in https://github.com/yt-dlp/yt-dlp.
|
|
||||||
- Search the bugtracker for similar issues: https://github.com/yt-dlp/yt-dlp. DO NOT post duplicates.
|
|
||||||
- Finally, put x into all relevant boxes like this [x] (Dont forget to delete the empty space)
|
|
||||||
-->
|
|
||||||
|
|
||||||
- [ ] I'm reporting a broken site support
|
|
||||||
- [ ] I've verified that I'm running yt-dlp version **%(version)s**
|
|
||||||
- [ ] I've checked that all provided URLs are alive and playable in a browser
|
|
||||||
- [ ] I've checked that all URLs and arguments with special characters are properly quoted or escaped
|
|
||||||
- [ ] I've searched the bugtracker for similar issues including closed ones
|
|
||||||
|
|
||||||
|
|
||||||
## Verbose log
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Provide the complete verbose output of yt-dlp that clearly demonstrates the problem.
|
|
||||||
Add the `-v` flag to your command line you run yt-dlp with (`yt-dlp -v <your command line>`), copy the WHOLE output and insert it below. It should look similar to this:
|
|
||||||
[debug] System config: []
|
|
||||||
[debug] User config: []
|
|
||||||
[debug] Command-line args: [u'-v', u'http://www.youtube.com/watch?v=BaW_jenozKc']
|
|
||||||
[debug] Encodings: locale cp1251, fs mbcs, out cp866, pref cp1251
|
|
||||||
[debug] yt-dlp version %(version)s
|
|
||||||
[debug] Python version 2.7.11 - Windows-2003Server-5.2.3790-SP2
|
|
||||||
[debug] exe versions: ffmpeg N-75573-g1d0487f, ffprobe N-75573-g1d0487f, rtmpdump 2.4
|
|
||||||
[debug] Proxy map: {}
|
|
||||||
<more lines>
|
|
||||||
-->
|
|
||||||
|
|
||||||
```
|
|
||||||
PASTE VERBOSE LOG HERE
|
|
||||||
|
|
||||||
```
|
|
||||||
<!--
|
|
||||||
Do not remove the above ```
|
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Provide an explanation of your issue in an arbitrary form. Provide any additional information, suggested solution and as much context and examples as possible.
|
|
||||||
If work on your issue requires account credentials please provide them or explain how one can obtain them.
|
|
||||||
-->
|
|
||||||
|
|
||||||
WRITE DESCRIPTION HERE
|
|
@ -0,0 +1,40 @@
|
|||||||
|
name: Broken site support
|
||||||
|
description: Report issue with yt-dlp on a supported site
|
||||||
|
labels: [triage, site-bug]
|
||||||
|
body:
|
||||||
|
%(no_skip)s
|
||||||
|
- type: checkboxes
|
||||||
|
id: checklist
|
||||||
|
attributes:
|
||||||
|
label: Checklist
|
||||||
|
description: |
|
||||||
|
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of yt-dlp:
|
||||||
|
options:
|
||||||
|
- label: I'm reporting that yt-dlp is broken on a **supported** site
|
||||||
|
required: true
|
||||||
|
- label: I've verified that I'm running yt-dlp version **%(version)s** ([update instructions](https://github.com/yt-dlp/yt-dlp#update)) or later (specify commit)
|
||||||
|
required: true
|
||||||
|
- label: I've checked that all provided URLs are playable in a browser with the same IP and same login details
|
||||||
|
required: true
|
||||||
|
- label: I've checked that all URLs and arguments with special characters are [properly quoted or escaped](https://github.com/yt-dlp/yt-dlp/wiki/FAQ#video-url-contains-an-ampersand--and-im-getting-some-strange-output-1-2839-or-v-is-not-recognized-as-an-internal-or-external-command)
|
||||||
|
required: true
|
||||||
|
- label: I've searched [known issues](https://github.com/yt-dlp/yt-dlp/issues/3766) and the [bugtracker](https://github.com/yt-dlp/yt-dlp/issues?q=) for similar issues **including closed ones**. DO NOT post duplicates
|
||||||
|
required: true
|
||||||
|
- label: I've read the [guidelines for opening an issue](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#opening-an-issue)
|
||||||
|
required: true
|
||||||
|
- label: I've read about [sharing account credentials](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#are-you-willing-to-share-account-details-if-needed) and I'm willing to share it if required
|
||||||
|
- type: input
|
||||||
|
id: region
|
||||||
|
attributes:
|
||||||
|
label: Region
|
||||||
|
description: Enter the country/region that the site is accessible from
|
||||||
|
placeholder: India
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Provide a description that is worded well enough to be understood
|
||||||
|
description: See [is-the-description-of-the-issue-itself-sufficient](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#is-the-description-of-the-issue-itself-sufficient)
|
||||||
|
placeholder: Provide any additional information, any suggested solutions, and as much context and examples as possible
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
%(verbose)s
|
@ -1,57 +0,0 @@
|
|||||||
---
|
|
||||||
name: Site support request
|
|
||||||
about: Request support for a new site
|
|
||||||
title: "[Site Request]"
|
|
||||||
labels: Request
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!--
|
|
||||||
|
|
||||||
######################################################################
|
|
||||||
WARNING!
|
|
||||||
IGNORING THE FOLLOWING TEMPLATE WILL RESULT IN ISSUE CLOSED AS INCOMPLETE
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
## Checklist
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of yt-dlp:
|
|
||||||
- First of, make sure you are using the latest version of yt-dlp. Run `yt-dlp --version` and ensure your version is %(version)s. If it's not, see https://github.com/yt-dlp/yt-dlp on how to update. Issues with outdated version will be REJECTED.
|
|
||||||
- Make sure that all provided video/audio/playlist URLs (if any) are alive and playable in a browser.
|
|
||||||
- Make sure that site you are requesting is not dedicated to copyright infringement, see https://github.com/yt-dlp/yt-dlp. yt-dlp does not support such sites. In order for site support request to be accepted all provided example URLs should not violate any copyrights.
|
|
||||||
- Search the bugtracker for similar site support requests: https://github.com/yt-dlp/yt-dlp. DO NOT post duplicates.
|
|
||||||
- Finally, put x into all relevant boxes like this [x] (Dont forget to delete the empty space)
|
|
||||||
-->
|
|
||||||
|
|
||||||
- [ ] I'm reporting a new site support request
|
|
||||||
- [ ] I've verified that I'm running yt-dlp version **%(version)s**
|
|
||||||
- [ ] I've checked that all provided URLs are alive and playable in a browser
|
|
||||||
- [ ] I've checked that none of provided URLs violate any copyrights
|
|
||||||
- [ ] The provided URLs do not contain any DRM to the best of my knowledge
|
|
||||||
- [ ] I've searched the bugtracker for similar site support requests including closed ones
|
|
||||||
|
|
||||||
|
|
||||||
## Example URLs
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Provide all kinds of example URLs support for which should be included. Replace following example URLs by yours.
|
|
||||||
-->
|
|
||||||
|
|
||||||
- Single video: https://www.youtube.com/watch?v=BaW_jenozKc
|
|
||||||
- Single video: https://youtu.be/BaW_jenozKc
|
|
||||||
- Playlist: https://www.youtube.com/playlist?list=PL4lCao7KL_QFVb7Iudeipvc2BCavECqzc
|
|
||||||
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Provide any additional information.
|
|
||||||
If work on your issue requires account credentials please provide them or explain how one can obtain them.
|
|
||||||
-->
|
|
||||||
|
|
||||||
WRITE DESCRIPTION HERE
|
|
@ -0,0 +1,52 @@
|
|||||||
|
name: Site support request
|
||||||
|
description: Request support for a new site
|
||||||
|
labels: [triage, site-request]
|
||||||
|
body:
|
||||||
|
%(no_skip)s
|
||||||
|
- type: checkboxes
|
||||||
|
id: checklist
|
||||||
|
attributes:
|
||||||
|
label: Checklist
|
||||||
|
description: |
|
||||||
|
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of yt-dlp:
|
||||||
|
options:
|
||||||
|
- label: I'm reporting a new site support request
|
||||||
|
required: true
|
||||||
|
- label: I've verified that I'm running yt-dlp version **%(version)s** ([update instructions](https://github.com/yt-dlp/yt-dlp#update)) or later (specify commit)
|
||||||
|
required: true
|
||||||
|
- label: I've checked that all provided URLs are playable in a browser with the same IP and same login details
|
||||||
|
required: true
|
||||||
|
- label: I've checked that none of provided URLs [violate any copyrights](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#is-the-website-primarily-used-for-piracy) or contain any [DRM](https://en.wikipedia.org/wiki/Digital_rights_management) to the best of my knowledge
|
||||||
|
required: true
|
||||||
|
- label: I've searched [known issues](https://github.com/yt-dlp/yt-dlp/issues/3766) and the [bugtracker](https://github.com/yt-dlp/yt-dlp/issues?q=) for similar issues **including closed ones**. DO NOT post duplicates
|
||||||
|
required: true
|
||||||
|
- label: I've read the [guidelines for opening an issue](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#opening-an-issue)
|
||||||
|
required: true
|
||||||
|
- label: I've read about [sharing account credentials](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#are-you-willing-to-share-account-details-if-needed) and am willing to share it if required
|
||||||
|
- type: input
|
||||||
|
id: region
|
||||||
|
attributes:
|
||||||
|
label: Region
|
||||||
|
description: Enter the country/region that the site is accessible from
|
||||||
|
placeholder: India
|
||||||
|
- type: textarea
|
||||||
|
id: example-urls
|
||||||
|
attributes:
|
||||||
|
label: Example URLs
|
||||||
|
description: |
|
||||||
|
Provide all kinds of example URLs for which support should be added
|
||||||
|
placeholder: |
|
||||||
|
- Single video: https://www.youtube.com/watch?v=BaW_jenozKc
|
||||||
|
- Single video: https://youtu.be/BaW_jenozKc
|
||||||
|
- Playlist: https://www.youtube.com/playlist?list=PL4lCao7KL_QFVb7Iudeipvc2BCavECqzc
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Provide a description that is worded well enough to be understood
|
||||||
|
description: See [is-the-description-of-the-issue-itself-sufficient](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#is-the-description-of-the-issue-itself-sufficient)
|
||||||
|
placeholder: Provide any additional information, any suggested solutions, and as much context and examples as possible
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
%(verbose)s
|
@ -1,40 +0,0 @@
|
|||||||
---
|
|
||||||
name: Site feature request
|
|
||||||
about: Request a new functionality for a site
|
|
||||||
title: "[Site Request]"
|
|
||||||
labels: Request
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!--
|
|
||||||
|
|
||||||
######################################################################
|
|
||||||
WARNING!
|
|
||||||
IGNORING THE FOLLOWING TEMPLATE WILL RESULT IN ISSUE CLOSED AS INCOMPLETE
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
## Checklist
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of yt-dlp:
|
|
||||||
- First of, make sure you are using the latest version of yt-dlp. Run `yt-dlp --version` and ensure your version is %(version)s. If it's not, see https://github.com/yt-dlp/yt-dlp on how to update. Issues with outdated version will be REJECTED.
|
|
||||||
- Search the bugtracker for similar site feature requests: https://github.com/yt-dlp/yt-dlp. DO NOT post duplicates.
|
|
||||||
- Finally, put x into all relevant boxes like this [x] (Dont forget to delete the empty space)
|
|
||||||
-->
|
|
||||||
|
|
||||||
- [ ] I'm reporting a site feature request
|
|
||||||
- [ ] I've verified that I'm running yt-dlp version **%(version)s**
|
|
||||||
- [ ] I've searched the bugtracker for similar site feature requests including closed ones
|
|
||||||
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Provide an explanation of your site feature request in an arbitrary form. Please make sure the description is worded well enough to be understood, see https://github.com/ytdl-org/youtube-dl#is-the-description-of-the-issue-itself-sufficient. Provide any additional information, suggested solution and as much context and examples as possible.
|
|
||||||
-->
|
|
||||||
|
|
||||||
WRITE DESCRIPTION HERE
|
|
@ -0,0 +1,48 @@
|
|||||||
|
name: Site feature request
|
||||||
|
description: Request a new functionality for a supported site
|
||||||
|
labels: [triage, site-enhancement]
|
||||||
|
body:
|
||||||
|
%(no_skip)s
|
||||||
|
- type: checkboxes
|
||||||
|
id: checklist
|
||||||
|
attributes:
|
||||||
|
label: Checklist
|
||||||
|
description: |
|
||||||
|
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of yt-dlp:
|
||||||
|
options:
|
||||||
|
- label: I'm requesting a site-specific feature
|
||||||
|
required: true
|
||||||
|
- label: I've verified that I'm running yt-dlp version **%(version)s** ([update instructions](https://github.com/yt-dlp/yt-dlp#update)) or later (specify commit)
|
||||||
|
required: true
|
||||||
|
- label: I've checked that all provided URLs are playable in a browser with the same IP and same login details
|
||||||
|
required: true
|
||||||
|
- label: I've searched [known issues](https://github.com/yt-dlp/yt-dlp/issues/3766) and the [bugtracker](https://github.com/yt-dlp/yt-dlp/issues?q=) for similar issues **including closed ones**. DO NOT post duplicates
|
||||||
|
required: true
|
||||||
|
- label: I've read the [guidelines for opening an issue](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#opening-an-issue)
|
||||||
|
required: true
|
||||||
|
- label: I've read about [sharing account credentials](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#are-you-willing-to-share-account-details-if-needed) and I'm willing to share it if required
|
||||||
|
- type: input
|
||||||
|
id: region
|
||||||
|
attributes:
|
||||||
|
label: Region
|
||||||
|
description: Enter the country/region that the site is accessible from
|
||||||
|
placeholder: India
|
||||||
|
- type: textarea
|
||||||
|
id: example-urls
|
||||||
|
attributes:
|
||||||
|
label: Example URLs
|
||||||
|
description: |
|
||||||
|
Example URLs that can be used to demonstrate the requested feature
|
||||||
|
placeholder: |
|
||||||
|
https://www.youtube.com/watch?v=BaW_jenozKc
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Provide a description that is worded well enough to be understood
|
||||||
|
description: See [is-the-description-of-the-issue-itself-sufficient](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#is-the-description-of-the-issue-itself-sufficient)
|
||||||
|
placeholder: Provide any additional information, any suggested solutions, and as much context and examples as possible
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
%(verbose)s
|
@ -1,73 +0,0 @@
|
|||||||
---
|
|
||||||
name: Bug report
|
|
||||||
about: Report a bug unrelated to any particular site or extractor
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!--
|
|
||||||
|
|
||||||
######################################################################
|
|
||||||
WARNING!
|
|
||||||
IGNORING THE FOLLOWING TEMPLATE WILL RESULT IN ISSUE CLOSED AS INCOMPLETE
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
## Checklist
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of yt-dlp:
|
|
||||||
- First of, make sure you are using the latest version of yt-dlp. Run `yt-dlp --version` and ensure your version is %(version)s. If it's not, see https://github.com/yt-dlp/yt-dlp on how to update. Issues with outdated version will be REJECTED.
|
|
||||||
- Make sure that all provided video/audio/playlist URLs (if any) are alive and playable in a browser.
|
|
||||||
- Make sure that all URLs and arguments with special characters are properly quoted or escaped as explained in https://github.com/yt-dlp/yt-dlp.
|
|
||||||
- Search the bugtracker for similar issues: https://github.com/yt-dlp/yt-dlp. DO NOT post duplicates.
|
|
||||||
- Read bugs section in FAQ: https://github.com/yt-dlp/yt-dlp
|
|
||||||
- Finally, put x into all relevant boxes like this [x] (Dont forget to delete the empty space)
|
|
||||||
-->
|
|
||||||
|
|
||||||
- [ ] I'm reporting a bug unrelated to a specific site
|
|
||||||
- [ ] I've verified that I'm running yt-dlp version **%(version)s**
|
|
||||||
- [ ] I've checked that all provided URLs are alive and playable in a browser
|
|
||||||
- [ ] The provided URLs do not contain any DRM to the best of my knowledge
|
|
||||||
- [ ] I've checked that all URLs and arguments with special characters are properly quoted or escaped
|
|
||||||
- [ ] I've searched the bugtracker for similar bug reports including closed ones
|
|
||||||
- [ ] I've read bugs section in FAQ
|
|
||||||
|
|
||||||
|
|
||||||
## Verbose log
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Provide the complete verbose output of yt-dlp that clearly demonstrates the problem.
|
|
||||||
Add the `-v` flag to your command line you run yt-dlp with (`yt-dlp -v <your command line>`), copy the WHOLE output and insert it below. It should look similar to this:
|
|
||||||
[debug] System config: []
|
|
||||||
[debug] User config: []
|
|
||||||
[debug] Command-line args: [u'-v', u'http://www.youtube.com/watch?v=BaW_jenozKc']
|
|
||||||
[debug] Encodings: locale cp1251, fs mbcs, out cp866, pref cp1251
|
|
||||||
[debug] yt-dlp version %(version)s
|
|
||||||
[debug] Python version 2.7.11 - Windows-2003Server-5.2.3790-SP2
|
|
||||||
[debug] exe versions: ffmpeg N-75573-g1d0487f, ffprobe N-75573-g1d0487f, rtmpdump 2.4
|
|
||||||
[debug] Proxy map: {}
|
|
||||||
<more lines>
|
|
||||||
-->
|
|
||||||
|
|
||||||
```
|
|
||||||
PASTE VERBOSE LOG HERE
|
|
||||||
|
|
||||||
```
|
|
||||||
<!--
|
|
||||||
Do not remove the above ```
|
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Provide an explanation of your issue in an arbitrary form. Please make sure the description is worded well enough to be understood, see https://github.com/ytdl-org/youtube-dl#is-the-description-of-the-issue-itself-sufficient. Provide any additional information, suggested solution and as much context and examples as possible.
|
|
||||||
If work on your issue requires account credentials please provide them or explain how one can obtain them.
|
|
||||||
-->
|
|
||||||
|
|
||||||
WRITE DESCRIPTION HERE
|
|
@ -0,0 +1,33 @@
|
|||||||
|
name: Core bug report
|
||||||
|
description: Report a bug unrelated to any particular site or extractor
|
||||||
|
labels: [triage, bug]
|
||||||
|
body:
|
||||||
|
%(no_skip)s
|
||||||
|
- type: checkboxes
|
||||||
|
id: checklist
|
||||||
|
attributes:
|
||||||
|
label: Checklist
|
||||||
|
description: |
|
||||||
|
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of yt-dlp:
|
||||||
|
options:
|
||||||
|
- label: I'm reporting a bug unrelated to a specific site
|
||||||
|
required: true
|
||||||
|
- label: I've verified that I'm running yt-dlp version **%(version)s** ([update instructions](https://github.com/yt-dlp/yt-dlp#update)) or later (specify commit)
|
||||||
|
required: true
|
||||||
|
- label: I've checked that all provided URLs are playable in a browser with the same IP and same login details
|
||||||
|
required: true
|
||||||
|
- label: I've checked that all URLs and arguments with special characters are [properly quoted or escaped](https://github.com/yt-dlp/yt-dlp/wiki/FAQ#video-url-contains-an-ampersand--and-im-getting-some-strange-output-1-2839-or-v-is-not-recognized-as-an-internal-or-external-command)
|
||||||
|
required: true
|
||||||
|
- label: I've searched [known issues](https://github.com/yt-dlp/yt-dlp/issues/3766) and the [bugtracker](https://github.com/yt-dlp/yt-dlp/issues?q=) for similar issues **including closed ones**. DO NOT post duplicates
|
||||||
|
required: true
|
||||||
|
- label: I've read the [guidelines for opening an issue](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#opening-an-issue)
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Provide a description that is worded well enough to be understood
|
||||||
|
description: See [is-the-description-of-the-issue-itself-sufficient](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#is-the-description-of-the-issue-itself-sufficient)
|
||||||
|
placeholder: Provide any additional information, any suggested solutions, and as much context and examples as possible
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
%(verbose)s
|
@ -1,40 +0,0 @@
|
|||||||
---
|
|
||||||
name: Feature request
|
|
||||||
about: Request a new functionality unrelated to any particular site or extractor
|
|
||||||
title: "[Feature Request]"
|
|
||||||
labels: Request
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!--
|
|
||||||
|
|
||||||
######################################################################
|
|
||||||
WARNING!
|
|
||||||
IGNORING THE FOLLOWING TEMPLATE WILL RESULT IN ISSUE CLOSED AS INCOMPLETE
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
## Checklist
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of yt-dlp:
|
|
||||||
- First of, make sure you are using the latest version of yt-dlp. Run `yt-dlp --version` and ensure your version is %(version)s. If it's not, see https://github.com/yt-dlp/yt-dlp on how to update. Issues with outdated version will be REJECTED.
|
|
||||||
- Search the bugtracker for similar feature requests: https://github.com/yt-dlp/yt-dlp. DO NOT post duplicates.
|
|
||||||
- Finally, put x into all relevant boxes like this [x] (Dont forget to delete the empty space)
|
|
||||||
-->
|
|
||||||
|
|
||||||
- [ ] I'm reporting a feature request
|
|
||||||
- [ ] I've verified that I'm running yt-dlp version **%(version)s**
|
|
||||||
- [ ] I've searched the bugtracker for similar feature requests including closed ones
|
|
||||||
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Provide an explanation of your issue in an arbitrary form. Please make sure the description is worded well enough to be understood, see https://github.com/ytdl-org/youtube-dl#is-the-description-of-the-issue-itself-sufficient. Provide any additional information, suggested solution and as much context and examples as possible.
|
|
||||||
-->
|
|
||||||
|
|
||||||
WRITE DESCRIPTION HERE
|
|
@ -0,0 +1,31 @@
|
|||||||
|
name: Feature request
|
||||||
|
description: Request a new functionality unrelated to any particular site or extractor
|
||||||
|
labels: [triage, enhancement]
|
||||||
|
body:
|
||||||
|
%(no_skip)s
|
||||||
|
- type: checkboxes
|
||||||
|
id: checklist
|
||||||
|
attributes:
|
||||||
|
label: Checklist
|
||||||
|
description: |
|
||||||
|
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of yt-dlp:
|
||||||
|
options:
|
||||||
|
- label: I'm requesting a feature unrelated to a specific site
|
||||||
|
required: true
|
||||||
|
- label: I've looked through the [README](https://github.com/yt-dlp/yt-dlp#readme)
|
||||||
|
required: true
|
||||||
|
- label: I've verified that I'm running yt-dlp version **%(version)s** ([update instructions](https://github.com/yt-dlp/yt-dlp#update)) or later (specify commit)
|
||||||
|
required: true
|
||||||
|
- label: I've searched [known issues](https://github.com/yt-dlp/yt-dlp/issues/3766) and the [bugtracker](https://github.com/yt-dlp/yt-dlp/issues?q=) for similar issues **including closed ones**. DO NOT post duplicates
|
||||||
|
required: true
|
||||||
|
- label: I've read the [guidelines for opening an issue](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#opening-an-issue)
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Provide a description that is worded well enough to be understood
|
||||||
|
description: See [is-the-description-of-the-issue-itself-sufficient](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#is-the-description-of-the-issue-itself-sufficient)
|
||||||
|
placeholder: Provide any additional information, any suggested solutions, and as much context and examples as possible
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
%(verbose_optional)s
|
@ -0,0 +1,37 @@
|
|||||||
|
name: Ask question
|
||||||
|
description: Ask yt-dlp related question
|
||||||
|
labels: [question]
|
||||||
|
body:
|
||||||
|
%(no_skip)s
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
### Make sure you are **only** asking a question and not reporting a bug or requesting a feature.
|
||||||
|
If your question contains "isn't working" or "can you add", this is most likely the wrong template.
|
||||||
|
If you are in doubt whether this is the right template, **USE ANOTHER TEMPLATE**!
|
||||||
|
- type: checkboxes
|
||||||
|
id: checklist
|
||||||
|
attributes:
|
||||||
|
label: Checklist
|
||||||
|
description: |
|
||||||
|
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of yt-dlp:
|
||||||
|
options:
|
||||||
|
- label: I'm asking a question and **not** reporting a bug or requesting a feature
|
||||||
|
required: true
|
||||||
|
- label: I've looked through the [README](https://github.com/yt-dlp/yt-dlp#readme)
|
||||||
|
required: true
|
||||||
|
- label: I've verified that I'm running yt-dlp version **%(version)s** ([update instructions](https://github.com/yt-dlp/yt-dlp#update)) or later (specify commit)
|
||||||
|
required: true
|
||||||
|
- label: I've searched [known issues](https://github.com/yt-dlp/yt-dlp/issues/3766) and the [bugtracker](https://github.com/yt-dlp/yt-dlp/issues?q=) for similar questions **including closed ones**. DO NOT post duplicates
|
||||||
|
required: true
|
||||||
|
- label: I've read the [guidelines for opening an issue](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#opening-an-issue)
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: question
|
||||||
|
attributes:
|
||||||
|
label: Please make sure the question is worded well enough to be understood
|
||||||
|
description: See [is-the-description-of-the-issue-itself-sufficient](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#is-the-description-of-the-issue-itself-sufficient)
|
||||||
|
placeholder: Provide any additional information and as much context and examples as possible
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
%(verbose_optional)s
|
@ -1,28 +1,49 @@
|
|||||||
## Please follow the guide below
|
**IMPORTANT**: PRs without the template will be CLOSED
|
||||||
|
|
||||||
|
### Description of your *pull request* and other information
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
Explanation of your *pull request* in arbitrary form goes here. Please **make sure the description explains the purpose and effect** of your *pull request* and is worded well enough to be understood. Provide as much **context and examples** as possible
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
ADD DESCRIPTION HERE
|
||||||
|
|
||||||
|
Fixes #
|
||||||
|
|
||||||
|
|
||||||
|
<details open><summary>Template</summary> <!-- OPEN is intentional -->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
|
# PLEASE FOLLOW THE GUIDE BELOW
|
||||||
|
|
||||||
- You will be asked some questions, please read them **carefully** and answer honestly
|
- You will be asked some questions, please read them **carefully** and answer honestly
|
||||||
- Put an `x` into all the boxes [ ] relevant to your *pull request* (like that [x])
|
- Put an `x` into all the boxes `[ ]` relevant to your *pull request* (like [x])
|
||||||
- Use *Preview* tab to see how your *pull request* will actually look like
|
- Use *Preview* tab to see how your *pull request* will actually look like
|
||||||
|
|
||||||
---
|
-->
|
||||||
|
|
||||||
### Before submitting a *pull request* make sure you have:
|
### Before submitting a *pull request* make sure you have:
|
||||||
- [ ] At least skimmed through [adding new extractor tutorial](https://github.com/ytdl-org/youtube-dl#adding-support-for-a-new-site) and [youtube-dl coding conventions](https://github.com/ytdl-org/youtube-dl#youtube-dl-coding-conventions) sections
|
- [ ] 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
|
- [ ] [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)
|
- [ ] 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 one of the following options:
|
### 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/)
|
- [ ] I am the original author of this code and I am willing to release it under [Unlicense](http://unlicense.org/)
|
||||||
- [ ] I am not the original author of this code but it is in public domain or released under [Unlicense](http://unlicense.org/) (provide reliable evidence)
|
- [ ] I am not the original author of this code but it is in public domain or released under [Unlicense](http://unlicense.org/) (provide reliable evidence)
|
||||||
|
|
||||||
### What is the purpose of your *pull request*?
|
### What is the purpose of your *pull request*?
|
||||||
- [ ] Bug fix
|
- [ ] Fix or improvement to an extractor (Make sure to add/update tests)
|
||||||
- [ ] Improvement
|
- [ ] New extractor ([Piracy websites will not be accepted](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#is-the-website-primarily-used-for-piracy))
|
||||||
- [ ] New extractor
|
- [ ] Core bug fix/improvement
|
||||||
- [ ] New feature
|
- [ ] New feature (It is strongly [recommended to open an issue first](https://github.com/yt-dlp/yt-dlp/blob/master/CONTRIBUTING.md#adding-new-feature-or-making-overarching-changes))
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Description of your *pull request* and other information
|
<!-- Do NOT edit/remove anything below this! -->
|
||||||
|
</details><details><summary>Copilot Summary</summary>
|
||||||
|
|
||||||
|
copilot:all
|
||||||
|
|
||||||
Explanation of your *pull request* in arbitrary form goes here. Please make sure the description explains the purpose and effect of your *pull request* and is worded well enough to be understood. Provide as much context and examples as possible.
|
</details>
|
||||||
|
@ -1,298 +1,428 @@
|
|||||||
name: Build
|
name: Build Artifacts
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
workflow_call:
|
||||||
branches:
|
inputs:
|
||||||
- release
|
version:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
channel:
|
||||||
|
required: false
|
||||||
|
default: stable
|
||||||
|
type: string
|
||||||
|
unix:
|
||||||
|
default: true
|
||||||
|
type: boolean
|
||||||
|
linux_arm:
|
||||||
|
default: true
|
||||||
|
type: boolean
|
||||||
|
macos:
|
||||||
|
default: true
|
||||||
|
type: boolean
|
||||||
|
macos_legacy:
|
||||||
|
default: true
|
||||||
|
type: boolean
|
||||||
|
windows:
|
||||||
|
default: true
|
||||||
|
type: boolean
|
||||||
|
windows32:
|
||||||
|
default: true
|
||||||
|
type: boolean
|
||||||
|
meta_files:
|
||||||
|
default: true
|
||||||
|
type: boolean
|
||||||
|
secrets:
|
||||||
|
GPG_SIGNING_KEY:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: Version tag (YYYY.MM.DD[.REV])
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
channel:
|
||||||
|
description: Update channel (stable/nightly/...)
|
||||||
|
required: true
|
||||||
|
default: stable
|
||||||
|
type: string
|
||||||
|
unix:
|
||||||
|
description: yt-dlp, yt-dlp.tar.gz, yt-dlp_linux, yt-dlp_linux.zip
|
||||||
|
default: true
|
||||||
|
type: boolean
|
||||||
|
linux_arm:
|
||||||
|
description: yt-dlp_linux_aarch64, yt-dlp_linux_armv7l
|
||||||
|
default: true
|
||||||
|
type: boolean
|
||||||
|
macos:
|
||||||
|
description: yt-dlp_macos, yt-dlp_macos.zip
|
||||||
|
default: true
|
||||||
|
type: boolean
|
||||||
|
macos_legacy:
|
||||||
|
description: yt-dlp_macos_legacy
|
||||||
|
default: true
|
||||||
|
type: boolean
|
||||||
|
windows:
|
||||||
|
description: yt-dlp.exe, yt-dlp_min.exe, yt-dlp_win.zip
|
||||||
|
default: true
|
||||||
|
type: boolean
|
||||||
|
windows32:
|
||||||
|
description: yt-dlp_x86.exe
|
||||||
|
default: true
|
||||||
|
type: boolean
|
||||||
|
meta_files:
|
||||||
|
description: SHA2-256SUMS, SHA2-512SUMS, _update_spec
|
||||||
|
default: true
|
||||||
|
type: boolean
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_unix:
|
unix:
|
||||||
|
if: inputs.unix
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: "3.10"
|
||||||
|
- uses: conda-incubator/setup-miniconda@v2
|
||||||
|
with:
|
||||||
|
miniforge-variant: Mambaforge
|
||||||
|
use-mamba: true
|
||||||
|
channels: conda-forge
|
||||||
|
auto-update-conda: true
|
||||||
|
activate-environment: ""
|
||||||
|
auto-activate-base: false
|
||||||
|
- name: Install Requirements
|
||||||
|
run: |
|
||||||
|
sudo apt-get -y install zip pandoc man sed
|
||||||
|
python -m pip install -U pip setuptools wheel
|
||||||
|
python -m pip install -U Pyinstaller -r requirements.txt
|
||||||
|
reqs=$(mktemp)
|
||||||
|
cat > $reqs << EOF
|
||||||
|
python=3.10.*
|
||||||
|
pyinstaller
|
||||||
|
cffi
|
||||||
|
brotli-python
|
||||||
|
EOF
|
||||||
|
sed '/^brotli.*/d' requirements.txt >> $reqs
|
||||||
|
mamba create -n build --file $reqs
|
||||||
|
|
||||||
|
- name: Prepare
|
||||||
|
run: |
|
||||||
|
python devscripts/update-version.py -c ${{ inputs.channel }} ${{ inputs.version }}
|
||||||
|
python devscripts/make_lazy_extractors.py
|
||||||
|
- name: Build Unix platform-independent binary
|
||||||
|
run: |
|
||||||
|
make all tar
|
||||||
|
- name: Build Unix standalone binary
|
||||||
|
shell: bash -l {0}
|
||||||
|
run: |
|
||||||
|
unset LD_LIBRARY_PATH # Harmful; set by setup-python
|
||||||
|
conda activate build
|
||||||
|
python pyinst.py --onedir
|
||||||
|
(cd ./dist/yt-dlp_linux && zip -r ../yt-dlp_linux.zip .)
|
||||||
|
python pyinst.py
|
||||||
|
mv ./dist/yt-dlp_linux ./yt-dlp_linux
|
||||||
|
mv ./dist/yt-dlp_linux.zip ./yt-dlp_linux.zip
|
||||||
|
|
||||||
outputs:
|
- name: Verify --update-to
|
||||||
ytdlp_version: ${{ steps.bump_version.outputs.ytdlp_version }}
|
if: vars.UPDATE_TO_VERIFICATION
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
run: |
|
||||||
sha256_bin: ${{ steps.sha256_bin.outputs.sha256_bin }}
|
binaries=("yt-dlp" "yt-dlp_linux")
|
||||||
sha512_bin: ${{ steps.sha512_bin.outputs.sha512_bin }}
|
for binary in "${binaries[@]}"; do
|
||||||
sha256_tar: ${{ steps.sha256_tar.outputs.sha256_tar }}
|
chmod +x ./${binary}
|
||||||
sha512_tar: ${{ steps.sha512_tar.outputs.sha512_tar }}
|
cp ./${binary} ./${binary}_downgraded
|
||||||
|
version="$(./${binary} --version)"
|
||||||
|
./${binary}_downgraded -v --update-to yt-dlp/yt-dlp@2023.03.04
|
||||||
|
downgraded_version="$(./${binary}_downgraded --version)"
|
||||||
|
[[ "$version" != "$downgraded_version" ]]
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
yt-dlp
|
||||||
|
yt-dlp.tar.gz
|
||||||
|
yt-dlp_linux
|
||||||
|
yt-dlp_linux.zip
|
||||||
|
|
||||||
|
linux_arm:
|
||||||
|
if: inputs.linux_arm
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write # for creating cache
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
architecture:
|
||||||
|
- armv7
|
||||||
|
- aarch64
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
path: ./repo
|
||||||
- name: Set up Python
|
- name: Virtualized Install, Prepare & Build
|
||||||
uses: actions/setup-python@v2
|
uses: yt-dlp/run-on-arch-action@v2
|
||||||
with:
|
with:
|
||||||
python-version: '3.8'
|
# Ref: https://github.com/uraimo/run-on-arch-action/issues/55
|
||||||
- name: Install packages
|
env: |
|
||||||
run: sudo apt-get -y install zip pandoc man
|
GITHUB_WORKFLOW: build
|
||||||
- name: Bump version
|
githubToken: ${{ github.token }} # To cache image
|
||||||
id: bump_version
|
arch: ${{ matrix.architecture }}
|
||||||
run: |
|
distro: ubuntu18.04 # Standalone executable should be built on minimum supported OS
|
||||||
python devscripts/update-version.py
|
dockerRunArgs: --volume "${PWD}/repo:/repo"
|
||||||
make issuetemplates
|
install: | # Installing Python 3.10 from the Deadsnakes repo raises errors
|
||||||
- name: Print version
|
apt update
|
||||||
run: echo "${{ steps.bump_version.outputs.ytdlp_version }}"
|
apt -y install zlib1g-dev python3.8 python3.8-dev python3.8-distutils python3-pip
|
||||||
- name: Update master
|
python3.8 -m pip install -U pip setuptools wheel
|
||||||
id: push_update
|
# Cannot access requirements.txt from the repo directory at this stage
|
||||||
run: |
|
python3.8 -m pip install -U Pyinstaller mutagen pycryptodomex websockets brotli certifi
|
||||||
git config --global user.email "${{ github.event.pusher.email }}"
|
|
||||||
git config --global user.name "${{ github.event.pusher.name }}"
|
run: |
|
||||||
git add -u
|
cd repo
|
||||||
git commit -m "[version] update" -m ":ci skip all"
|
python3.8 -m pip install -U Pyinstaller -r requirements.txt # Cached version may be out of date
|
||||||
git pull --rebase origin ${{ github.event.repository.master_branch }}
|
python3.8 devscripts/update-version.py -c ${{ inputs.channel }} ${{ inputs.version }}
|
||||||
git push origin ${{ github.event.ref }}:${{ github.event.repository.master_branch }}
|
python3.8 devscripts/make_lazy_extractors.py
|
||||||
echo ::set-output name=head_sha::$(git rev-parse HEAD)
|
python3.8 pyinst.py
|
||||||
- name: Get Changelog
|
|
||||||
id: get_changelog
|
|
||||||
run: |
|
|
||||||
changelog=$(cat Changelog.md | grep -oPz '(?s)(?<=### ${{ steps.bump_version.outputs.ytdlp_version }}\n{2}).+?(?=\n{2,3}###)')
|
|
||||||
echo "changelog<<EOF" >> $GITHUB_ENV
|
|
||||||
echo "$changelog" >> $GITHUB_ENV
|
|
||||||
echo "EOF" >> $GITHUB_ENV
|
|
||||||
- name: Run Make
|
|
||||||
run: make all tar
|
|
||||||
- name: Get SHA2-256SUMS for yt-dlp
|
|
||||||
id: sha256_bin
|
|
||||||
run: echo "::set-output name=sha256_bin::$(sha256sum yt-dlp | awk '{print $1}')"
|
|
||||||
- name: Get SHA2-256SUMS for yt-dlp.tar.gz
|
|
||||||
id: sha256_tar
|
|
||||||
run: echo "::set-output name=sha256_tar::$(sha256sum yt-dlp.tar.gz | awk '{print $1}')"
|
|
||||||
- name: Get SHA2-512SUMS for yt-dlp
|
|
||||||
id: sha512_bin
|
|
||||||
run: echo "::set-output name=sha512_bin::$(sha512sum yt-dlp | awk '{print $1}')"
|
|
||||||
- name: Get SHA2-512SUMS for yt-dlp.tar.gz
|
|
||||||
id: sha512_tar
|
|
||||||
run: echo "::set-output name=sha512_tar::$(sha512sum yt-dlp.tar.gz | awk '{print $1}')"
|
|
||||||
- name: Install dependencies for pypi
|
|
||||||
env:
|
|
||||||
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
|
|
||||||
if: "env.PYPI_TOKEN != ''"
|
|
||||||
run: |
|
|
||||||
python -m pip install --upgrade pip
|
|
||||||
pip install setuptools wheel twine
|
|
||||||
- name: Build and publish on pypi
|
|
||||||
env:
|
|
||||||
TWINE_USERNAME: __token__
|
|
||||||
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
|
|
||||||
if: "env.TWINE_PASSWORD != ''"
|
|
||||||
run: |
|
|
||||||
rm -rf dist/*
|
|
||||||
python setup.py sdist bdist_wheel
|
|
||||||
twine upload dist/*
|
|
||||||
- name: Install SSH private key
|
|
||||||
env:
|
|
||||||
BREW_TOKEN: ${{ secrets.BREW_TOKEN }}
|
|
||||||
if: "env.BREW_TOKEN != ''"
|
|
||||||
uses: webfactory/ssh-agent@v0.5.3
|
|
||||||
with:
|
|
||||||
ssh-private-key: ${{ env.BREW_TOKEN }}
|
|
||||||
- name: Update Homebrew Formulae
|
|
||||||
env:
|
|
||||||
BREW_TOKEN: ${{ secrets.BREW_TOKEN }}
|
|
||||||
if: "env.BREW_TOKEN != ''"
|
|
||||||
run: |
|
|
||||||
git clone git@github.com:yt-dlp/homebrew-taps taps/
|
|
||||||
python3 devscripts/update-formulae.py taps/Formula/yt-dlp.rb "${{ steps.bump_version.outputs.ytdlp_version }}"
|
|
||||||
git -C taps/ config user.name github-actions
|
|
||||||
git -C taps/ config user.email github-actions@example.com
|
|
||||||
git -C taps/ commit -am 'yt-dlp: ${{ steps.bump_version.outputs.ytdlp_version }}'
|
|
||||||
git -C taps/ push
|
|
||||||
- name: Create Release
|
|
||||||
id: create_release
|
|
||||||
uses: actions/create-release@v1
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
with:
|
|
||||||
tag_name: ${{ steps.bump_version.outputs.ytdlp_version }}
|
|
||||||
release_name: yt-dlp ${{ steps.bump_version.outputs.ytdlp_version }}
|
|
||||||
commitish: ${{ steps.push_update.outputs.head_sha }}
|
|
||||||
body: |
|
|
||||||
Changelog:
|
|
||||||
${{ env.changelog }}
|
|
||||||
draft: false
|
|
||||||
prerelease: false
|
|
||||||
- name: Upload yt-dlp Unix binary
|
|
||||||
id: upload-release-asset
|
|
||||||
uses: actions/upload-release-asset@v1
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
with:
|
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
|
||||||
asset_path: ./yt-dlp
|
|
||||||
asset_name: yt-dlp
|
|
||||||
asset_content_type: application/octet-stream
|
|
||||||
- name: Upload Source tar
|
|
||||||
uses: actions/upload-release-asset@v1
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
with:
|
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
|
||||||
asset_path: ./yt-dlp.tar.gz
|
|
||||||
asset_name: yt-dlp.tar.gz
|
|
||||||
asset_content_type: application/gzip
|
|
||||||
|
|
||||||
build_windows:
|
|
||||||
runs-on: windows-latest
|
|
||||||
needs: build_unix
|
|
||||||
|
|
||||||
outputs:
|
if ${{ vars.UPDATE_TO_VERIFICATION && 'true' || 'false' }}; then
|
||||||
sha256_win: ${{ steps.sha256_win.outputs.sha256_win }}
|
arch="${{ (matrix.architecture == 'armv7' && 'armv7l') || matrix.architecture }}"
|
||||||
sha512_win: ${{ steps.sha512_win.outputs.sha512_win }}
|
chmod +x ./dist/yt-dlp_linux_${arch}
|
||||||
sha256_win_zip: ${{ steps.sha256_win_zip.outputs.sha256_win_zip }}
|
cp ./dist/yt-dlp_linux_${arch} ./dist/yt-dlp_linux_${arch}_downgraded
|
||||||
sha512_win_zip: ${{ steps.sha512_win_zip.outputs.sha512_win_zip }}
|
version="$(./dist/yt-dlp_linux_${arch} --version)"
|
||||||
|
./dist/yt-dlp_linux_${arch}_downgraded -v --update-to yt-dlp/yt-dlp@2023.03.04
|
||||||
|
downgraded_version="$(./dist/yt-dlp_linux_${arch}_downgraded --version)"
|
||||||
|
[[ "$version" != "$downgraded_version" ]]
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
path: | # run-on-arch-action designates armv7l as armv7
|
||||||
|
repo/dist/yt-dlp_linux_${{ (matrix.architecture == 'armv7' && 'armv7l') || matrix.architecture }}
|
||||||
|
|
||||||
|
macos:
|
||||||
|
if: inputs.macos
|
||||||
|
runs-on: macos-11
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
# 3.8 is used for Win7 support
|
# NB: Building universal2 does not work with python from actions/setup-python
|
||||||
- name: Set up Python 3.8
|
- name: Install Requirements
|
||||||
uses: actions/setup-python@v2
|
run: |
|
||||||
with:
|
brew install coreutils
|
||||||
python-version: '3.8'
|
python3 -m pip install -U --user pip setuptools wheel
|
||||||
- name: Upgrade pip and enable wheel support
|
# We need to ignore wheels otherwise we break universal2 builds
|
||||||
run: python -m pip install --upgrade pip setuptools wheel
|
python3 -m pip install -U --user --no-binary :all: Pyinstaller -r requirements.txt
|
||||||
- name: Install Requirements
|
|
||||||
# Custom pyinstaller built with https://github.com/yt-dlp/pyinstaller-builds
|
- name: Prepare
|
||||||
run: pip install "https://yt-dlp.github.io/Pyinstaller-Builds/x86_64/pyinstaller-4.5.1-py3-none-any.whl" mutagen pycryptodome websockets
|
run: |
|
||||||
- name: Bump version
|
python3 devscripts/update-version.py -c ${{ inputs.channel }} ${{ inputs.version }}
|
||||||
id: bump_version
|
python3 devscripts/make_lazy_extractors.py
|
||||||
run: python devscripts/update-version.py
|
- name: Build
|
||||||
- name: Print version
|
run: |
|
||||||
run: echo "${{ steps.bump_version.outputs.ytdlp_version }}"
|
python3 pyinst.py --target-architecture universal2 --onedir
|
||||||
- name: Run PyInstaller Script
|
(cd ./dist/yt-dlp_macos && zip -r ../yt-dlp_macos.zip .)
|
||||||
run: python pyinst.py
|
python3 pyinst.py --target-architecture universal2
|
||||||
- name: Upload yt-dlp.exe Windows binary
|
|
||||||
id: upload-release-windows
|
- name: Verify --update-to
|
||||||
uses: actions/upload-release-asset@v1
|
if: vars.UPDATE_TO_VERIFICATION
|
||||||
env:
|
run: |
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
chmod +x ./dist/yt-dlp_macos
|
||||||
with:
|
cp ./dist/yt-dlp_macos ./dist/yt-dlp_macos_downgraded
|
||||||
upload_url: ${{ needs.build_unix.outputs.upload_url }}
|
version="$(./dist/yt-dlp_macos --version)"
|
||||||
asset_path: ./dist/yt-dlp.exe
|
./dist/yt-dlp_macos_downgraded -v --update-to yt-dlp/yt-dlp@2023.03.04
|
||||||
asset_name: yt-dlp.exe
|
downgraded_version="$(./dist/yt-dlp_macos_downgraded --version)"
|
||||||
asset_content_type: application/vnd.microsoft.portable-executable
|
[[ "$version" != "$downgraded_version" ]]
|
||||||
- name: Get SHA2-256SUMS for yt-dlp.exe
|
|
||||||
id: sha256_win
|
- name: Upload artifacts
|
||||||
run: echo "::set-output name=sha256_win::$((Get-FileHash dist\yt-dlp.exe -Algorithm SHA256).Hash.ToLower())"
|
uses: actions/upload-artifact@v3
|
||||||
- name: Get SHA2-512SUMS for yt-dlp.exe
|
with:
|
||||||
id: sha512_win
|
path: |
|
||||||
run: echo "::set-output name=sha512_win::$((Get-FileHash dist\yt-dlp.exe -Algorithm SHA512).Hash.ToLower())"
|
dist/yt-dlp_macos
|
||||||
- name: Run PyInstaller Script with --onedir
|
dist/yt-dlp_macos.zip
|
||||||
run: python pyinst.py --onedir
|
|
||||||
- uses: papeloto/action-zip@v1
|
macos_legacy:
|
||||||
with:
|
if: inputs.macos_legacy
|
||||||
files: ./dist/yt-dlp
|
runs-on: macos-latest
|
||||||
dest: ./dist/yt-dlp.zip
|
|
||||||
- name: Upload yt-dlp.zip Windows onedir
|
steps:
|
||||||
id: upload-release-windows-zip
|
- uses: actions/checkout@v3
|
||||||
uses: actions/upload-release-asset@v1
|
- name: Install Python
|
||||||
env:
|
# We need the official Python, because the GA ones only support newer macOS versions
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
env:
|
||||||
with:
|
PYTHON_VERSION: 3.10.5
|
||||||
upload_url: ${{ needs.build_unix.outputs.upload_url }}
|
MACOSX_DEPLOYMENT_TARGET: 10.9 # Used up by the Python build tools
|
||||||
asset_path: ./dist/yt-dlp.zip
|
run: |
|
||||||
asset_name: yt-dlp.zip
|
# Hack to get the latest patch version. Uncomment if needed
|
||||||
asset_content_type: application/zip
|
#brew install python@3.10
|
||||||
- name: Get SHA2-256SUMS for yt-dlp.zip
|
#export PYTHON_VERSION=$( $(brew --prefix)/opt/python@3.10/bin/python3 --version | cut -d ' ' -f 2 )
|
||||||
id: sha256_win_zip
|
curl https://www.python.org/ftp/python/${PYTHON_VERSION}/python-${PYTHON_VERSION}-macos11.pkg -o "python.pkg"
|
||||||
run: echo "::set-output name=sha256_win_zip::$((Get-FileHash dist\yt-dlp.zip -Algorithm SHA256).Hash.ToLower())"
|
sudo installer -pkg python.pkg -target /
|
||||||
- name: Get SHA2-512SUMS for yt-dlp.zip
|
python3 --version
|
||||||
id: sha512_win_zip
|
- name: Install Requirements
|
||||||
run: echo "::set-output name=sha512_win_zip::$((Get-FileHash dist\yt-dlp.zip -Algorithm SHA512).Hash.ToLower())"
|
run: |
|
||||||
|
brew install coreutils
|
||||||
build_windows32:
|
python3 -m pip install -U --user pip setuptools wheel
|
||||||
|
python3 -m pip install -U --user Pyinstaller -r requirements.txt
|
||||||
|
|
||||||
|
- name: Prepare
|
||||||
|
run: |
|
||||||
|
python3 devscripts/update-version.py -c ${{ inputs.channel }} ${{ inputs.version }}
|
||||||
|
python3 devscripts/make_lazy_extractors.py
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
python3 pyinst.py
|
||||||
|
mv dist/yt-dlp_macos dist/yt-dlp_macos_legacy
|
||||||
|
|
||||||
|
- name: Verify --update-to
|
||||||
|
if: vars.UPDATE_TO_VERIFICATION
|
||||||
|
run: |
|
||||||
|
chmod +x ./dist/yt-dlp_macos_legacy
|
||||||
|
cp ./dist/yt-dlp_macos_legacy ./dist/yt-dlp_macos_legacy_downgraded
|
||||||
|
version="$(./dist/yt-dlp_macos_legacy --version)"
|
||||||
|
./dist/yt-dlp_macos_legacy_downgraded -v --update-to yt-dlp/yt-dlp@2023.03.04
|
||||||
|
downgraded_version="$(./dist/yt-dlp_macos_legacy_downgraded --version)"
|
||||||
|
[[ "$version" != "$downgraded_version" ]]
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
dist/yt-dlp_macos_legacy
|
||||||
|
|
||||||
|
windows:
|
||||||
|
if: inputs.windows
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
needs: [build_unix, build_windows]
|
|
||||||
|
|
||||||
outputs:
|
steps:
|
||||||
sha256_win32: ${{ steps.sha256_win32.outputs.sha256_win32 }}
|
- uses: actions/checkout@v3
|
||||||
sha512_win32: ${{ steps.sha512_win32.outputs.sha512_win32 }}
|
- uses: actions/setup-python@v4
|
||||||
|
with: # 3.8 is used for Win7 support
|
||||||
|
python-version: "3.8"
|
||||||
|
- name: Install Requirements
|
||||||
|
run: | # Custom pyinstaller built with https://github.com/yt-dlp/pyinstaller-builds
|
||||||
|
python -m pip install -U pip setuptools wheel py2exe
|
||||||
|
pip install -U "https://yt-dlp.github.io/Pyinstaller-Builds/x86_64/pyinstaller-5.8.0-py3-none-any.whl" -r requirements.txt
|
||||||
|
|
||||||
|
- name: Prepare
|
||||||
|
run: |
|
||||||
|
python devscripts/update-version.py -c ${{ inputs.channel }} ${{ inputs.version }}
|
||||||
|
python devscripts/make_lazy_extractors.py
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
python setup.py py2exe
|
||||||
|
Move-Item ./dist/yt-dlp.exe ./dist/yt-dlp_min.exe
|
||||||
|
python pyinst.py
|
||||||
|
python pyinst.py --onedir
|
||||||
|
Compress-Archive -Path ./dist/yt-dlp/* -DestinationPath ./dist/yt-dlp_win.zip
|
||||||
|
|
||||||
|
- name: Verify --update-to
|
||||||
|
if: vars.UPDATE_TO_VERIFICATION
|
||||||
|
run: |
|
||||||
|
foreach ($name in @("yt-dlp","yt-dlp_min")) {
|
||||||
|
Copy-Item "./dist/${name}.exe" "./dist/${name}_downgraded.exe"
|
||||||
|
$version = & "./dist/${name}.exe" --version
|
||||||
|
& "./dist/${name}_downgraded.exe" -v --update-to yt-dlp/yt-dlp@2023.03.04
|
||||||
|
$downgraded_version = & "./dist/${name}_downgraded.exe" --version
|
||||||
|
if ($version -eq $downgraded_version) {
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
dist/yt-dlp.exe
|
||||||
|
dist/yt-dlp_min.exe
|
||||||
|
dist/yt-dlp_win.zip
|
||||||
|
|
||||||
|
windows32:
|
||||||
|
if: inputs.windows32
|
||||||
|
runs-on: windows-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
# 3.7 is used for Vista support. See https://github.com/yt-dlp/yt-dlp/issues/390
|
- uses: actions/setup-python@v4
|
||||||
- name: Set up Python 3.7 32-Bit
|
with: # 3.7 is used for Vista support. See https://github.com/yt-dlp/yt-dlp/issues/390
|
||||||
uses: actions/setup-python@v2
|
python-version: "3.7"
|
||||||
with:
|
architecture: "x86"
|
||||||
python-version: '3.7'
|
- name: Install Requirements
|
||||||
architecture: 'x86'
|
run: |
|
||||||
- name: Upgrade pip and enable wheel support
|
python -m pip install -U pip setuptools wheel
|
||||||
run: python -m pip install --upgrade pip setuptools wheel
|
pip install -U "https://yt-dlp.github.io/Pyinstaller-Builds/i686/pyinstaller-5.8.0-py3-none-any.whl" -r requirements.txt
|
||||||
- name: Install Requirements
|
|
||||||
run: pip install "https://yt-dlp.github.io/Pyinstaller-Builds/i686/pyinstaller-4.5.1-py3-none-any.whl" mutagen pycryptodome websockets
|
- name: Prepare
|
||||||
- name: Bump version
|
run: |
|
||||||
id: bump_version
|
python devscripts/update-version.py -c ${{ inputs.channel }} ${{ inputs.version }}
|
||||||
run: python devscripts/update-version.py
|
python devscripts/make_lazy_extractors.py
|
||||||
- name: Print version
|
- name: Build
|
||||||
run: echo "${{ steps.bump_version.outputs.ytdlp_version }}"
|
run: |
|
||||||
- name: Run PyInstaller Script for 32 Bit
|
python pyinst.py
|
||||||
run: python pyinst.py
|
|
||||||
- name: Upload Executable yt-dlp_x86.exe
|
- name: Verify --update-to
|
||||||
id: upload-release-windows32
|
if: vars.UPDATE_TO_VERIFICATION
|
||||||
uses: actions/upload-release-asset@v1
|
run: |
|
||||||
env:
|
foreach ($name in @("yt-dlp_x86")) {
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
Copy-Item "./dist/${name}.exe" "./dist/${name}_downgraded.exe"
|
||||||
with:
|
$version = & "./dist/${name}.exe" --version
|
||||||
upload_url: ${{ needs.build_unix.outputs.upload_url }}
|
& "./dist/${name}_downgraded.exe" -v --update-to yt-dlp/yt-dlp@2023.03.04
|
||||||
asset_path: ./dist/yt-dlp_x86.exe
|
$downgraded_version = & "./dist/${name}_downgraded.exe" --version
|
||||||
asset_name: yt-dlp_x86.exe
|
if ($version -eq $downgraded_version) {
|
||||||
asset_content_type: application/vnd.microsoft.portable-executable
|
exit 1
|
||||||
- name: Get SHA2-256SUMS for yt-dlp_x86.exe
|
}
|
||||||
id: sha256_win32
|
}
|
||||||
run: echo "::set-output name=sha256_win32::$((Get-FileHash dist\yt-dlp_x86.exe -Algorithm SHA256).Hash.ToLower())"
|
|
||||||
- name: Get SHA2-512SUMS for yt-dlp_x86.exe
|
|
||||||
id: sha512_win32
|
|
||||||
run: echo "::set-output name=sha512_win32::$((Get-FileHash dist\yt-dlp_x86.exe -Algorithm SHA512).Hash.ToLower())"
|
|
||||||
|
|
||||||
finish:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [build_unix, build_windows, build_windows32]
|
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
dist/yt-dlp_x86.exe
|
||||||
|
|
||||||
|
meta_files:
|
||||||
|
if: inputs.meta_files && always() && !cancelled()
|
||||||
|
needs:
|
||||||
|
- unix
|
||||||
|
- linux_arm
|
||||||
|
- macos
|
||||||
|
- macos_legacy
|
||||||
|
- windows
|
||||||
|
- windows32
|
||||||
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Make SHA2-256SUMS file
|
- uses: actions/download-artifact@v3
|
||||||
env:
|
|
||||||
SHA256_WIN: ${{ needs.build_windows.outputs.sha256_win }}
|
- name: Make SHA2-SUMS files
|
||||||
SHA256_WIN_ZIP: ${{ needs.build_windows.outputs.sha256_win_zip }}
|
run: |
|
||||||
SHA256_WIN32: ${{ needs.build_windows32.outputs.sha256_win32 }}
|
cd ./artifact/
|
||||||
SHA256_BIN: ${{ needs.build_unix.outputs.sha256_bin }}
|
sha256sum * > ../SHA2-256SUMS
|
||||||
SHA256_TAR: ${{ needs.build_unix.outputs.sha256_tar }}
|
sha512sum * > ../SHA2-512SUMS
|
||||||
run: |
|
|
||||||
echo "${{ env.SHA256_WIN }} yt-dlp.exe" >> SHA2-256SUMS
|
- name: Make Update spec
|
||||||
echo "${{ env.SHA256_WIN32 }} yt-dlp_x86.exe" >> SHA2-256SUMS
|
run: |
|
||||||
echo "${{ env.SHA256_BIN }} yt-dlp" >> SHA2-256SUMS
|
cat >> _update_spec << EOF
|
||||||
echo "${{ env.SHA256_TAR }} yt-dlp.tar.gz" >> SHA2-256SUMS
|
# This file is used for regulating self-update
|
||||||
echo "${{ env.SHA256_WIN_ZIP }} yt-dlp.zip" >> SHA2-256SUMS
|
lock 2022.08.18.36 .+ Python 3.6
|
||||||
- name: Upload 256SUMS file
|
EOF
|
||||||
id: upload-sums
|
|
||||||
uses: actions/upload-release-asset@v1
|
- name: Sign checksum files
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GPG_SIGNING_KEY: ${{ secrets.GPG_SIGNING_KEY }}
|
||||||
with:
|
if: env.GPG_SIGNING_KEY != ''
|
||||||
upload_url: ${{ needs.build_unix.outputs.upload_url }}
|
run: |
|
||||||
asset_path: ./SHA2-256SUMS
|
gpg --batch --import <<< "${{ secrets.GPG_SIGNING_KEY }}"
|
||||||
asset_name: SHA2-256SUMS
|
for signfile in ./SHA*SUMS; do
|
||||||
asset_content_type: text/plain
|
gpg --batch --detach-sign "$signfile"
|
||||||
- name: Make SHA2-512SUMS file
|
done
|
||||||
env:
|
|
||||||
SHA512_WIN: ${{ needs.build_windows.outputs.sha512_win }}
|
- name: Upload artifacts
|
||||||
SHA512_WIN_ZIP: ${{ needs.build_windows.outputs.sha512_win_zip }}
|
uses: actions/upload-artifact@v3
|
||||||
SHA512_WIN32: ${{ needs.build_windows32.outputs.sha512_win32 }}
|
with:
|
||||||
SHA512_BIN: ${{ needs.build_unix.outputs.sha512_bin }}
|
path: |
|
||||||
SHA512_TAR: ${{ needs.build_unix.outputs.sha512_tar }}
|
SHA*SUMS*
|
||||||
run: |
|
_update_spec
|
||||||
echo "${{ env.SHA512_WIN }} yt-dlp.exe" >> SHA2-512SUMS
|
|
||||||
echo "${{ env.SHA512_WIN32 }} yt-dlp_x86.exe" >> SHA2-512SUMS
|
|
||||||
echo "${{ env.SHA512_BIN }} yt-dlp" >> SHA2-512SUMS
|
|
||||||
echo "${{ env.SHA512_TAR }} yt-dlp.tar.gz" >> SHA2-512SUMS
|
|
||||||
echo "${{ env.SHA512_WIN_ZIP }} yt-dlp.zip" >> SHA2-512SUMS
|
|
||||||
- name: Upload 512SUMS file
|
|
||||||
id: upload-512sums
|
|
||||||
uses: actions/upload-release-asset@v1
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
with:
|
|
||||||
upload_url: ${{ needs.build_unix.outputs.upload_url }}
|
|
||||||
asset_path: ./SHA2-512SUMS
|
|
||||||
asset_name: SHA2-512SUMS
|
|
||||||
asset_content_type: text/plain
|
|
||||||
|
@ -0,0 +1,97 @@
|
|||||||
|
name: Publish
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
channel:
|
||||||
|
default: stable
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
version:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
target_commitish:
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
prerelease:
|
||||||
|
default: false
|
||||||
|
required: true
|
||||||
|
type: boolean
|
||||||
|
secrets:
|
||||||
|
ARCHIVE_REPO_TOKEN:
|
||||||
|
required: false
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- uses: actions/download-artifact@v3
|
||||||
|
- uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: "3.10"
|
||||||
|
|
||||||
|
- name: Generate release notes
|
||||||
|
run: |
|
||||||
|
printf '%s' \
|
||||||
|
'[![Installation](https://img.shields.io/badge/-Which%20file%20should%20I%20download%3F-white.svg?style=for-the-badge)]' \
|
||||||
|
'(https://github.com/yt-dlp/yt-dlp#installation "Installation instructions") ' \
|
||||||
|
'[![Documentation](https://img.shields.io/badge/-Docs-brightgreen.svg?style=for-the-badge&logo=GitBook&labelColor=555555)]' \
|
||||||
|
'(https://github.com/yt-dlp/yt-dlp/tree/2023.03.04#readme "Documentation") ' \
|
||||||
|
'[![Donate](https://img.shields.io/badge/_-Donate-red.svg?logo=githubsponsors&labelColor=555555&style=for-the-badge)]' \
|
||||||
|
'(https://github.com/yt-dlp/yt-dlp/blob/master/Collaborators.md#collaborators "Donate") ' \
|
||||||
|
'[![Discord](https://img.shields.io/discord/807245652072857610?color=blue&labelColor=555555&label=&logo=discord&style=for-the-badge)]' \
|
||||||
|
'(https://discord.gg/H5MNcFW63r "Discord") ' \
|
||||||
|
${{ inputs.channel != 'nightly' && '"[![Nightly](https://img.shields.io/badge/Get%20nightly%20builds-purple.svg?style=for-the-badge)]" \
|
||||||
|
"(https://github.com/yt-dlp/yt-dlp-nightly-builds/releases/latest \"Nightly builds\")"' || '' }} \
|
||||||
|
> ./RELEASE_NOTES
|
||||||
|
printf '\n\n' >> ./RELEASE_NOTES
|
||||||
|
cat >> ./RELEASE_NOTES << EOF
|
||||||
|
#### A description of the various files are in the [README](https://github.com/yt-dlp/yt-dlp#release-files)
|
||||||
|
---
|
||||||
|
$(python ./devscripts/make_changelog.py -vv --collapsible)
|
||||||
|
EOF
|
||||||
|
printf '%s\n\n' '**This is an automated nightly pre-release build**' >> ./NIGHTLY_NOTES
|
||||||
|
cat ./RELEASE_NOTES >> ./NIGHTLY_NOTES
|
||||||
|
printf '%s\n\n' 'Generated from: https://github.com/${{ github.repository }}/commit/${{ inputs.target_commitish }}' >> ./ARCHIVE_NOTES
|
||||||
|
cat ./RELEASE_NOTES >> ./ARCHIVE_NOTES
|
||||||
|
|
||||||
|
- name: Archive nightly release
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.ARCHIVE_REPO_TOKEN }}
|
||||||
|
GH_REPO: ${{ vars.ARCHIVE_REPO }}
|
||||||
|
if: |
|
||||||
|
inputs.channel == 'nightly' && env.GH_TOKEN != '' && env.GH_REPO != ''
|
||||||
|
run: |
|
||||||
|
gh release create \
|
||||||
|
--notes-file ARCHIVE_NOTES \
|
||||||
|
--title "yt-dlp nightly ${{ inputs.version }}" \
|
||||||
|
${{ inputs.version }} \
|
||||||
|
artifact/*
|
||||||
|
|
||||||
|
- name: Prune old nightly release
|
||||||
|
if: inputs.channel == 'nightly' && !vars.ARCHIVE_REPO
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
run: |
|
||||||
|
gh release delete --yes --cleanup-tag "nightly" || true
|
||||||
|
git tag --delete "nightly" || true
|
||||||
|
sleep 5 # Enough time to cover deletion race condition
|
||||||
|
|
||||||
|
- name: Publish release${{ inputs.channel == 'nightly' && ' (nightly)' || '' }}
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
if: (inputs.channel == 'nightly' && !vars.ARCHIVE_REPO) || inputs.channel != 'nightly'
|
||||||
|
run: |
|
||||||
|
gh release create \
|
||||||
|
--notes-file ${{ inputs.channel == 'nightly' && 'NIGHTLY_NOTES' || 'RELEASE_NOTES' }} \
|
||||||
|
--target ${{ inputs.target_commitish }} \
|
||||||
|
--title "yt-dlp ${{ inputs.channel == 'nightly' && 'nightly ' || '' }}${{ inputs.version }}" \
|
||||||
|
${{ inputs.prerelease && '--prerelease' || '' }} \
|
||||||
|
${{ inputs.channel == 'nightly' && '"nightly"' || inputs.version }} \
|
||||||
|
artifact/*
|
@ -1,33 +1,35 @@
|
|||||||
name: Quick Test
|
name: Quick Test
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
tests:
|
tests:
|
||||||
name: Core Test
|
name: Core Test
|
||||||
if: "!contains(github.event.head_commit.message, 'ci skip all')"
|
if: "!contains(github.event.head_commit.message, 'ci skip all')"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Set up Python
|
- name: Set up Python 3.11
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: 3.9
|
python-version: '3.11'
|
||||||
- name: Install test requirements
|
- name: Install test requirements
|
||||||
run: pip install pytest pycryptodome
|
run: pip install pytest pycryptodomex
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: ./devscripts/run_tests.sh core
|
run: |
|
||||||
|
python3 -m yt_dlp -v || true
|
||||||
|
./devscripts/run_tests.sh core
|
||||||
flake8:
|
flake8:
|
||||||
name: Linter
|
name: Linter
|
||||||
if: "!contains(github.event.head_commit.message, 'ci skip all')"
|
if: "!contains(github.event.head_commit.message, 'ci skip all')"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Set up Python
|
- uses: actions/setup-python@v4
|
||||||
uses: actions/setup-python@v2
|
|
||||||
with:
|
|
||||||
python-version: 3.9
|
|
||||||
- name: Install flake8
|
- name: Install flake8
|
||||||
run: pip install flake8
|
run: pip install flake8
|
||||||
- name: Make lazy extractors
|
- name: Make lazy extractors
|
||||||
run: python devscripts/make_lazy_extractors.py yt_dlp/extractor/lazy_extractors.py
|
run: python devscripts/make_lazy_extractors.py
|
||||||
- name: Run flake8
|
- name: Run flake8
|
||||||
run: flake8 .
|
run: flake8 .
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
name: Release (nightly)
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
paths:
|
||||||
|
- "yt_dlp/**.py"
|
||||||
|
- "!yt_dlp/version.py"
|
||||||
|
concurrency:
|
||||||
|
group: release-nightly
|
||||||
|
cancel-in-progress: true
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
prepare:
|
||||||
|
if: vars.BUILD_NIGHTLY != ''
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
version: ${{ steps.get_version.outputs.version }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Get version
|
||||||
|
id: get_version
|
||||||
|
run: |
|
||||||
|
python devscripts/update-version.py "$(date -u +"%H%M%S")" | grep -Po "version=\d+(\.\d+){3}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
build:
|
||||||
|
needs: prepare
|
||||||
|
uses: ./.github/workflows/build.yml
|
||||||
|
with:
|
||||||
|
version: ${{ needs.prepare.outputs.version }}
|
||||||
|
channel: nightly
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write # For package cache
|
||||||
|
secrets:
|
||||||
|
GPG_SIGNING_KEY: ${{ secrets.GPG_SIGNING_KEY }}
|
||||||
|
|
||||||
|
publish:
|
||||||
|
needs: [prepare, build]
|
||||||
|
uses: ./.github/workflows/publish.yml
|
||||||
|
secrets:
|
||||||
|
ARCHIVE_REPO_TOKEN: ${{ secrets.ARCHIVE_REPO_TOKEN }}
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
with:
|
||||||
|
channel: nightly
|
||||||
|
prerelease: true
|
||||||
|
version: ${{ needs.prepare.outputs.version }}
|
||||||
|
target_commitish: ${{ github.sha }}
|
@ -0,0 +1,163 @@
|
|||||||
|
name: Release
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: Version tag (YYYY.MM.DD[.REV])
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
type: string
|
||||||
|
channel:
|
||||||
|
description: Update channel (stable/nightly/...)
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
type: string
|
||||||
|
prerelease:
|
||||||
|
description: Pre-release
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
prepare:
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
channel: ${{ steps.set_channel.outputs.channel }}
|
||||||
|
version: ${{ steps.update_version.outputs.version }}
|
||||||
|
head_sha: ${{ steps.get_target.outputs.head_sha }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: "3.10"
|
||||||
|
|
||||||
|
- name: Set channel
|
||||||
|
id: set_channel
|
||||||
|
run: |
|
||||||
|
CHANNEL="${{ github.repository == 'yt-dlp/yt-dlp' && 'stable' || github.repository }}"
|
||||||
|
echo "channel=${{ inputs.channel || '$CHANNEL' }}" > "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Update version
|
||||||
|
id: update_version
|
||||||
|
run: |
|
||||||
|
REVISION="${{ vars.PUSH_VERSION_COMMIT == '' && '$(date -u +"%H%M%S")' || '' }}"
|
||||||
|
REVISION="${{ inputs.prerelease && '$(date -u +"%H%M%S")' || '$REVISION' }}"
|
||||||
|
python devscripts/update-version.py ${{ inputs.version || '$REVISION' }} | \
|
||||||
|
grep -Po "version=\d+\.\d+\.\d+(\.\d+)?" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Update documentation
|
||||||
|
run: |
|
||||||
|
make doc
|
||||||
|
sed '/### /Q' Changelog.md >> ./CHANGELOG
|
||||||
|
echo '### ${{ steps.update_version.outputs.version }}' >> ./CHANGELOG
|
||||||
|
python ./devscripts/make_changelog.py -vv -c >> ./CHANGELOG
|
||||||
|
echo >> ./CHANGELOG
|
||||||
|
grep -Poz '(?s)### \d+\.\d+\.\d+.+' 'Changelog.md' | head -n -1 >> ./CHANGELOG
|
||||||
|
cat ./CHANGELOG > Changelog.md
|
||||||
|
|
||||||
|
- name: Push to release
|
||||||
|
id: push_release
|
||||||
|
if: ${{ !inputs.prerelease }}
|
||||||
|
run: |
|
||||||
|
git config --global user.name github-actions
|
||||||
|
git config --global user.email github-actions@example.com
|
||||||
|
git add -u
|
||||||
|
git commit -m "Release ${{ steps.update_version.outputs.version }}" \
|
||||||
|
-m "Created by: ${{ github.event.sender.login }}" -m ":ci skip all :ci run dl"
|
||||||
|
git push origin --force ${{ github.event.ref }}:release
|
||||||
|
|
||||||
|
- name: Get target commitish
|
||||||
|
id: get_target
|
||||||
|
run: |
|
||||||
|
echo "head_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Update master
|
||||||
|
if: vars.PUSH_VERSION_COMMIT != '' && !inputs.prerelease
|
||||||
|
run: git push origin ${{ github.event.ref }}
|
||||||
|
|
||||||
|
build:
|
||||||
|
needs: prepare
|
||||||
|
uses: ./.github/workflows/build.yml
|
||||||
|
with:
|
||||||
|
version: ${{ needs.prepare.outputs.version }}
|
||||||
|
channel: ${{ needs.prepare.outputs.channel }}
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write # For package cache
|
||||||
|
secrets:
|
||||||
|
GPG_SIGNING_KEY: ${{ secrets.GPG_SIGNING_KEY }}
|
||||||
|
|
||||||
|
publish_pypi_homebrew:
|
||||||
|
needs: [prepare, build]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: "3.10"
|
||||||
|
|
||||||
|
- name: Install Requirements
|
||||||
|
run: |
|
||||||
|
sudo apt-get -y install pandoc man
|
||||||
|
python -m pip install -U pip setuptools wheel twine
|
||||||
|
python -m pip install -U -r requirements.txt
|
||||||
|
|
||||||
|
- name: Prepare
|
||||||
|
run: |
|
||||||
|
python devscripts/update-version.py ${{ needs.prepare.outputs.version }}
|
||||||
|
python devscripts/make_lazy_extractors.py
|
||||||
|
|
||||||
|
- name: Build and publish on PyPI
|
||||||
|
env:
|
||||||
|
TWINE_USERNAME: __token__
|
||||||
|
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
|
||||||
|
if: env.TWINE_PASSWORD != '' && !inputs.prerelease
|
||||||
|
run: |
|
||||||
|
rm -rf dist/*
|
||||||
|
make pypi-files
|
||||||
|
python devscripts/set-variant.py pip -M "You installed yt-dlp with pip or using the wheel from PyPi; Use that to update"
|
||||||
|
python setup.py sdist bdist_wheel
|
||||||
|
twine upload dist/*
|
||||||
|
|
||||||
|
- name: Checkout Homebrew repository
|
||||||
|
env:
|
||||||
|
BREW_TOKEN: ${{ secrets.BREW_TOKEN }}
|
||||||
|
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
|
||||||
|
if: env.BREW_TOKEN != '' && env.PYPI_TOKEN != '' && !inputs.prerelease
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
repository: yt-dlp/homebrew-taps
|
||||||
|
path: taps
|
||||||
|
ssh-key: ${{ secrets.BREW_TOKEN }}
|
||||||
|
|
||||||
|
- name: Update Homebrew Formulae
|
||||||
|
env:
|
||||||
|
BREW_TOKEN: ${{ secrets.BREW_TOKEN }}
|
||||||
|
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
|
||||||
|
if: env.BREW_TOKEN != '' && env.PYPI_TOKEN != '' && !inputs.prerelease
|
||||||
|
run: |
|
||||||
|
python devscripts/update-formulae.py taps/Formula/yt-dlp.rb "${{ needs.prepare.outputs.version }}"
|
||||||
|
git -C taps/ config user.name github-actions
|
||||||
|
git -C taps/ config user.email github-actions@example.com
|
||||||
|
git -C taps/ commit -am 'yt-dlp: ${{ needs.prepare.outputs.version }}'
|
||||||
|
git -C taps/ push
|
||||||
|
|
||||||
|
publish:
|
||||||
|
needs: [prepare, build]
|
||||||
|
uses: ./.github/workflows/publish.yml
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
with:
|
||||||
|
channel: ${{ needs.prepare.outputs.channel }}
|
||||||
|
prerelease: ${{ inputs.prerelease }}
|
||||||
|
version: ${{ needs.prepare.outputs.version }}
|
||||||
|
target_commitish: ${{ needs.prepare.outputs.head_sha }}
|
@ -1,22 +0,0 @@
|
|||||||
# .readthedocs.yaml
|
|
||||||
# Read the Docs configuration file
|
|
||||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
|
||||||
|
|
||||||
# Required
|
|
||||||
version: 2
|
|
||||||
|
|
||||||
# Build documentation in the docs/ directory with Sphinx
|
|
||||||
sphinx:
|
|
||||||
configuration: docs/conf.py
|
|
||||||
|
|
||||||
# Optionally build your docs in additional formats such as PDF
|
|
||||||
formats:
|
|
||||||
- epub
|
|
||||||
- pdf
|
|
||||||
- htmlzip
|
|
||||||
|
|
||||||
# Optionally set the version of Python and requirements required to build your docs
|
|
||||||
python:
|
|
||||||
version: 3
|
|
||||||
install:
|
|
||||||
- requirements: docs/requirements.txt
|
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1 @@
|
|||||||
|
# Empty file needed to make devscripts.utils properly importable from outside
|
@ -1,435 +0,0 @@
|
|||||||
# UNUSED
|
|
||||||
|
|
||||||
#!/usr/bin/python3
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import ctypes
|
|
||||||
import functools
|
|
||||||
import shutil
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import tempfile
|
|
||||||
import threading
|
|
||||||
import traceback
|
|
||||||
import os.path
|
|
||||||
|
|
||||||
sys.path.insert(0, os.path.dirname(os.path.dirname((os.path.abspath(__file__)))))
|
|
||||||
from yt_dlp.compat import (
|
|
||||||
compat_input,
|
|
||||||
compat_http_server,
|
|
||||||
compat_str,
|
|
||||||
compat_urlparse,
|
|
||||||
)
|
|
||||||
|
|
||||||
# These are not used outside of buildserver.py thus not in compat.py
|
|
||||||
|
|
||||||
try:
|
|
||||||
import winreg as compat_winreg
|
|
||||||
except ImportError: # Python 2
|
|
||||||
import _winreg as compat_winreg
|
|
||||||
|
|
||||||
try:
|
|
||||||
import socketserver as compat_socketserver
|
|
||||||
except ImportError: # Python 2
|
|
||||||
import SocketServer as compat_socketserver
|
|
||||||
|
|
||||||
|
|
||||||
class BuildHTTPServer(compat_socketserver.ThreadingMixIn, compat_http_server.HTTPServer):
|
|
||||||
allow_reuse_address = True
|
|
||||||
|
|
||||||
|
|
||||||
advapi32 = ctypes.windll.advapi32
|
|
||||||
|
|
||||||
SC_MANAGER_ALL_ACCESS = 0xf003f
|
|
||||||
SC_MANAGER_CREATE_SERVICE = 0x02
|
|
||||||
SERVICE_WIN32_OWN_PROCESS = 0x10
|
|
||||||
SERVICE_AUTO_START = 0x2
|
|
||||||
SERVICE_ERROR_NORMAL = 0x1
|
|
||||||
DELETE = 0x00010000
|
|
||||||
SERVICE_STATUS_START_PENDING = 0x00000002
|
|
||||||
SERVICE_STATUS_RUNNING = 0x00000004
|
|
||||||
SERVICE_ACCEPT_STOP = 0x1
|
|
||||||
|
|
||||||
SVCNAME = 'youtubedl_builder'
|
|
||||||
|
|
||||||
LPTSTR = ctypes.c_wchar_p
|
|
||||||
START_CALLBACK = ctypes.WINFUNCTYPE(None, ctypes.c_int, ctypes.POINTER(LPTSTR))
|
|
||||||
|
|
||||||
|
|
||||||
class SERVICE_TABLE_ENTRY(ctypes.Structure):
|
|
||||||
_fields_ = [
|
|
||||||
('lpServiceName', LPTSTR),
|
|
||||||
('lpServiceProc', START_CALLBACK)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
HandlerEx = ctypes.WINFUNCTYPE(
|
|
||||||
ctypes.c_int, # return
|
|
||||||
ctypes.c_int, # dwControl
|
|
||||||
ctypes.c_int, # dwEventType
|
|
||||||
ctypes.c_void_p, # lpEventData,
|
|
||||||
ctypes.c_void_p, # lpContext,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _ctypes_array(c_type, py_array):
|
|
||||||
ar = (c_type * len(py_array))()
|
|
||||||
ar[:] = py_array
|
|
||||||
return ar
|
|
||||||
|
|
||||||
|
|
||||||
def win_OpenSCManager():
|
|
||||||
res = advapi32.OpenSCManagerW(None, None, SC_MANAGER_ALL_ACCESS)
|
|
||||||
if not res:
|
|
||||||
raise Exception('Opening service manager failed - '
|
|
||||||
'are you running this as administrator?')
|
|
||||||
return res
|
|
||||||
|
|
||||||
|
|
||||||
def win_install_service(service_name, cmdline):
|
|
||||||
manager = win_OpenSCManager()
|
|
||||||
try:
|
|
||||||
h = advapi32.CreateServiceW(
|
|
||||||
manager, service_name, None,
|
|
||||||
SC_MANAGER_CREATE_SERVICE, SERVICE_WIN32_OWN_PROCESS,
|
|
||||||
SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
|
|
||||||
cmdline, None, None, None, None, None)
|
|
||||||
if not h:
|
|
||||||
raise OSError('Service creation failed: %s' % ctypes.FormatError())
|
|
||||||
|
|
||||||
advapi32.CloseServiceHandle(h)
|
|
||||||
finally:
|
|
||||||
advapi32.CloseServiceHandle(manager)
|
|
||||||
|
|
||||||
|
|
||||||
def win_uninstall_service(service_name):
|
|
||||||
manager = win_OpenSCManager()
|
|
||||||
try:
|
|
||||||
h = advapi32.OpenServiceW(manager, service_name, DELETE)
|
|
||||||
if not h:
|
|
||||||
raise OSError('Could not find service %s: %s' % (
|
|
||||||
service_name, ctypes.FormatError()))
|
|
||||||
|
|
||||||
try:
|
|
||||||
if not advapi32.DeleteService(h):
|
|
||||||
raise OSError('Deletion failed: %s' % ctypes.FormatError())
|
|
||||||
finally:
|
|
||||||
advapi32.CloseServiceHandle(h)
|
|
||||||
finally:
|
|
||||||
advapi32.CloseServiceHandle(manager)
|
|
||||||
|
|
||||||
|
|
||||||
def win_service_report_event(service_name, msg, is_error=True):
|
|
||||||
with open('C:/sshkeys/log', 'a', encoding='utf-8') as f:
|
|
||||||
f.write(msg + '\n')
|
|
||||||
|
|
||||||
event_log = advapi32.RegisterEventSourceW(None, service_name)
|
|
||||||
if not event_log:
|
|
||||||
raise OSError('Could not report event: %s' % ctypes.FormatError())
|
|
||||||
|
|
||||||
try:
|
|
||||||
type_id = 0x0001 if is_error else 0x0004
|
|
||||||
event_id = 0xc0000000 if is_error else 0x40000000
|
|
||||||
lines = _ctypes_array(LPTSTR, [msg])
|
|
||||||
|
|
||||||
if not advapi32.ReportEventW(
|
|
||||||
event_log, type_id, 0, event_id, None, len(lines), 0,
|
|
||||||
lines, None):
|
|
||||||
raise OSError('Event reporting failed: %s' % ctypes.FormatError())
|
|
||||||
finally:
|
|
||||||
advapi32.DeregisterEventSource(event_log)
|
|
||||||
|
|
||||||
|
|
||||||
def win_service_handler(stop_event, *args):
|
|
||||||
try:
|
|
||||||
raise ValueError('Handler called with args ' + repr(args))
|
|
||||||
TODO
|
|
||||||
except Exception as e:
|
|
||||||
tb = traceback.format_exc()
|
|
||||||
msg = str(e) + '\n' + tb
|
|
||||||
win_service_report_event(service_name, msg, is_error=True)
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
def win_service_set_status(handle, status_code):
|
|
||||||
svcStatus = SERVICE_STATUS()
|
|
||||||
svcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS
|
|
||||||
svcStatus.dwCurrentState = status_code
|
|
||||||
svcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP
|
|
||||||
|
|
||||||
svcStatus.dwServiceSpecificExitCode = 0
|
|
||||||
|
|
||||||
if not advapi32.SetServiceStatus(handle, ctypes.byref(svcStatus)):
|
|
||||||
raise OSError('SetServiceStatus failed: %r' % ctypes.FormatError())
|
|
||||||
|
|
||||||
|
|
||||||
def win_service_main(service_name, real_main, argc, argv_raw):
|
|
||||||
try:
|
|
||||||
# args = [argv_raw[i].value for i in range(argc)]
|
|
||||||
stop_event = threading.Event()
|
|
||||||
handler = HandlerEx(functools.partial(stop_event, win_service_handler))
|
|
||||||
h = advapi32.RegisterServiceCtrlHandlerExW(service_name, handler, None)
|
|
||||||
if not h:
|
|
||||||
raise OSError('Handler registration failed: %s' %
|
|
||||||
ctypes.FormatError())
|
|
||||||
|
|
||||||
TODO
|
|
||||||
except Exception as e:
|
|
||||||
tb = traceback.format_exc()
|
|
||||||
msg = str(e) + '\n' + tb
|
|
||||||
win_service_report_event(service_name, msg, is_error=True)
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
def win_service_start(service_name, real_main):
|
|
||||||
try:
|
|
||||||
cb = START_CALLBACK(
|
|
||||||
functools.partial(win_service_main, service_name, real_main))
|
|
||||||
dispatch_table = _ctypes_array(SERVICE_TABLE_ENTRY, [
|
|
||||||
SERVICE_TABLE_ENTRY(
|
|
||||||
service_name,
|
|
||||||
cb
|
|
||||||
),
|
|
||||||
SERVICE_TABLE_ENTRY(None, ctypes.cast(None, START_CALLBACK))
|
|
||||||
])
|
|
||||||
|
|
||||||
if not advapi32.StartServiceCtrlDispatcherW(dispatch_table):
|
|
||||||
raise OSError('ctypes start failed: %s' % ctypes.FormatError())
|
|
||||||
except Exception as e:
|
|
||||||
tb = traceback.format_exc()
|
|
||||||
msg = str(e) + '\n' + tb
|
|
||||||
win_service_report_event(service_name, msg, is_error=True)
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
def main(args=None):
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument('-i', '--install',
|
|
||||||
action='store_const', dest='action', const='install',
|
|
||||||
help='Launch at Windows startup')
|
|
||||||
parser.add_argument('-u', '--uninstall',
|
|
||||||
action='store_const', dest='action', const='uninstall',
|
|
||||||
help='Remove Windows service')
|
|
||||||
parser.add_argument('-s', '--service',
|
|
||||||
action='store_const', dest='action', const='service',
|
|
||||||
help='Run as a Windows service')
|
|
||||||
parser.add_argument('-b', '--bind', metavar='<host:port>',
|
|
||||||
action='store', default='0.0.0.0:8142',
|
|
||||||
help='Bind to host:port (default %default)')
|
|
||||||
options = parser.parse_args(args=args)
|
|
||||||
|
|
||||||
if options.action == 'install':
|
|
||||||
fn = os.path.abspath(__file__).replace('v:', '\\\\vboxsrv\\vbox')
|
|
||||||
cmdline = '%s %s -s -b %s' % (sys.executable, fn, options.bind)
|
|
||||||
win_install_service(SVCNAME, cmdline)
|
|
||||||
return
|
|
||||||
|
|
||||||
if options.action == 'uninstall':
|
|
||||||
win_uninstall_service(SVCNAME)
|
|
||||||
return
|
|
||||||
|
|
||||||
if options.action == 'service':
|
|
||||||
win_service_start(SVCNAME, main)
|
|
||||||
return
|
|
||||||
|
|
||||||
host, port_str = options.bind.split(':')
|
|
||||||
port = int(port_str)
|
|
||||||
|
|
||||||
print('Listening on %s:%d' % (host, port))
|
|
||||||
srv = BuildHTTPServer((host, port), BuildHTTPRequestHandler)
|
|
||||||
thr = threading.Thread(target=srv.serve_forever)
|
|
||||||
thr.start()
|
|
||||||
compat_input('Press ENTER to shut down')
|
|
||||||
srv.shutdown()
|
|
||||||
thr.join()
|
|
||||||
|
|
||||||
|
|
||||||
def rmtree(path):
|
|
||||||
for name in os.listdir(path):
|
|
||||||
fname = os.path.join(path, name)
|
|
||||||
if os.path.isdir(fname):
|
|
||||||
rmtree(fname)
|
|
||||||
else:
|
|
||||||
os.chmod(fname, 0o666)
|
|
||||||
os.remove(fname)
|
|
||||||
os.rmdir(path)
|
|
||||||
|
|
||||||
|
|
||||||
class BuildError(Exception):
|
|
||||||
def __init__(self, output, code=500):
|
|
||||||
self.output = output
|
|
||||||
self.code = code
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.output
|
|
||||||
|
|
||||||
|
|
||||||
class HTTPError(BuildError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class PythonBuilder(object):
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
python_version = kwargs.pop('python', '3.4')
|
|
||||||
python_path = None
|
|
||||||
for node in ('Wow6432Node\\', ''):
|
|
||||||
try:
|
|
||||||
key = compat_winreg.OpenKey(
|
|
||||||
compat_winreg.HKEY_LOCAL_MACHINE,
|
|
||||||
r'SOFTWARE\%sPython\PythonCore\%s\InstallPath' % (node, python_version))
|
|
||||||
try:
|
|
||||||
python_path, _ = compat_winreg.QueryValueEx(key, '')
|
|
||||||
finally:
|
|
||||||
compat_winreg.CloseKey(key)
|
|
||||||
break
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if not python_path:
|
|
||||||
raise BuildError('No such Python version: %s' % python_version)
|
|
||||||
|
|
||||||
self.pythonPath = python_path
|
|
||||||
|
|
||||||
super(PythonBuilder, self).__init__(**kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class GITInfoBuilder(object):
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
try:
|
|
||||||
self.user, self.repoName = kwargs['path'][:2]
|
|
||||||
self.rev = kwargs.pop('rev')
|
|
||||||
except ValueError:
|
|
||||||
raise BuildError('Invalid path')
|
|
||||||
except KeyError as e:
|
|
||||||
raise BuildError('Missing mandatory parameter "%s"' % e.args[0])
|
|
||||||
|
|
||||||
path = os.path.join(os.environ['APPDATA'], 'Build archive', self.repoName, self.user)
|
|
||||||
if not os.path.exists(path):
|
|
||||||
os.makedirs(path)
|
|
||||||
self.basePath = tempfile.mkdtemp(dir=path)
|
|
||||||
self.buildPath = os.path.join(self.basePath, 'build')
|
|
||||||
|
|
||||||
super(GITInfoBuilder, self).__init__(**kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class GITBuilder(GITInfoBuilder):
|
|
||||||
def build(self):
|
|
||||||
try:
|
|
||||||
subprocess.check_output(['git', 'clone', 'git://github.com/%s/%s.git' % (self.user, self.repoName), self.buildPath])
|
|
||||||
subprocess.check_output(['git', 'checkout', self.rev], cwd=self.buildPath)
|
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
raise BuildError(e.output)
|
|
||||||
|
|
||||||
super(GITBuilder, self).build()
|
|
||||||
|
|
||||||
|
|
||||||
class YoutubeDLBuilder(object):
|
|
||||||
authorizedUsers = ['fraca7', 'phihag', 'rg3', 'FiloSottile', 'ytdl-org']
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
if self.repoName != 'yt-dlp':
|
|
||||||
raise BuildError('Invalid repository "%s"' % self.repoName)
|
|
||||||
if self.user not in self.authorizedUsers:
|
|
||||||
raise HTTPError('Unauthorized user "%s"' % self.user, 401)
|
|
||||||
|
|
||||||
super(YoutubeDLBuilder, self).__init__(**kwargs)
|
|
||||||
|
|
||||||
def build(self):
|
|
||||||
try:
|
|
||||||
proc = subprocess.Popen([os.path.join(self.pythonPath, 'python.exe'), 'setup.py', 'py2exe'], stdin=subprocess.PIPE, cwd=self.buildPath)
|
|
||||||
proc.wait()
|
|
||||||
#subprocess.check_output([os.path.join(self.pythonPath, 'python.exe'), 'setup.py', 'py2exe'],
|
|
||||||
# cwd=self.buildPath)
|
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
raise BuildError(e.output)
|
|
||||||
|
|
||||||
super(YoutubeDLBuilder, self).build()
|
|
||||||
|
|
||||||
|
|
||||||
class DownloadBuilder(object):
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
self.handler = kwargs.pop('handler')
|
|
||||||
self.srcPath = os.path.join(self.buildPath, *tuple(kwargs['path'][2:]))
|
|
||||||
self.srcPath = os.path.abspath(os.path.normpath(self.srcPath))
|
|
||||||
if not self.srcPath.startswith(self.buildPath):
|
|
||||||
raise HTTPError(self.srcPath, 401)
|
|
||||||
|
|
||||||
super(DownloadBuilder, self).__init__(**kwargs)
|
|
||||||
|
|
||||||
def build(self):
|
|
||||||
if not os.path.exists(self.srcPath):
|
|
||||||
raise HTTPError('No such file', 404)
|
|
||||||
if os.path.isdir(self.srcPath):
|
|
||||||
raise HTTPError('Is a directory: %s' % self.srcPath, 401)
|
|
||||||
|
|
||||||
self.handler.send_response(200)
|
|
||||||
self.handler.send_header('Content-Type', 'application/octet-stream')
|
|
||||||
self.handler.send_header('Content-Disposition', 'attachment; filename=%s' % os.path.split(self.srcPath)[-1])
|
|
||||||
self.handler.send_header('Content-Length', str(os.stat(self.srcPath).st_size))
|
|
||||||
self.handler.end_headers()
|
|
||||||
|
|
||||||
with open(self.srcPath, 'rb') as src:
|
|
||||||
shutil.copyfileobj(src, self.handler.wfile)
|
|
||||||
|
|
||||||
super(DownloadBuilder, self).build()
|
|
||||||
|
|
||||||
|
|
||||||
class CleanupTempDir(object):
|
|
||||||
def build(self):
|
|
||||||
try:
|
|
||||||
rmtree(self.basePath)
|
|
||||||
except Exception as e:
|
|
||||||
print('WARNING deleting "%s": %s' % (self.basePath, e))
|
|
||||||
|
|
||||||
super(CleanupTempDir, self).build()
|
|
||||||
|
|
||||||
|
|
||||||
class Null(object):
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def build(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class Builder(PythonBuilder, GITBuilder, YoutubeDLBuilder, DownloadBuilder, CleanupTempDir, Null):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class BuildHTTPRequestHandler(compat_http_server.BaseHTTPRequestHandler):
|
|
||||||
actionDict = {'build': Builder, 'download': Builder} # They're the same, no more caching.
|
|
||||||
|
|
||||||
def do_GET(self):
|
|
||||||
path = compat_urlparse.urlparse(self.path)
|
|
||||||
paramDict = dict([(key, value[0]) for key, value in compat_urlparse.parse_qs(path.query).items()])
|
|
||||||
action, _, path = path.path.strip('/').partition('/')
|
|
||||||
if path:
|
|
||||||
path = path.split('/')
|
|
||||||
if action in self.actionDict:
|
|
||||||
try:
|
|
||||||
builder = self.actionDict[action](path=path, handler=self, **paramDict)
|
|
||||||
builder.start()
|
|
||||||
try:
|
|
||||||
builder.build()
|
|
||||||
finally:
|
|
||||||
builder.close()
|
|
||||||
except BuildError as e:
|
|
||||||
self.send_response(e.code)
|
|
||||||
msg = compat_str(e).encode('UTF-8')
|
|
||||||
self.send_header('Content-Type', 'text/plain; charset=UTF-8')
|
|
||||||
self.send_header('Content-Length', len(msg))
|
|
||||||
self.end_headers()
|
|
||||||
self.wfile.write(msg)
|
|
||||||
else:
|
|
||||||
self.send_response(500, 'Unknown build method "%s"' % action)
|
|
||||||
else:
|
|
||||||
self.send_response(500, 'Malformed URL')
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -0,0 +1,102 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"action": "add",
|
||||||
|
"when": "29cb20bd563c02671b31dd840139e93dd37150a1",
|
||||||
|
"short": "[priority] **A new release type has been added!**\n * [`nightly`](https://github.com/yt-dlp/yt-dlp/releases/tag/nightly) builds will be made after each push, containing the latest fixes (but also possibly bugs).\n * When using `--update`/`-U`, a release binary will only update to its current channel (either `stable` or `nightly`).\n * The `--update-to` option has been added allowing the user more control over program upgrades (or downgrades).\n * `--update-to` can change the release channel (`stable`, `nightly`) and also upgrade or downgrade to specific tags.\n * **Usage**: `--update-to CHANNEL`, `--update-to TAG`, `--update-to CHANNEL@TAG`"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "add",
|
||||||
|
"when": "5038f6d713303e0967d002216e7a88652401c22a",
|
||||||
|
"short": "[priority] **YouTube throttling fixes!**"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "remove",
|
||||||
|
"when": "2e023649ea4e11151545a34dc1360c114981a236"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "add",
|
||||||
|
"when": "01aba2519a0884ef17d5f85608dbd2a455577147",
|
||||||
|
"short": "[priority] YouTube: Improved throttling and signature fixes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "change",
|
||||||
|
"when": "c86e433c35fe5da6cb29f3539eef97497f84ed38",
|
||||||
|
"short": "[extractor/niconico:series] Fix extraction (#6898)",
|
||||||
|
"authors": ["sqrtNOT"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "change",
|
||||||
|
"when": "69a40e4a7f6caa5662527ebd2f3c4e8aa02857a2",
|
||||||
|
"short": "[extractor/youtube:music_search_url] Extract title (#7102)",
|
||||||
|
"authors": ["kangalio"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "change",
|
||||||
|
"when": "8417f26b8a819cd7ffcd4e000ca3e45033e670fb",
|
||||||
|
"short": "Add option `--color` (#6904)",
|
||||||
|
"authors": ["Grub4K"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "change",
|
||||||
|
"when": "b4e0d75848e9447cee2cd3646ce54d4744a7ff56",
|
||||||
|
"short": "Improve `--download-sections`\n - Support negative time-ranges\n - Add `*from-url` to obey time-ranges in URL",
|
||||||
|
"authors": ["pukkandan"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "change",
|
||||||
|
"when": "1e75d97db21152acc764b30a688e516f04b8a142",
|
||||||
|
"short": "[extractor/youtube] Add `ios` to default clients used\n - IOS is affected neither by 403 nor by nsig so helps mitigate them preemptively\n - IOS also has higher bit-rate 'premium' formats though they are not labeled as such",
|
||||||
|
"authors": ["pukkandan"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "change",
|
||||||
|
"when": "f2ff0f6f1914b82d4a51681a72cc0828115dcb4a",
|
||||||
|
"short": "[extractor/motherless] Add gallery support, fix groups (#7211)",
|
||||||
|
"authors": ["rexlambert22", "Ti4eeT4e"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "change",
|
||||||
|
"when": "a4486bfc1dc7057efca9dd3fe70d7fa25c56f700",
|
||||||
|
"short": "[misc] Revert \"Add automatic duplicate issue detection\"",
|
||||||
|
"authors": ["pukkandan"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "add",
|
||||||
|
"when": "1ceb657bdd254ad961489e5060f2ccc7d556b729",
|
||||||
|
"short": "[priority] Security: [[CVE-2023-35934](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-35934)] Fix [Cookie leak](https://github.com/yt-dlp/yt-dlp/security/advisories/GHSA-v8mc-9377-rwjj)\n - `--add-header Cookie:` is deprecated and auto-scoped to input URL domains\n - Cookies are scoped when passed to external downloaders\n - Add `cookies` field to info.json and deprecate `http_headers.Cookie`"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "change",
|
||||||
|
"when": "b03fa7834579a01cc5fba48c0e73488a16683d48",
|
||||||
|
"short": "[ie/twitter] Revert 92315c03774cfabb3a921884326beb4b981f786b",
|
||||||
|
"authors": ["pukkandan"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "change",
|
||||||
|
"when": "fcd6a76adc49d5cd8783985c7ce35384b72e545f",
|
||||||
|
"short": "[test] Add tests for socks proxies (#7908)",
|
||||||
|
"authors": ["coletdjnz"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "change",
|
||||||
|
"when": "4bf912282a34b58b6b35d8f7e6be535770c89c76",
|
||||||
|
"short": "[rh:urllib] Remove dot segments during URL normalization (#7662)",
|
||||||
|
"authors": ["coletdjnz"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "change",
|
||||||
|
"when": "59e92b1f1833440bb2190f847eb735cf0f90bc85",
|
||||||
|
"short": "[rh:urllib] Simplify gzip decoding (#7611)",
|
||||||
|
"authors": ["Grub4K"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "add",
|
||||||
|
"when": "c1d71d0d9f41db5e4306c86af232f5f6220a130b",
|
||||||
|
"short": "[priority] **The minimum *recommended* Python version has been raised to 3.8**\nSince Python 3.7 has reached end-of-life, support for it will be dropped soon. [Read more](https://github.com/yt-dlp/yt-dlp/issues/7803)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"action": "add",
|
||||||
|
"when": "61bdf15fc7400601c3da1aa7a43917310a5bf391",
|
||||||
|
"short": "[priority] Security: [[CVE-2023-40581](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-40581)] [Prevent RCE when using `--exec` with `%q` on Windows](https://github.com/yt-dlp/yt-dlp/security/advisories/GHSA-42h4-v29r-42qg)\n - The shell escape function is now using `\"\"` instead of `\\\"`.\n - `utils.Popen` has been patched to properly quote commands."
|
||||||
|
}
|
||||||
|
]
|
@ -0,0 +1,96 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft/2020-12/schema",
|
||||||
|
"type": "array",
|
||||||
|
"uniqueItems": true,
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"action": {
|
||||||
|
"enum": [
|
||||||
|
"add"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"when": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^([0-9a-f]{40}|\\d{4}\\.\\d{2}\\.\\d{2})$"
|
||||||
|
},
|
||||||
|
"hash": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[0-9a-f]{40}$"
|
||||||
|
},
|
||||||
|
"short": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"authors": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"action",
|
||||||
|
"short"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"action": {
|
||||||
|
"enum": [
|
||||||
|
"remove"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"when": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^([0-9a-f]{40}|\\d{4}\\.\\d{2}\\.\\d{2})$"
|
||||||
|
},
|
||||||
|
"hash": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[0-9a-f]{40}$"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"action",
|
||||||
|
"hash"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"action": {
|
||||||
|
"enum": [
|
||||||
|
"change"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"when": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^([0-9a-f]{40}|\\d{4}\\.\\d{2}\\.\\d{2})$"
|
||||||
|
},
|
||||||
|
"hash": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[0-9a-f]{40}$"
|
||||||
|
},
|
||||||
|
"short": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"authors": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"action",
|
||||||
|
"hash",
|
||||||
|
"short",
|
||||||
|
"authors"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
# Allow direct execution
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
|
import yt_dlp
|
||||||
|
import yt_dlp.options
|
||||||
|
|
||||||
|
create_parser = yt_dlp.options.create_parser
|
||||||
|
|
||||||
|
|
||||||
|
def parse_patched_options(opts):
|
||||||
|
patched_parser = create_parser()
|
||||||
|
patched_parser.defaults.update({
|
||||||
|
'ignoreerrors': False,
|
||||||
|
'retries': 0,
|
||||||
|
'fragment_retries': 0,
|
||||||
|
'extract_flat': False,
|
||||||
|
'concat_playlist': 'never',
|
||||||
|
})
|
||||||
|
yt_dlp.options.create_parser = lambda: patched_parser
|
||||||
|
try:
|
||||||
|
return yt_dlp.parse_options(opts)
|
||||||
|
finally:
|
||||||
|
yt_dlp.options.create_parser = create_parser
|
||||||
|
|
||||||
|
|
||||||
|
default_opts = parse_patched_options([]).ydl_opts
|
||||||
|
|
||||||
|
|
||||||
|
def cli_to_api(opts, cli_defaults=False):
|
||||||
|
opts = (yt_dlp.parse_options if cli_defaults else parse_patched_options)(opts).ydl_opts
|
||||||
|
|
||||||
|
diff = {k: v for k, v in opts.items() if default_opts[k] != v}
|
||||||
|
if 'postprocessors' in diff:
|
||||||
|
diff['postprocessors'] = [pp for pp in diff['postprocessors']
|
||||||
|
if pp not in default_opts['postprocessors']]
|
||||||
|
return diff
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
print('\nThe arguments passed translate to:\n')
|
||||||
|
pprint(cli_to_api(sys.argv[1:]))
|
||||||
|
print('\nCombining these with the CLI defaults gives:\n')
|
||||||
|
pprint(cli_to_api(sys.argv[1:], True))
|
@ -1,112 +0,0 @@
|
|||||||
# Unused
|
|
||||||
|
|
||||||
#!/usr/bin/env python3
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import io
|
|
||||||
import json
|
|
||||||
import mimetypes
|
|
||||||
import netrc
|
|
||||||
import optparse
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
|
|
||||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
||||||
|
|
||||||
from yt_dlp.compat import (
|
|
||||||
compat_basestring,
|
|
||||||
compat_getpass,
|
|
||||||
compat_print,
|
|
||||||
compat_urllib_request,
|
|
||||||
)
|
|
||||||
from yt_dlp.utils import (
|
|
||||||
make_HTTPS_handler,
|
|
||||||
sanitized_Request,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class GitHubReleaser(object):
|
|
||||||
_API_URL = 'https://api.github.com/repos/ytdl-org/youtube-dl/releases'
|
|
||||||
_UPLOADS_URL = 'https://uploads.github.com/repos/ytdl-org/youtube-dl/releases/%s/assets?name=%s'
|
|
||||||
_NETRC_MACHINE = 'github.com'
|
|
||||||
|
|
||||||
def __init__(self, debuglevel=0):
|
|
||||||
self._init_github_account()
|
|
||||||
https_handler = make_HTTPS_handler({}, debuglevel=debuglevel)
|
|
||||||
self._opener = compat_urllib_request.build_opener(https_handler)
|
|
||||||
|
|
||||||
def _init_github_account(self):
|
|
||||||
try:
|
|
||||||
info = netrc.netrc().authenticators(self._NETRC_MACHINE)
|
|
||||||
if info is not None:
|
|
||||||
self._token = info[2]
|
|
||||||
compat_print('Using GitHub credentials found in .netrc...')
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
compat_print('No GitHub credentials found in .netrc')
|
|
||||||
except (IOError, netrc.NetrcParseError):
|
|
||||||
compat_print('Unable to parse .netrc')
|
|
||||||
self._token = compat_getpass(
|
|
||||||
'Type your GitHub PAT (personal access token) and press [Return]: ')
|
|
||||||
|
|
||||||
def _call(self, req):
|
|
||||||
if isinstance(req, compat_basestring):
|
|
||||||
req = sanitized_Request(req)
|
|
||||||
req.add_header('Authorization', 'token %s' % self._token)
|
|
||||||
response = self._opener.open(req).read().decode('utf-8')
|
|
||||||
return json.loads(response)
|
|
||||||
|
|
||||||
def list_releases(self):
|
|
||||||
return self._call(self._API_URL)
|
|
||||||
|
|
||||||
def create_release(self, tag_name, name=None, body='', draft=False, prerelease=False):
|
|
||||||
data = {
|
|
||||||
'tag_name': tag_name,
|
|
||||||
'target_commitish': 'master',
|
|
||||||
'name': name,
|
|
||||||
'body': body,
|
|
||||||
'draft': draft,
|
|
||||||
'prerelease': prerelease,
|
|
||||||
}
|
|
||||||
req = sanitized_Request(self._API_URL, json.dumps(data).encode('utf-8'))
|
|
||||||
return self._call(req)
|
|
||||||
|
|
||||||
def create_asset(self, release_id, asset):
|
|
||||||
asset_name = os.path.basename(asset)
|
|
||||||
url = self._UPLOADS_URL % (release_id, asset_name)
|
|
||||||
# Our files are small enough to be loaded directly into memory.
|
|
||||||
data = open(asset, 'rb').read()
|
|
||||||
req = sanitized_Request(url, data)
|
|
||||||
mime_type, _ = mimetypes.guess_type(asset_name)
|
|
||||||
req.add_header('Content-Type', mime_type or 'application/octet-stream')
|
|
||||||
return self._call(req)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
parser = optparse.OptionParser(usage='%prog CHANGELOG VERSION BUILDPATH')
|
|
||||||
options, args = parser.parse_args()
|
|
||||||
if len(args) != 3:
|
|
||||||
parser.error('Expected a version and a build directory')
|
|
||||||
|
|
||||||
changelog_file, version, build_path = args
|
|
||||||
|
|
||||||
with io.open(changelog_file, encoding='utf-8') as inf:
|
|
||||||
changelog = inf.read()
|
|
||||||
|
|
||||||
mobj = re.search(r'(?s)version %s\n{2}(.+?)\n{3}' % version, changelog)
|
|
||||||
body = mobj.group(1) if mobj else ''
|
|
||||||
|
|
||||||
releaser = GitHubReleaser()
|
|
||||||
|
|
||||||
new_release = releaser.create_release(
|
|
||||||
version, name='yt-dlp %s' % version, body=body)
|
|
||||||
release_id = new_release['id']
|
|
||||||
|
|
||||||
for asset in os.listdir(build_path):
|
|
||||||
compat_print('Uploading %s...' % asset)
|
|
||||||
releaser.create_asset(release_id, os.path.join(build_path, asset))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -1,43 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import json
|
|
||||||
import sys
|
|
||||||
import hashlib
|
|
||||||
import os.path
|
|
||||||
|
|
||||||
|
|
||||||
if len(sys.argv) <= 1:
|
|
||||||
print('Specify the version number as parameter')
|
|
||||||
sys.exit()
|
|
||||||
version = sys.argv[1]
|
|
||||||
|
|
||||||
with open('update/LATEST_VERSION', 'w') as f:
|
|
||||||
f.write(version)
|
|
||||||
|
|
||||||
versions_info = json.load(open('update/versions.json'))
|
|
||||||
if 'signature' in versions_info:
|
|
||||||
del versions_info['signature']
|
|
||||||
|
|
||||||
new_version = {}
|
|
||||||
|
|
||||||
filenames = {
|
|
||||||
'bin': 'yt-dlp',
|
|
||||||
'exe': 'yt-dlp.exe',
|
|
||||||
'tar': 'yt-dlp-%s.tar.gz' % version}
|
|
||||||
build_dir = os.path.join('..', '..', 'build', version)
|
|
||||||
for key, filename in filenames.items():
|
|
||||||
url = 'https://yt-dl.org/downloads/%s/%s' % (version, filename)
|
|
||||||
fn = os.path.join(build_dir, filename)
|
|
||||||
with open(fn, 'rb') as f:
|
|
||||||
data = f.read()
|
|
||||||
if not data:
|
|
||||||
raise ValueError('File %s is empty!' % fn)
|
|
||||||
sha256sum = hashlib.sha256(data).hexdigest()
|
|
||||||
new_version[key] = (url, sha256sum)
|
|
||||||
|
|
||||||
versions_info['versions'][version] = new_version
|
|
||||||
versions_info['latest'] = version
|
|
||||||
|
|
||||||
with open('update/versions.json', 'w') as jsonf:
|
|
||||||
json.dump(versions_info, jsonf, indent=4, sort_keys=True)
|
|
@ -1,22 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import json
|
|
||||||
|
|
||||||
versions_info = json.load(open('update/versions.json'))
|
|
||||||
version = versions_info['latest']
|
|
||||||
version_dict = versions_info['versions'][version]
|
|
||||||
|
|
||||||
# Read template page
|
|
||||||
with open('download.html.in', 'r', encoding='utf-8') as tmplf:
|
|
||||||
template = tmplf.read()
|
|
||||||
|
|
||||||
template = template.replace('@PROGRAM_VERSION@', version)
|
|
||||||
template = template.replace('@PROGRAM_URL@', version_dict['bin'][0])
|
|
||||||
template = template.replace('@PROGRAM_SHA256SUM@', version_dict['bin'][1])
|
|
||||||
template = template.replace('@EXE_URL@', version_dict['exe'][0])
|
|
||||||
template = template.replace('@EXE_SHA256SUM@', version_dict['exe'][1])
|
|
||||||
template = template.replace('@TAR_URL@', version_dict['tar'][0])
|
|
||||||
template = template.replace('@TAR_SHA256SUM@', version_dict['tar'][1])
|
|
||||||
with open('download.html', 'w', encoding='utf-8') as dlf:
|
|
||||||
dlf.write(template)
|
|
@ -1,34 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
from __future__ import unicode_literals, with_statement
|
|
||||||
|
|
||||||
import rsa
|
|
||||||
import json
|
|
||||||
from binascii import hexlify
|
|
||||||
|
|
||||||
try:
|
|
||||||
input = raw_input
|
|
||||||
except NameError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
versions_info = json.load(open('update/versions.json'))
|
|
||||||
if 'signature' in versions_info:
|
|
||||||
del versions_info['signature']
|
|
||||||
|
|
||||||
print('Enter the PKCS1 private key, followed by a blank line:')
|
|
||||||
privkey = b''
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
line = input()
|
|
||||||
except EOFError:
|
|
||||||
break
|
|
||||||
if line == '':
|
|
||||||
break
|
|
||||||
privkey += line.encode('ascii') + b'\n'
|
|
||||||
privkey = rsa.PrivateKey.load_pkcs1(privkey)
|
|
||||||
|
|
||||||
signature = hexlify(rsa.pkcs1.sign(json.dumps(versions_info, sort_keys=True).encode('utf-8'), privkey, 'SHA-256')).decode()
|
|
||||||
print('signature: ' + signature)
|
|
||||||
|
|
||||||
versions_info['signature'] = signature
|
|
||||||
with open('update/versions.json', 'w') as versionsf:
|
|
||||||
json.dump(versions_info, versionsf, indent=4, sort_keys=True)
|
|
@ -1,21 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# coding: utf-8
|
|
||||||
|
|
||||||
from __future__ import with_statement, unicode_literals
|
|
||||||
|
|
||||||
import datetime
|
|
||||||
import glob
|
|
||||||
import io # For Python 2 compatibility
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
|
|
||||||
year = str(datetime.datetime.now().year)
|
|
||||||
for fn in glob.glob('*.html*'):
|
|
||||||
with io.open(fn, encoding='utf-8') as f:
|
|
||||||
content = f.read()
|
|
||||||
newc = re.sub(r'(?P<copyright>Copyright © 2011-)(?P<year>[0-9]{4})', 'Copyright © 2011-' + year, content)
|
|
||||||
if content != newc:
|
|
||||||
tmpFn = fn + '.part'
|
|
||||||
with io.open(tmpFn, 'wt', encoding='utf-8') as outf:
|
|
||||||
outf.write(newc)
|
|
||||||
os.rename(tmpFn, fn)
|
|
@ -1,76 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import datetime
|
|
||||||
import io
|
|
||||||
import json
|
|
||||||
import textwrap
|
|
||||||
|
|
||||||
|
|
||||||
atom_template = textwrap.dedent("""\
|
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
|
||||||
<link rel="self" href="http://ytdl-org.github.io/youtube-dl/update/releases.atom" />
|
|
||||||
<title>yt-dlp releases</title>
|
|
||||||
<id>https://yt-dl.org/feed/yt-dlp-updates-feed</id>
|
|
||||||
<updated>@TIMESTAMP@</updated>
|
|
||||||
@ENTRIES@
|
|
||||||
</feed>""")
|
|
||||||
|
|
||||||
entry_template = textwrap.dedent("""
|
|
||||||
<entry>
|
|
||||||
<id>https://yt-dl.org/feed/yt-dlp-updates-feed/yt-dlp-@VERSION@</id>
|
|
||||||
<title>New version @VERSION@</title>
|
|
||||||
<link href="http://ytdl-org.github.io/yt-dlp" />
|
|
||||||
<content type="xhtml">
|
|
||||||
<div xmlns="http://www.w3.org/1999/xhtml">
|
|
||||||
Downloads available at <a href="https://yt-dl.org/downloads/@VERSION@/">https://yt-dl.org/downloads/@VERSION@/</a>
|
|
||||||
</div>
|
|
||||||
</content>
|
|
||||||
<author>
|
|
||||||
<name>The yt-dlp maintainers</name>
|
|
||||||
</author>
|
|
||||||
<updated>@TIMESTAMP@</updated>
|
|
||||||
</entry>
|
|
||||||
""")
|
|
||||||
|
|
||||||
now = datetime.datetime.now()
|
|
||||||
now_iso = now.isoformat() + 'Z'
|
|
||||||
|
|
||||||
atom_template = atom_template.replace('@TIMESTAMP@', now_iso)
|
|
||||||
|
|
||||||
versions_info = json.load(open('update/versions.json'))
|
|
||||||
versions = list(versions_info['versions'].keys())
|
|
||||||
versions.sort()
|
|
||||||
|
|
||||||
entries = []
|
|
||||||
for v in versions:
|
|
||||||
fields = v.split('.')
|
|
||||||
year, month, day = map(int, fields[:3])
|
|
||||||
faked = 0
|
|
||||||
patchlevel = 0
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
datetime.date(year, month, day)
|
|
||||||
except ValueError:
|
|
||||||
day -= 1
|
|
||||||
faked += 1
|
|
||||||
assert day > 0
|
|
||||||
continue
|
|
||||||
break
|
|
||||||
if len(fields) >= 4:
|
|
||||||
try:
|
|
||||||
patchlevel = int(fields[3])
|
|
||||||
except ValueError:
|
|
||||||
patchlevel = 1
|
|
||||||
timestamp = '%04d-%02d-%02dT00:%02d:%02dZ' % (year, month, day, faked, patchlevel)
|
|
||||||
|
|
||||||
entry = entry_template.replace('@TIMESTAMP@', timestamp)
|
|
||||||
entry = entry.replace('@VERSION@', v)
|
|
||||||
entries.append(entry)
|
|
||||||
|
|
||||||
entries_str = textwrap.indent(''.join(entries), '\t')
|
|
||||||
atom_template = atom_template.replace('@ENTRIES@', entries_str)
|
|
||||||
|
|
||||||
with io.open('update/releases.atom', 'w', encoding='utf-8') as atom_file:
|
|
||||||
atom_file.write(atom_template)
|
|
@ -1,37 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import textwrap
|
|
||||||
|
|
||||||
# We must be able to import yt_dlp
|
|
||||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
|
||||||
|
|
||||||
import yt_dlp
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
with open('supportedsites.html.in', 'r', encoding='utf-8') as tmplf:
|
|
||||||
template = tmplf.read()
|
|
||||||
|
|
||||||
ie_htmls = []
|
|
||||||
for ie in yt_dlp.list_extractors(age_limit=None):
|
|
||||||
ie_html = '<b>{}</b>'.format(ie.IE_NAME)
|
|
||||||
ie_desc = getattr(ie, 'IE_DESC', None)
|
|
||||||
if ie_desc is False:
|
|
||||||
continue
|
|
||||||
elif ie_desc is not None:
|
|
||||||
ie_html += ': {}'.format(ie.IE_DESC)
|
|
||||||
if not ie.working():
|
|
||||||
ie_html += ' (Currently broken)'
|
|
||||||
ie_htmls.append('<li>{}</li>'.format(ie_html))
|
|
||||||
|
|
||||||
template = template.replace('@SITES@', textwrap.indent('\n'.join(ie_htmls), '\t'))
|
|
||||||
|
|
||||||
with open('supportedsites.html', 'w', encoding='utf-8') as sitesf:
|
|
||||||
sitesf.write(template)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
@ -1,31 +1,39 @@
|
|||||||
# coding: utf-8
|
import importlib
|
||||||
|
import random
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from ..utils import bug_reports_message, write_string
|
from ..utils import (
|
||||||
|
age_restricted,
|
||||||
|
bug_reports_message,
|
||||||
|
classproperty,
|
||||||
|
variadic,
|
||||||
|
write_string,
|
||||||
|
)
|
||||||
|
|
||||||
|
# These bloat the lazy_extractors, so allow them to passthrough silently
|
||||||
|
ALLOWED_CLASSMETHODS = {'extract_from_webpage', 'get_testcases', 'get_webpage_testcases'}
|
||||||
|
_WARNED = False
|
||||||
|
|
||||||
|
|
||||||
class LazyLoadMetaClass(type):
|
class LazyLoadMetaClass(type):
|
||||||
def __getattr__(cls, name):
|
def __getattr__(cls, name):
|
||||||
if '_real_class' not in cls.__dict__:
|
global _WARNED
|
||||||
write_string(
|
if ('_real_class' not in cls.__dict__
|
||||||
f'WARNING: Falling back to normal extractor since lazy extractor '
|
and name not in ALLOWED_CLASSMETHODS and not _WARNED):
|
||||||
f'{cls.__name__} does not have attribute {name}{bug_reports_message()}')
|
_WARNED = True
|
||||||
return getattr(cls._get_real_class(), name)
|
write_string('WARNING: Falling back to normal extractor since lazy extractor '
|
||||||
|
f'{cls.__name__} does not have attribute {name}{bug_reports_message()}\n')
|
||||||
|
return getattr(cls.real_class, name)
|
||||||
|
|
||||||
|
|
||||||
class LazyLoadExtractor(metaclass=LazyLoadMetaClass):
|
class LazyLoadExtractor(metaclass=LazyLoadMetaClass):
|
||||||
_module = None
|
@classproperty
|
||||||
_WORKING = True
|
def real_class(cls):
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _get_real_class(cls):
|
|
||||||
if '_real_class' not in cls.__dict__:
|
if '_real_class' not in cls.__dict__:
|
||||||
mod = __import__(cls._module, fromlist=(cls.__name__,))
|
cls._real_class = getattr(importlib.import_module(cls._module), cls.__name__)
|
||||||
cls._real_class = getattr(mod, cls.__name__)
|
|
||||||
return cls._real_class
|
return cls._real_class
|
||||||
|
|
||||||
def __new__(cls, *args, **kwargs):
|
def __new__(cls, *args, **kwargs):
|
||||||
real_cls = cls._get_real_class()
|
instance = cls.real_class.__new__(cls.real_class)
|
||||||
instance = real_cls.__new__(real_cls)
|
|
||||||
instance.__init__(*args, **kwargs)
|
instance.__init__(*args, **kwargs)
|
||||||
return instance
|
return instance
|
||||||
|
@ -0,0 +1,517 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
# Allow direct execution
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
|
import enum
|
||||||
|
import itertools
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
from collections import defaultdict
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from functools import lru_cache
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from devscripts.utils import read_file, run_process, write_file
|
||||||
|
|
||||||
|
BASE_URL = 'https://github.com'
|
||||||
|
LOCATION_PATH = Path(__file__).parent
|
||||||
|
HASH_LENGTH = 7
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class CommitGroup(enum.Enum):
|
||||||
|
PRIORITY = 'Important'
|
||||||
|
CORE = 'Core'
|
||||||
|
EXTRACTOR = 'Extractor'
|
||||||
|
DOWNLOADER = 'Downloader'
|
||||||
|
POSTPROCESSOR = 'Postprocessor'
|
||||||
|
NETWORKING = 'Networking'
|
||||||
|
MISC = 'Misc.'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@lru_cache
|
||||||
|
def subgroup_lookup(cls):
|
||||||
|
return {
|
||||||
|
name: group
|
||||||
|
for group, names in {
|
||||||
|
cls.CORE: {
|
||||||
|
'aes',
|
||||||
|
'cache',
|
||||||
|
'compat_utils',
|
||||||
|
'compat',
|
||||||
|
'cookies',
|
||||||
|
'dependencies',
|
||||||
|
'formats',
|
||||||
|
'jsinterp',
|
||||||
|
'outtmpl',
|
||||||
|
'plugins',
|
||||||
|
'update',
|
||||||
|
'utils',
|
||||||
|
},
|
||||||
|
cls.MISC: {
|
||||||
|
'build',
|
||||||
|
'ci',
|
||||||
|
'cleanup',
|
||||||
|
'devscripts',
|
||||||
|
'docs',
|
||||||
|
'test',
|
||||||
|
},
|
||||||
|
cls.NETWORKING: {
|
||||||
|
'rh',
|
||||||
|
},
|
||||||
|
}.items()
|
||||||
|
for name in names
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@lru_cache
|
||||||
|
def group_lookup(cls):
|
||||||
|
result = {
|
||||||
|
'fd': cls.DOWNLOADER,
|
||||||
|
'ie': cls.EXTRACTOR,
|
||||||
|
'pp': cls.POSTPROCESSOR,
|
||||||
|
'upstream': cls.CORE,
|
||||||
|
}
|
||||||
|
result.update({item.name.lower(): item for item in iter(cls)})
|
||||||
|
return result
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get(cls, value: str) -> tuple[CommitGroup | None, str | None]:
|
||||||
|
group, _, subgroup = (group.strip().lower() for group in value.partition('/'))
|
||||||
|
|
||||||
|
result = cls.group_lookup().get(group)
|
||||||
|
if not result:
|
||||||
|
if subgroup:
|
||||||
|
return None, value
|
||||||
|
subgroup = group
|
||||||
|
result = cls.subgroup_lookup().get(subgroup)
|
||||||
|
|
||||||
|
return result, subgroup or None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Commit:
|
||||||
|
hash: str | None
|
||||||
|
short: str
|
||||||
|
authors: list[str]
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
result = f'{self.short!r}'
|
||||||
|
|
||||||
|
if self.hash:
|
||||||
|
result += f' ({self.hash[:HASH_LENGTH]})'
|
||||||
|
|
||||||
|
if self.authors:
|
||||||
|
authors = ', '.join(self.authors)
|
||||||
|
result += f' by {authors}'
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class CommitInfo:
|
||||||
|
details: str | None
|
||||||
|
sub_details: tuple[str, ...]
|
||||||
|
message: str
|
||||||
|
issues: list[str]
|
||||||
|
commit: Commit
|
||||||
|
fixes: list[Commit]
|
||||||
|
|
||||||
|
def key(self):
|
||||||
|
return ((self.details or '').lower(), self.sub_details, self.message)
|
||||||
|
|
||||||
|
|
||||||
|
def unique(items):
|
||||||
|
return sorted({item.strip().lower(): item for item in items if item}.values())
|
||||||
|
|
||||||
|
|
||||||
|
class Changelog:
|
||||||
|
MISC_RE = re.compile(r'(?:^|\b)(?:lint(?:ing)?|misc|format(?:ting)?|fixes)(?:\b|$)', re.IGNORECASE)
|
||||||
|
ALWAYS_SHOWN = (CommitGroup.PRIORITY,)
|
||||||
|
|
||||||
|
def __init__(self, groups, repo, collapsible=False):
|
||||||
|
self._groups = groups
|
||||||
|
self._repo = repo
|
||||||
|
self._collapsible = collapsible
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '\n'.join(self._format_groups(self._groups)).replace('\t', ' ')
|
||||||
|
|
||||||
|
def _format_groups(self, groups):
|
||||||
|
first = True
|
||||||
|
for item in CommitGroup:
|
||||||
|
if self._collapsible and item not in self.ALWAYS_SHOWN and first:
|
||||||
|
first = False
|
||||||
|
yield '\n<details><summary><h3>Changelog</h3></summary>\n'
|
||||||
|
|
||||||
|
group = groups[item]
|
||||||
|
if group:
|
||||||
|
yield self.format_module(item.value, group)
|
||||||
|
|
||||||
|
if self._collapsible:
|
||||||
|
yield '\n</details>'
|
||||||
|
|
||||||
|
def format_module(self, name, group):
|
||||||
|
result = f'\n#### {name} changes\n' if name else '\n'
|
||||||
|
return result + '\n'.join(self._format_group(group))
|
||||||
|
|
||||||
|
def _format_group(self, group):
|
||||||
|
sorted_group = sorted(group, key=CommitInfo.key)
|
||||||
|
detail_groups = itertools.groupby(sorted_group, lambda item: (item.details or '').lower())
|
||||||
|
for _, items in detail_groups:
|
||||||
|
items = list(items)
|
||||||
|
details = items[0].details
|
||||||
|
|
||||||
|
if details == 'cleanup':
|
||||||
|
items = self._prepare_cleanup_misc_items(items)
|
||||||
|
|
||||||
|
prefix = '-'
|
||||||
|
if details:
|
||||||
|
if len(items) == 1:
|
||||||
|
prefix = f'- **{details}**:'
|
||||||
|
else:
|
||||||
|
yield f'- **{details}**'
|
||||||
|
prefix = '\t-'
|
||||||
|
|
||||||
|
sub_detail_groups = itertools.groupby(items, lambda item: tuple(map(str.lower, item.sub_details)))
|
||||||
|
for sub_details, entries in sub_detail_groups:
|
||||||
|
if not sub_details:
|
||||||
|
for entry in entries:
|
||||||
|
yield f'{prefix} {self.format_single_change(entry)}'
|
||||||
|
continue
|
||||||
|
|
||||||
|
entries = list(entries)
|
||||||
|
sub_prefix = f'{prefix} {", ".join(entries[0].sub_details)}'
|
||||||
|
if len(entries) == 1:
|
||||||
|
yield f'{sub_prefix}: {self.format_single_change(entries[0])}'
|
||||||
|
continue
|
||||||
|
|
||||||
|
yield sub_prefix
|
||||||
|
for entry in entries:
|
||||||
|
yield f'\t{prefix} {self.format_single_change(entry)}'
|
||||||
|
|
||||||
|
def _prepare_cleanup_misc_items(self, items):
|
||||||
|
cleanup_misc_items = defaultdict(list)
|
||||||
|
sorted_items = []
|
||||||
|
for item in items:
|
||||||
|
if self.MISC_RE.search(item.message):
|
||||||
|
cleanup_misc_items[tuple(item.commit.authors)].append(item)
|
||||||
|
else:
|
||||||
|
sorted_items.append(item)
|
||||||
|
|
||||||
|
for commit_infos in cleanup_misc_items.values():
|
||||||
|
sorted_items.append(CommitInfo(
|
||||||
|
'cleanup', ('Miscellaneous',), ', '.join(
|
||||||
|
self._format_message_link(None, info.commit.hash)
|
||||||
|
for info in sorted(commit_infos, key=lambda item: item.commit.hash or '')),
|
||||||
|
[], Commit(None, '', commit_infos[0].commit.authors), []))
|
||||||
|
|
||||||
|
return sorted_items
|
||||||
|
|
||||||
|
def format_single_change(self, info: CommitInfo):
|
||||||
|
message, sep, rest = info.message.partition('\n')
|
||||||
|
if '[' not in message:
|
||||||
|
# If the message doesn't already contain markdown links, try to add a link to the commit
|
||||||
|
message = self._format_message_link(message, info.commit.hash)
|
||||||
|
|
||||||
|
if info.issues:
|
||||||
|
message = f'{message} ({self._format_issues(info.issues)})'
|
||||||
|
|
||||||
|
if info.commit.authors:
|
||||||
|
message = f'{message} by {self._format_authors(info.commit.authors)}'
|
||||||
|
|
||||||
|
if info.fixes:
|
||||||
|
fix_message = ', '.join(f'{self._format_message_link(None, fix.hash)}' for fix in info.fixes)
|
||||||
|
|
||||||
|
authors = sorted({author for fix in info.fixes for author in fix.authors}, key=str.casefold)
|
||||||
|
if authors != info.commit.authors:
|
||||||
|
fix_message = f'{fix_message} by {self._format_authors(authors)}'
|
||||||
|
|
||||||
|
message = f'{message} (With fixes in {fix_message})'
|
||||||
|
|
||||||
|
return message if not sep else f'{message}{sep}{rest}'
|
||||||
|
|
||||||
|
def _format_message_link(self, message, hash):
|
||||||
|
assert message or hash, 'Improperly defined commit message or override'
|
||||||
|
message = message if message else hash[:HASH_LENGTH]
|
||||||
|
return f'[{message}]({self.repo_url}/commit/{hash})' if hash else message
|
||||||
|
|
||||||
|
def _format_issues(self, issues):
|
||||||
|
return ', '.join(f'[#{issue}]({self.repo_url}/issues/{issue})' for issue in issues)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _format_authors(authors):
|
||||||
|
return ', '.join(f'[{author}]({BASE_URL}/{author})' for author in authors)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def repo_url(self):
|
||||||
|
return f'{BASE_URL}/{self._repo}'
|
||||||
|
|
||||||
|
|
||||||
|
class CommitRange:
|
||||||
|
COMMAND = 'git'
|
||||||
|
COMMIT_SEPARATOR = '-----'
|
||||||
|
|
||||||
|
AUTHOR_INDICATOR_RE = re.compile(r'Authored by:? ', re.IGNORECASE)
|
||||||
|
MESSAGE_RE = re.compile(r'''
|
||||||
|
(?:\[(?P<prefix>[^\]]+)\]\ )?
|
||||||
|
(?:(?P<sub_details>`?[\w.-]+`?): )?
|
||||||
|
(?P<message>.+?)
|
||||||
|
(?:\ \((?P<issues>\#\d+(?:,\ \#\d+)*)\))?
|
||||||
|
''', re.VERBOSE | re.DOTALL)
|
||||||
|
EXTRACTOR_INDICATOR_RE = re.compile(r'(?:Fix|Add)\s+Extractors?', re.IGNORECASE)
|
||||||
|
REVERT_RE = re.compile(r'(?:\[[^\]]+\]\s+)?(?i:Revert)\s+([\da-f]{40})')
|
||||||
|
FIXES_RE = re.compile(r'(?i:Fix(?:es)?(?:\s+bugs?)?(?:\s+in|\s+for)?|Revert)\s+([\da-f]{40})')
|
||||||
|
UPSTREAM_MERGE_RE = re.compile(r'Update to ytdl-commit-([\da-f]+)')
|
||||||
|
|
||||||
|
def __init__(self, start, end, default_author=None):
|
||||||
|
self._start, self._end = start, end
|
||||||
|
self._commits, self._fixes = self._get_commits_and_fixes(default_author)
|
||||||
|
self._commits_added = []
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(itertools.chain(self._commits.values(), self._commits_added))
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._commits) + len(self._commits_added)
|
||||||
|
|
||||||
|
def __contains__(self, commit):
|
||||||
|
if isinstance(commit, Commit):
|
||||||
|
if not commit.hash:
|
||||||
|
return False
|
||||||
|
commit = commit.hash
|
||||||
|
|
||||||
|
return commit in self._commits
|
||||||
|
|
||||||
|
def _get_commits_and_fixes(self, default_author):
|
||||||
|
result = run_process(
|
||||||
|
self.COMMAND, 'log', f'--format=%H%n%s%n%b%n{self.COMMIT_SEPARATOR}',
|
||||||
|
f'{self._start}..{self._end}' if self._start else self._end).stdout
|
||||||
|
|
||||||
|
commits, reverts = {}, {}
|
||||||
|
fixes = defaultdict(list)
|
||||||
|
lines = iter(result.splitlines(False))
|
||||||
|
for i, commit_hash in enumerate(lines):
|
||||||
|
short = next(lines)
|
||||||
|
skip = short.startswith('Release ') or short == '[version] update'
|
||||||
|
|
||||||
|
authors = [default_author] if default_author else []
|
||||||
|
for line in iter(lambda: next(lines), self.COMMIT_SEPARATOR):
|
||||||
|
match = self.AUTHOR_INDICATOR_RE.match(line)
|
||||||
|
if match:
|
||||||
|
authors = sorted(map(str.strip, line[match.end():].split(',')), key=str.casefold)
|
||||||
|
|
||||||
|
commit = Commit(commit_hash, short, authors)
|
||||||
|
if skip and (self._start or not i):
|
||||||
|
logger.debug(f'Skipped commit: {commit}')
|
||||||
|
continue
|
||||||
|
elif skip:
|
||||||
|
logger.debug(f'Reached Release commit, breaking: {commit}')
|
||||||
|
break
|
||||||
|
|
||||||
|
revert_match = self.REVERT_RE.fullmatch(commit.short)
|
||||||
|
if revert_match:
|
||||||
|
reverts[revert_match.group(1)] = commit
|
||||||
|
continue
|
||||||
|
|
||||||
|
fix_match = self.FIXES_RE.search(commit.short)
|
||||||
|
if fix_match:
|
||||||
|
commitish = fix_match.group(1)
|
||||||
|
fixes[commitish].append(commit)
|
||||||
|
|
||||||
|
commits[commit.hash] = commit
|
||||||
|
|
||||||
|
for commitish, revert_commit in reverts.items():
|
||||||
|
reverted = commits.pop(commitish, None)
|
||||||
|
if reverted:
|
||||||
|
logger.debug(f'{commitish} fully reverted {reverted}')
|
||||||
|
else:
|
||||||
|
commits[revert_commit.hash] = revert_commit
|
||||||
|
|
||||||
|
for commitish, fix_commits in fixes.items():
|
||||||
|
if commitish in commits:
|
||||||
|
hashes = ', '.join(commit.hash[:HASH_LENGTH] for commit in fix_commits)
|
||||||
|
logger.info(f'Found fix(es) for {commitish[:HASH_LENGTH]}: {hashes}')
|
||||||
|
for fix_commit in fix_commits:
|
||||||
|
del commits[fix_commit.hash]
|
||||||
|
else:
|
||||||
|
logger.debug(f'Commit with fixes not in changes: {commitish[:HASH_LENGTH]}')
|
||||||
|
|
||||||
|
return commits, fixes
|
||||||
|
|
||||||
|
def apply_overrides(self, overrides):
|
||||||
|
for override in overrides:
|
||||||
|
when = override.get('when')
|
||||||
|
if when and when not in self and when != self._start:
|
||||||
|
logger.debug(f'Ignored {when!r} override')
|
||||||
|
continue
|
||||||
|
|
||||||
|
override_hash = override.get('hash') or when
|
||||||
|
if override['action'] == 'add':
|
||||||
|
commit = Commit(override.get('hash'), override['short'], override.get('authors') or [])
|
||||||
|
logger.info(f'ADD {commit}')
|
||||||
|
self._commits_added.append(commit)
|
||||||
|
|
||||||
|
elif override['action'] == 'remove':
|
||||||
|
if override_hash in self._commits:
|
||||||
|
logger.info(f'REMOVE {self._commits[override_hash]}')
|
||||||
|
del self._commits[override_hash]
|
||||||
|
|
||||||
|
elif override['action'] == 'change':
|
||||||
|
if override_hash not in self._commits:
|
||||||
|
continue
|
||||||
|
commit = Commit(override_hash, override['short'], override.get('authors') or [])
|
||||||
|
logger.info(f'CHANGE {self._commits[commit.hash]} -> {commit}')
|
||||||
|
self._commits[commit.hash] = commit
|
||||||
|
|
||||||
|
self._commits = {key: value for key, value in reversed(self._commits.items())}
|
||||||
|
|
||||||
|
def groups(self):
|
||||||
|
group_dict = defaultdict(list)
|
||||||
|
for commit in self:
|
||||||
|
upstream_re = self.UPSTREAM_MERGE_RE.search(commit.short)
|
||||||
|
if upstream_re:
|
||||||
|
commit.short = f'[upstream] Merged with youtube-dl {upstream_re.group(1)}'
|
||||||
|
|
||||||
|
match = self.MESSAGE_RE.fullmatch(commit.short)
|
||||||
|
if not match:
|
||||||
|
logger.error(f'Error parsing short commit message: {commit.short!r}')
|
||||||
|
continue
|
||||||
|
|
||||||
|
prefix, sub_details_alt, message, issues = match.groups()
|
||||||
|
issues = [issue.strip()[1:] for issue in issues.split(',')] if issues else []
|
||||||
|
|
||||||
|
if prefix:
|
||||||
|
groups, details, sub_details = zip(*map(self.details_from_prefix, prefix.split(',')))
|
||||||
|
group = next(iter(filter(None, groups)), None)
|
||||||
|
details = ', '.join(unique(details))
|
||||||
|
sub_details = list(itertools.chain.from_iterable(sub_details))
|
||||||
|
else:
|
||||||
|
group = CommitGroup.CORE
|
||||||
|
details = None
|
||||||
|
sub_details = []
|
||||||
|
|
||||||
|
if sub_details_alt:
|
||||||
|
sub_details.append(sub_details_alt)
|
||||||
|
sub_details = tuple(unique(sub_details))
|
||||||
|
|
||||||
|
if not group:
|
||||||
|
if self.EXTRACTOR_INDICATOR_RE.search(commit.short):
|
||||||
|
group = CommitGroup.EXTRACTOR
|
||||||
|
else:
|
||||||
|
group = CommitGroup.POSTPROCESSOR
|
||||||
|
logger.warning(f'Failed to map {commit.short!r}, selected {group.name.lower()}')
|
||||||
|
|
||||||
|
commit_info = CommitInfo(
|
||||||
|
details, sub_details, message.strip(),
|
||||||
|
issues, commit, self._fixes[commit.hash])
|
||||||
|
|
||||||
|
logger.debug(f'Resolved {commit.short!r} to {commit_info!r}')
|
||||||
|
group_dict[group].append(commit_info)
|
||||||
|
|
||||||
|
return group_dict
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def details_from_prefix(prefix):
|
||||||
|
if not prefix:
|
||||||
|
return CommitGroup.CORE, None, ()
|
||||||
|
|
||||||
|
prefix, *sub_details = prefix.split(':')
|
||||||
|
|
||||||
|
group, details = CommitGroup.get(prefix)
|
||||||
|
if group is CommitGroup.PRIORITY and details:
|
||||||
|
details = details.partition('/')[2].strip()
|
||||||
|
|
||||||
|
if details and '/' in details:
|
||||||
|
logger.error(f'Prefix is overnested, using first part: {prefix}')
|
||||||
|
details = details.partition('/')[0].strip()
|
||||||
|
|
||||||
|
if details == 'common':
|
||||||
|
details = None
|
||||||
|
elif group is CommitGroup.NETWORKING and details == 'rh':
|
||||||
|
details = 'Request Handler'
|
||||||
|
|
||||||
|
return group, details, sub_details
|
||||||
|
|
||||||
|
|
||||||
|
def get_new_contributors(contributors_path, commits):
|
||||||
|
contributors = set()
|
||||||
|
if contributors_path.exists():
|
||||||
|
for line in read_file(contributors_path).splitlines():
|
||||||
|
author, _, _ = line.strip().partition(' (')
|
||||||
|
authors = author.split('/')
|
||||||
|
contributors.update(map(str.casefold, authors))
|
||||||
|
|
||||||
|
new_contributors = set()
|
||||||
|
for commit in commits:
|
||||||
|
for author in commit.authors:
|
||||||
|
author_folded = author.casefold()
|
||||||
|
if author_folded not in contributors:
|
||||||
|
contributors.add(author_folded)
|
||||||
|
new_contributors.add(author)
|
||||||
|
|
||||||
|
return sorted(new_contributors, key=str.casefold)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='Create a changelog markdown from a git commit range')
|
||||||
|
parser.add_argument(
|
||||||
|
'commitish', default='HEAD', nargs='?',
|
||||||
|
help='The commitish to create the range from (default: %(default)s)')
|
||||||
|
parser.add_argument(
|
||||||
|
'-v', '--verbosity', action='count', default=0,
|
||||||
|
help='increase verbosity (can be used twice)')
|
||||||
|
parser.add_argument(
|
||||||
|
'-c', '--contributors', action='store_true',
|
||||||
|
help='update CONTRIBUTORS file (default: %(default)s)')
|
||||||
|
parser.add_argument(
|
||||||
|
'--contributors-path', type=Path, default=LOCATION_PATH.parent / 'CONTRIBUTORS',
|
||||||
|
help='path to the CONTRIBUTORS file')
|
||||||
|
parser.add_argument(
|
||||||
|
'--no-override', action='store_true',
|
||||||
|
help='skip override json in commit generation (default: %(default)s)')
|
||||||
|
parser.add_argument(
|
||||||
|
'--override-path', type=Path, default=LOCATION_PATH / 'changelog_override.json',
|
||||||
|
help='path to the changelog_override.json file')
|
||||||
|
parser.add_argument(
|
||||||
|
'--default-author', default='pukkandan',
|
||||||
|
help='the author to use without a author indicator (default: %(default)s)')
|
||||||
|
parser.add_argument(
|
||||||
|
'--repo', default='yt-dlp/yt-dlp',
|
||||||
|
help='the github repository to use for the operations (default: %(default)s)')
|
||||||
|
parser.add_argument(
|
||||||
|
'--collapsible', action='store_true',
|
||||||
|
help='make changelog collapsible (default: %(default)s)')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
logging.basicConfig(
|
||||||
|
datefmt='%Y-%m-%d %H-%M-%S', format='{asctime} | {levelname:<8} | {message}',
|
||||||
|
level=logging.WARNING - 10 * args.verbosity, style='{', stream=sys.stderr)
|
||||||
|
|
||||||
|
commits = CommitRange(None, args.commitish, args.default_author)
|
||||||
|
|
||||||
|
if not args.no_override:
|
||||||
|
if args.override_path.exists():
|
||||||
|
overrides = json.loads(read_file(args.override_path))
|
||||||
|
commits.apply_overrides(overrides)
|
||||||
|
else:
|
||||||
|
logger.warning(f'File {args.override_path.as_posix()} does not exist')
|
||||||
|
|
||||||
|
logger.info(f'Loaded {len(commits)} commits')
|
||||||
|
|
||||||
|
new_contributors = get_new_contributors(args.contributors_path, commits)
|
||||||
|
if new_contributors:
|
||||||
|
if args.contributors:
|
||||||
|
write_file(args.contributors_path, '\n'.join(new_contributors) + '\n', mode='a')
|
||||||
|
logger.info(f'New contributors: {", ".join(new_contributors)}')
|
||||||
|
|
||||||
|
print(Changelog(commits.groups(), args.repo, args.collapsible))
|
@ -1,33 +1,32 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
# import io
|
|
||||||
import optparse
|
import optparse
|
||||||
# import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
return # This is unused in yt-dlp
|
||||||
|
|
||||||
parser = optparse.OptionParser(usage='%prog INFILE OUTFILE')
|
parser = optparse.OptionParser(usage='%prog INFILE OUTFILE')
|
||||||
options, args = parser.parse_args()
|
options, args = parser.parse_args()
|
||||||
if len(args) != 2:
|
if len(args) != 2:
|
||||||
parser.error('Expected an input and an output filename')
|
parser.error('Expected an input and an output filename')
|
||||||
|
|
||||||
|
infile, outfile = args
|
||||||
|
|
||||||
""" infile, outfile = args
|
with open(infile, encoding='utf-8') as inf:
|
||||||
|
|
||||||
with io.open(infile, encoding='utf-8') as inf:
|
|
||||||
readme = inf.read()
|
readme = inf.read()
|
||||||
|
|
||||||
bug_text = re.search( """
|
bug_text = re.search(
|
||||||
# r'(?s)#\s*BUGS\s*[^\n]*\s*(.*?)#\s*COPYRIGHT', readme).group(1)
|
r'(?s)#\s*BUGS\s*[^\n]*\s*(.*?)#\s*COPYRIGHT', readme).group(1)
|
||||||
# dev_text = re.search(
|
dev_text = re.search(
|
||||||
# r'(?s)(#\s*DEVELOPER INSTRUCTIONS.*?)#\s*EMBEDDING yt-dlp',
|
r'(?s)(#\s*DEVELOPER INSTRUCTIONS.*?)#\s*EMBEDDING yt-dlp', readme).group(1)
|
||||||
""" readme).group(1)
|
|
||||||
|
|
||||||
out = bug_text + dev_text
|
out = bug_text + dev_text
|
||||||
|
|
||||||
with io.open(outfile, 'w', encoding='utf-8') as outf:
|
with open(outfile, 'w', encoding='utf-8') as outf:
|
||||||
outf.write(out) """
|
outf.write(out)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
@ -1,29 +1,78 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import io
|
# Allow direct execution
|
||||||
import optparse
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
def main():
|
|
||||||
parser = optparse.OptionParser(usage='%prog INFILE OUTFILE')
|
|
||||||
options, args = parser.parse_args()
|
|
||||||
if len(args) != 2:
|
|
||||||
parser.error('Expected an input and an output filename')
|
|
||||||
|
|
||||||
infile, outfile = args
|
import re
|
||||||
|
|
||||||
|
from devscripts.utils import (
|
||||||
|
get_filename_args,
|
||||||
|
read_file,
|
||||||
|
read_version,
|
||||||
|
write_file,
|
||||||
|
)
|
||||||
|
|
||||||
|
VERBOSE_TMPL = '''
|
||||||
|
- type: checkboxes
|
||||||
|
id: verbose
|
||||||
|
attributes:
|
||||||
|
label: Provide verbose output that clearly demonstrates the problem
|
||||||
|
options:
|
||||||
|
- label: Run **your** yt-dlp command with **-vU** flag added (`yt-dlp -vU <your command line>`)
|
||||||
|
required: true
|
||||||
|
- label: "If using API, add `'verbose': True` to `YoutubeDL` params instead"
|
||||||
|
required: false
|
||||||
|
- label: Copy the WHOLE output (starting with `[debug] Command-line config`) and insert it below
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: log
|
||||||
|
attributes:
|
||||||
|
label: Complete Verbose Output
|
||||||
|
description: |
|
||||||
|
It should start like this:
|
||||||
|
placeholder: |
|
||||||
|
[debug] Command-line config: ['-vU', 'test:youtube']
|
||||||
|
[debug] Portable config "yt-dlp.conf": ['-i']
|
||||||
|
[debug] Encodings: locale cp65001, fs utf-8, pref cp65001, out utf-8, error utf-8, screen utf-8
|
||||||
|
[debug] yt-dlp version %(version)s [9d339c4] (win32_exe)
|
||||||
|
[debug] Python 3.8.10 (CPython 64bit) - Windows-10-10.0.22000-SP0
|
||||||
|
[debug] Checking exe version: ffmpeg -bsfs
|
||||||
|
[debug] Checking exe version: ffprobe -bsfs
|
||||||
|
[debug] exe versions: ffmpeg N-106550-g072101bd52-20220410 (fdk,setts), ffprobe N-106624-g391ce570c8-20220415, phantomjs 2.1.1
|
||||||
|
[debug] Optional libraries: Cryptodome-3.15.0, brotli-1.0.9, certifi-2022.06.15, mutagen-1.45.1, sqlite3-2.6.0, websockets-10.3
|
||||||
|
[debug] Proxy map: {}
|
||||||
|
[debug] Fetching release info: https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest
|
||||||
|
Latest version: %(version)s, Current version: %(version)s
|
||||||
|
yt-dlp is up to date (%(version)s)
|
||||||
|
<more lines>
|
||||||
|
render: shell
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
'''.strip()
|
||||||
|
|
||||||
with io.open(infile, encoding='utf-8') as inf:
|
NO_SKIP = '''
|
||||||
issue_template_tmpl = inf.read()
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: DO NOT REMOVE OR SKIP THE ISSUE TEMPLATE
|
||||||
|
description: Fill all fields even if you think it is irrelevant for the issue
|
||||||
|
options:
|
||||||
|
- label: I understand that I will be **blocked** if I *intentionally* remove or skip any mandatory\\* field
|
||||||
|
required: true
|
||||||
|
'''.strip()
|
||||||
|
|
||||||
# Get the version from yt_dlp/version.py without importing the package
|
|
||||||
exec(compile(open('yt_dlp/version.py').read(),
|
|
||||||
'yt_dlp/version.py', 'exec'))
|
|
||||||
|
|
||||||
out = issue_template_tmpl % {'version': locals()['__version__']}
|
def main():
|
||||||
|
fields = {'version': read_version(), 'no_skip': NO_SKIP}
|
||||||
|
fields['verbose'] = VERBOSE_TMPL % fields
|
||||||
|
fields['verbose_optional'] = re.sub(r'(\n\s+validations:)?\n\s+required: true', '', fields['verbose'])
|
||||||
|
|
||||||
|
infile, outfile = get_filename_args(has_infile=True)
|
||||||
|
write_file(outfile, read_file(infile) % fields)
|
||||||
|
|
||||||
with io.open(outfile, 'w', encoding='utf-8') as outf:
|
|
||||||
outf.write(out)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
@ -1,112 +1,132 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from __future__ import unicode_literals, print_function
|
|
||||||
|
|
||||||
from inspect import getsource
|
# Allow direct execution
|
||||||
import io
|
|
||||||
import os
|
import os
|
||||||
from os.path import dirname as dirn
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
sys.path.insert(0, dirn(dirn((os.path.abspath(__file__)))))
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
lazy_extractors_filename = sys.argv[1]
|
|
||||||
if os.path.exists(lazy_extractors_filename):
|
|
||||||
os.remove(lazy_extractors_filename)
|
|
||||||
|
|
||||||
# Block plugins from loading
|
|
||||||
plugins_dirname = 'ytdlp_plugins'
|
|
||||||
plugins_blocked_dirname = 'ytdlp_plugins_blocked'
|
|
||||||
if os.path.exists(plugins_dirname):
|
|
||||||
os.rename(plugins_dirname, plugins_blocked_dirname)
|
|
||||||
|
|
||||||
from yt_dlp.extractor import _ALL_CLASSES
|
|
||||||
from yt_dlp.extractor.common import InfoExtractor, SearchInfoExtractor
|
|
||||||
|
|
||||||
if os.path.exists(plugins_blocked_dirname):
|
|
||||||
os.rename(plugins_blocked_dirname, plugins_dirname)
|
|
||||||
|
|
||||||
with open('devscripts/lazy_load_template.py', 'rt') as f:
|
|
||||||
module_template = f.read()
|
|
||||||
|
|
||||||
CLASS_PROPERTIES = ['ie_key', 'working', '_match_valid_url', 'suitable', '_match_id', 'get_temp_id']
|
from inspect import getsource
|
||||||
module_contents = [
|
|
||||||
module_template,
|
|
||||||
*[getsource(getattr(InfoExtractor, k)) for k in CLASS_PROPERTIES],
|
|
||||||
'\nclass LazyLoadSearchExtractor(LazyLoadExtractor):\n pass\n']
|
|
||||||
|
|
||||||
ie_template = '''
|
from devscripts.utils import get_filename_args, read_file, write_file
|
||||||
|
|
||||||
|
NO_ATTR = object()
|
||||||
|
STATIC_CLASS_PROPERTIES = [
|
||||||
|
'IE_NAME', '_ENABLED', '_VALID_URL', # Used for URL matching
|
||||||
|
'_WORKING', 'IE_DESC', '_NETRC_MACHINE', 'SEARCH_KEY', # Used for --extractor-descriptions
|
||||||
|
'age_limit', # Used for --age-limit (evaluated)
|
||||||
|
'_RETURN_TYPE', # Accessed in CLI only with instance (evaluated)
|
||||||
|
]
|
||||||
|
CLASS_METHODS = [
|
||||||
|
'ie_key', 'suitable', '_match_valid_url', # Used for URL matching
|
||||||
|
'working', 'get_temp_id', '_match_id', # Accessed just before instance creation
|
||||||
|
'description', # Used for --extractor-descriptions
|
||||||
|
'is_suitable', # Used for --age-limit
|
||||||
|
'supports_login', 'is_single_video', # Accessed in CLI only with instance
|
||||||
|
]
|
||||||
|
IE_TEMPLATE = '''
|
||||||
class {name}({bases}):
|
class {name}({bases}):
|
||||||
_module = '{module}'
|
_module = {module!r}
|
||||||
'''
|
|
||||||
|
|
||||||
make_valid_template = '''
|
|
||||||
@classmethod
|
|
||||||
def _make_valid_url(cls):
|
|
||||||
return {valid_url!r}
|
|
||||||
'''
|
'''
|
||||||
|
MODULE_TEMPLATE = read_file('devscripts/lazy_load_template.py')
|
||||||
|
|
||||||
def get_base_name(base):
|
|
||||||
if base is InfoExtractor:
|
def main():
|
||||||
return 'LazyLoadExtractor'
|
lazy_extractors_filename = get_filename_args(default_outfile='yt_dlp/extractor/lazy_extractors.py')
|
||||||
elif base is SearchInfoExtractor:
|
if os.path.exists(lazy_extractors_filename):
|
||||||
return 'LazyLoadSearchExtractor'
|
os.remove(lazy_extractors_filename)
|
||||||
else:
|
|
||||||
return base.__name__
|
_ALL_CLASSES = get_all_ies() # Must be before import
|
||||||
|
|
||||||
|
import yt_dlp.plugins
|
||||||
def build_lazy_ie(ie, name):
|
from yt_dlp.extractor.common import InfoExtractor, SearchInfoExtractor
|
||||||
s = ie_template.format(
|
|
||||||
name=name,
|
# Filter out plugins
|
||||||
bases=', '.join(map(get_base_name, ie.__bases__)),
|
_ALL_CLASSES = [cls for cls in _ALL_CLASSES if not cls.__module__.startswith(f'{yt_dlp.plugins.PACKAGE_NAME}.')]
|
||||||
module=ie.__module__)
|
|
||||||
valid_url = getattr(ie, '_VALID_URL', None)
|
DummyInfoExtractor = type('InfoExtractor', (InfoExtractor,), {'IE_NAME': NO_ATTR})
|
||||||
if valid_url:
|
module_src = '\n'.join((
|
||||||
s += f' _VALID_URL = {valid_url!r}\n'
|
MODULE_TEMPLATE,
|
||||||
if not ie._WORKING:
|
' _module = None',
|
||||||
s += ' _WORKING = False\n'
|
*extra_ie_code(DummyInfoExtractor),
|
||||||
if ie.suitable.__func__ is not InfoExtractor.suitable.__func__:
|
'\nclass LazyLoadSearchExtractor(LazyLoadExtractor):\n pass\n',
|
||||||
s += f'\n{getsource(ie.suitable)}'
|
*build_ies(_ALL_CLASSES, (InfoExtractor, SearchInfoExtractor), DummyInfoExtractor),
|
||||||
if hasattr(ie, '_make_valid_url'):
|
))
|
||||||
# search extractors
|
|
||||||
s += make_valid_template.format(valid_url=ie._make_valid_url())
|
write_file(lazy_extractors_filename, f'{module_src}\n')
|
||||||
return s
|
|
||||||
|
|
||||||
|
def get_all_ies():
|
||||||
# find the correct sorting and add the required base classes so that subclasses
|
PLUGINS_DIRNAME = 'ytdlp_plugins'
|
||||||
# can be correctly created
|
BLOCKED_DIRNAME = f'{PLUGINS_DIRNAME}_blocked'
|
||||||
classes = _ALL_CLASSES[:-1]
|
if os.path.exists(PLUGINS_DIRNAME):
|
||||||
ordered_cls = []
|
# os.rename cannot be used, e.g. in Docker. See https://github.com/yt-dlp/yt-dlp/pull/4958
|
||||||
while classes:
|
shutil.move(PLUGINS_DIRNAME, BLOCKED_DIRNAME)
|
||||||
for c in classes[:]:
|
try:
|
||||||
bases = set(c.__bases__) - set((object, InfoExtractor, SearchInfoExtractor))
|
from yt_dlp.extractor.extractors import _ALL_CLASSES
|
||||||
stop = False
|
finally:
|
||||||
for b in bases:
|
if os.path.exists(BLOCKED_DIRNAME):
|
||||||
if b not in classes and b not in ordered_cls:
|
shutil.move(BLOCKED_DIRNAME, PLUGINS_DIRNAME)
|
||||||
if b.__name__ == 'GenericIE':
|
return _ALL_CLASSES
|
||||||
exit()
|
|
||||||
classes.insert(0, b)
|
|
||||||
stop = True
|
def extra_ie_code(ie, base=None):
|
||||||
if stop:
|
for var in STATIC_CLASS_PROPERTIES:
|
||||||
break
|
val = getattr(ie, var)
|
||||||
if all(b in ordered_cls for b in bases):
|
if val != (getattr(base, var) if base else NO_ATTR):
|
||||||
ordered_cls.append(c)
|
yield f' {var} = {val!r}'
|
||||||
classes.remove(c)
|
yield ''
|
||||||
break
|
|
||||||
ordered_cls.append(_ALL_CLASSES[-1])
|
for name in CLASS_METHODS:
|
||||||
|
f = getattr(ie, name)
|
||||||
names = []
|
if not base or f.__func__ != getattr(base, name).__func__:
|
||||||
for ie in ordered_cls:
|
yield getsource(f)
|
||||||
name = ie.__name__
|
|
||||||
src = build_lazy_ie(ie, name)
|
|
||||||
module_contents.append(src)
|
def build_ies(ies, bases, attr_base):
|
||||||
if ie in _ALL_CLASSES:
|
names = []
|
||||||
names.append(name)
|
for ie in sort_ies(ies, bases):
|
||||||
|
yield build_lazy_ie(ie, ie.__name__, attr_base)
|
||||||
module_contents.append(
|
if ie in ies:
|
||||||
'\n_ALL_CLASSES = [{0}]'.format(', '.join(names)))
|
names.append(ie.__name__)
|
||||||
|
|
||||||
module_src = '\n'.join(module_contents) + '\n'
|
yield f'\n_ALL_CLASSES = [{", ".join(names)}]'
|
||||||
|
|
||||||
with io.open(lazy_extractors_filename, 'wt', encoding='utf-8') as f:
|
|
||||||
f.write(module_src)
|
def sort_ies(ies, ignored_bases):
|
||||||
|
"""find the correct sorting and add the required base classes so that subclasses can be correctly created"""
|
||||||
|
classes, returned_classes = ies[:-1], set()
|
||||||
|
assert ies[-1].__name__ == 'GenericIE', 'Last IE must be GenericIE'
|
||||||
|
while classes:
|
||||||
|
for c in classes[:]:
|
||||||
|
bases = set(c.__bases__) - {object, *ignored_bases}
|
||||||
|
restart = False
|
||||||
|
for b in sorted(bases, key=lambda x: x.__name__):
|
||||||
|
if b not in classes and b not in returned_classes:
|
||||||
|
assert b.__name__ != 'GenericIE', 'Cannot inherit from GenericIE'
|
||||||
|
classes.insert(0, b)
|
||||||
|
restart = True
|
||||||
|
if restart:
|
||||||
|
break
|
||||||
|
if bases <= returned_classes:
|
||||||
|
yield c
|
||||||
|
returned_classes.add(c)
|
||||||
|
classes.remove(c)
|
||||||
|
break
|
||||||
|
yield ies[-1]
|
||||||
|
|
||||||
|
|
||||||
|
def build_lazy_ie(ie, name, attr_base):
|
||||||
|
bases = ', '.join({
|
||||||
|
'InfoExtractor': 'LazyLoadExtractor',
|
||||||
|
'SearchInfoExtractor': 'LazyLoadSearchExtractor',
|
||||||
|
}.get(base.__name__, base.__name__) for base in ie.__bases__)
|
||||||
|
|
||||||
|
s = IE_TEMPLATE.format(name=name, module=ie.__module__, bases=bases)
|
||||||
|
return s + '\n'.join(extra_ie_code(ie, attr_base))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
@ -1,31 +1,93 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
# yt-dlp --help | make_readme.py
|
"""
|
||||||
# This must be run in a console of correct width
|
yt-dlp --help | make_readme.py
|
||||||
|
This must be run in a console of correct width
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
# Allow direct execution
|
||||||
|
import os
|
||||||
import io
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
|
|
||||||
|
import functools
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from devscripts.utils import read_file, write_file
|
||||||
|
|
||||||
README_FILE = 'README.md'
|
README_FILE = 'README.md'
|
||||||
helptext = sys.stdin.read()
|
|
||||||
|
|
||||||
if isinstance(helptext, bytes):
|
OPTIONS_START = 'General Options:'
|
||||||
helptext = helptext.decode('utf-8')
|
OPTIONS_END = 'CONFIGURATION'
|
||||||
|
EPILOG_START = 'See full documentation'
|
||||||
|
ALLOWED_OVERSHOOT = 2
|
||||||
|
|
||||||
|
DISABLE_PATCH = object()
|
||||||
|
|
||||||
|
|
||||||
|
def take_section(text, start=None, end=None, *, shift=0):
|
||||||
|
return text[
|
||||||
|
text.index(start) + shift if start else None:
|
||||||
|
text.index(end) + shift if end else None
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def apply_patch(text, patch):
|
||||||
|
return text if patch[0] is DISABLE_PATCH else re.sub(*patch, text)
|
||||||
|
|
||||||
|
|
||||||
|
options = take_section(sys.stdin.read(), f'\n {OPTIONS_START}', f'\n{EPILOG_START}', shift=1)
|
||||||
|
|
||||||
with io.open(README_FILE, encoding='utf-8') as f:
|
max_width = max(map(len, options.split('\n')))
|
||||||
oldreadme = f.read()
|
switch_col_width = len(re.search(r'(?m)^\s{5,}', options).group())
|
||||||
|
delim = f'\n{" " * switch_col_width}'
|
||||||
|
|
||||||
header = oldreadme[:oldreadme.index('## General Options:')]
|
PATCHES = (
|
||||||
footer = oldreadme[oldreadme.index('# CONFIGURATION'):]
|
( # Standardize `--update` message
|
||||||
|
r'(?m)^( -U, --update\s+).+(\n \s.+)*$',
|
||||||
|
r'\1Update this program to the latest version',
|
||||||
|
),
|
||||||
|
( # Headings
|
||||||
|
r'(?m)^ (\w.+\n)( (?=\w))?',
|
||||||
|
r'## \1'
|
||||||
|
),
|
||||||
|
( # Fixup `--date` formatting
|
||||||
|
rf'(?m)( --date DATE.+({delim}[^\[]+)*)\[.+({delim}.+)*$',
|
||||||
|
(rf'\1[now|today|yesterday][-N[day|week|month|year]].{delim}'
|
||||||
|
f'E.g. "--date today-2weeks" downloads only{delim}'
|
||||||
|
'videos uploaded on the same day two weeks ago'),
|
||||||
|
),
|
||||||
|
( # Do not split URLs
|
||||||
|
rf'({delim[:-1]})? (?P<label>\[\S+\] )?(?P<url>https?({delim})?:({delim})?/({delim})?/(({delim})?\S+)+)\s',
|
||||||
|
lambda mobj: ''.join((delim, mobj.group('label') or '', re.sub(r'\s+', '', mobj.group('url')), '\n'))
|
||||||
|
),
|
||||||
|
( # Do not split "words"
|
||||||
|
rf'(?m)({delim}\S+)+$',
|
||||||
|
lambda mobj: ''.join((delim, mobj.group(0).replace(delim, '')))
|
||||||
|
),
|
||||||
|
( # Allow overshooting last line
|
||||||
|
rf'(?m)^(?P<prev>.+)${delim}(?P<current>.+)$(?!{delim})',
|
||||||
|
lambda mobj: (mobj.group().replace(delim, ' ')
|
||||||
|
if len(mobj.group()) - len(delim) + 1 <= max_width + ALLOWED_OVERSHOOT
|
||||||
|
else mobj.group())
|
||||||
|
),
|
||||||
|
( # Avoid newline when a space is available b/w switch and description
|
||||||
|
DISABLE_PATCH, # This creates issues with prepare_manpage
|
||||||
|
r'(?m)^(\s{4}-.{%d})(%s)' % (switch_col_width - 6, delim),
|
||||||
|
r'\1 '
|
||||||
|
),
|
||||||
|
( # Replace brackets with a Markdown link
|
||||||
|
r'SponsorBlock API \((http.+)\)',
|
||||||
|
r'[SponsorBlock API](\1)'
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
options = helptext[helptext.index(' General Options:'):]
|
readme = read_file(README_FILE)
|
||||||
options = re.sub(r'(?m)^ (\w.+)$', r'## \1', options)
|
|
||||||
options = options + '\n'
|
|
||||||
|
|
||||||
with io.open(README_FILE, 'w', encoding='utf-8') as f:
|
write_file(README_FILE, ''.join((
|
||||||
f.write(header)
|
take_section(readme, end=f'## {OPTIONS_START}'),
|
||||||
f.write(options)
|
functools.reduce(apply_patch, PATCHES, options),
|
||||||
f.write(footer)
|
take_section(readme, f'# {OPTIONS_END}'),
|
||||||
|
)))
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
|
|
||||||
# source this file in your shell to get a POSIX locale (which will break many programs, but that's kind of the point)
|
|
||||||
|
|
||||||
export LC_ALL=POSIX
|
|
||||||
export LANG=POSIX
|
|
||||||
export LANGUAGE=POSIX
|
|
@ -1,143 +0,0 @@
|
|||||||
# Unused
|
|
||||||
|
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# IMPORTANT: the following assumptions are made
|
|
||||||
# * the GH repo is on the origin remote
|
|
||||||
# * the gh-pages branch is named so locally
|
|
||||||
# * the git config user.signingkey is properly set
|
|
||||||
|
|
||||||
# You will need
|
|
||||||
# pip install coverage nose rsa wheel
|
|
||||||
|
|
||||||
# TODO
|
|
||||||
# release notes
|
|
||||||
# make hash on local files
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
skip_tests=true
|
|
||||||
gpg_sign_commits=""
|
|
||||||
buildserver='localhost:8142'
|
|
||||||
|
|
||||||
while true
|
|
||||||
do
|
|
||||||
case "$1" in
|
|
||||||
--run-tests)
|
|
||||||
skip_tests=false
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
--gpg-sign-commits|-S)
|
|
||||||
gpg_sign_commits="-S"
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
--buildserver)
|
|
||||||
buildserver="$2"
|
|
||||||
shift 2
|
|
||||||
;;
|
|
||||||
--*)
|
|
||||||
echo "ERROR: unknown option $1"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
break
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ -z "$1" ]; then echo "ERROR: specify version number like this: $0 1994.09.06"; exit 1; fi
|
|
||||||
version="$1"
|
|
||||||
major_version=$(echo "$version" | sed -n 's#^\([0-9]*\.[0-9]*\.[0-9]*\).*#\1#p')
|
|
||||||
if test "$major_version" '!=' "$(date '+%Y.%m.%d')"; then
|
|
||||||
echo "$version does not start with today's date!"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -z "`git tag | grep "$version"`" ]; then echo 'ERROR: version already present'; exit 1; fi
|
|
||||||
if [ ! -z "`git status --porcelain | grep -v CHANGELOG`" ]; then echo 'ERROR: the working directory is not clean; commit or stash changes'; exit 1; fi
|
|
||||||
useless_files=$(find yt_dlp -type f -not -name '*.py')
|
|
||||||
if [ ! -z "$useless_files" ]; then echo "ERROR: Non-.py files in yt_dlp: $useless_files"; exit 1; fi
|
|
||||||
if [ ! -f "updates_key.pem" ]; then echo 'ERROR: updates_key.pem missing'; exit 1; fi
|
|
||||||
if ! type pandoc >/dev/null 2>/dev/null; then echo 'ERROR: pandoc is missing'; exit 1; fi
|
|
||||||
if ! python3 -c 'import rsa' 2>/dev/null; then echo 'ERROR: python3-rsa is missing'; exit 1; fi
|
|
||||||
if ! python3 -c 'import wheel' 2>/dev/null; then echo 'ERROR: wheel is missing'; exit 1; fi
|
|
||||||
|
|
||||||
read -p "Is Changelog up to date? (y/n) " -n 1
|
|
||||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then exit 1; fi
|
|
||||||
|
|
||||||
/bin/echo -e "\n### First of all, testing..."
|
|
||||||
make clean
|
|
||||||
if $skip_tests ; then
|
|
||||||
echo 'SKIPPING TESTS'
|
|
||||||
else
|
|
||||||
nosetests --verbose --with-coverage --cover-package=yt_dlp --cover-html test --stop || exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
/bin/echo -e "\n### Changing version in version.py..."
|
|
||||||
sed -i "s/__version__ = '.*'/__version__ = '$version'/" yt_dlp/version.py
|
|
||||||
|
|
||||||
/bin/echo -e "\n### Changing version in Changelog..."
|
|
||||||
sed -i "s/<unreleased>/$version/" Changelog.md
|
|
||||||
|
|
||||||
/bin/echo -e "\n### Committing documentation, templates and yt_dlp/version.py..."
|
|
||||||
make README.md CONTRIBUTING.md issuetemplates supportedsites
|
|
||||||
git add README.md CONTRIBUTING.md .github/ISSUE_TEMPLATE/1_broken_site.md .github/ISSUE_TEMPLATE/2_site_support_request.md .github/ISSUE_TEMPLATE/3_site_feature_request.md .github/ISSUE_TEMPLATE/4_bug_report.md .github/ISSUE_TEMPLATE/5_feature_request.md .github/ISSUE_TEMPLATE/6_question.md docs/supportedsites.md yt_dlp/version.py Changelog.md
|
|
||||||
git commit $gpg_sign_commits -m "release $version"
|
|
||||||
|
|
||||||
/bin/echo -e "\n### Now tagging, signing and pushing..."
|
|
||||||
git tag -s -m "Release $version" "$version"
|
|
||||||
git show "$version"
|
|
||||||
read -p "Is it good, can I push? (y/n) " -n 1
|
|
||||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then exit 1; fi
|
|
||||||
echo
|
|
||||||
MASTER=$(git rev-parse --abbrev-ref HEAD)
|
|
||||||
git push origin $MASTER:master
|
|
||||||
git push origin "$version"
|
|
||||||
|
|
||||||
/bin/echo -e "\n### OK, now it is time to build the binaries..."
|
|
||||||
REV=$(git rev-parse HEAD)
|
|
||||||
make yt-dlp yt-dlp.tar.gz
|
|
||||||
read -p "VM running? (y/n) " -n 1
|
|
||||||
wget "http://$buildserver/build/ytdl-org/youtube-dl/yt-dlp.exe?rev=$REV" -O yt-dlp.exe
|
|
||||||
mkdir -p "build/$version"
|
|
||||||
mv yt-dlp yt-dlp.exe "build/$version"
|
|
||||||
mv yt-dlp.tar.gz "build/$version/yt-dlp-$version.tar.gz"
|
|
||||||
RELEASE_FILES="yt-dlp yt-dlp.exe yt-dlp-$version.tar.gz"
|
|
||||||
(cd build/$version/ && md5sum $RELEASE_FILES > MD5SUMS)
|
|
||||||
(cd build/$version/ && sha1sum $RELEASE_FILES > SHA1SUMS)
|
|
||||||
(cd build/$version/ && sha256sum $RELEASE_FILES > SHA2-256SUMS)
|
|
||||||
(cd build/$version/ && sha512sum $RELEASE_FILES > SHA2-512SUMS)
|
|
||||||
|
|
||||||
/bin/echo -e "\n### Signing and uploading the new binaries to GitHub..."
|
|
||||||
for f in $RELEASE_FILES; do gpg --passphrase-repeat 5 --detach-sig "build/$version/$f"; done
|
|
||||||
|
|
||||||
ROOT=$(pwd)
|
|
||||||
python devscripts/create-github-release.py Changelog.md $version "$ROOT/build/$version"
|
|
||||||
|
|
||||||
ssh ytdl@yt-dl.org "sh html/update_latest.sh $version"
|
|
||||||
|
|
||||||
/bin/echo -e "\n### Now switching to gh-pages..."
|
|
||||||
git clone --branch gh-pages --single-branch . build/gh-pages
|
|
||||||
(
|
|
||||||
set -e
|
|
||||||
ORIGIN_URL=$(git config --get remote.origin.url)
|
|
||||||
cd build/gh-pages
|
|
||||||
"$ROOT/devscripts/gh-pages/add-version.py" $version
|
|
||||||
"$ROOT/devscripts/gh-pages/update-feed.py"
|
|
||||||
"$ROOT/devscripts/gh-pages/sign-versions.py" < "$ROOT/updates_key.pem"
|
|
||||||
"$ROOT/devscripts/gh-pages/generate-download.py"
|
|
||||||
"$ROOT/devscripts/gh-pages/update-copyright.py"
|
|
||||||
"$ROOT/devscripts/gh-pages/update-sites.py"
|
|
||||||
git add *.html *.html.in update
|
|
||||||
git commit $gpg_sign_commits -m "release $version"
|
|
||||||
git push "$ROOT" gh-pages
|
|
||||||
git push "$ORIGIN_URL" gh-pages
|
|
||||||
)
|
|
||||||
rm -rf build
|
|
||||||
|
|
||||||
make pypi-files
|
|
||||||
echo "Uploading to PyPi ..."
|
|
||||||
python setup.py sdist bdist_wheel upload
|
|
||||||
make clean
|
|
||||||
|
|
||||||
/bin/echo -e "\n### DONE!"
|
|
@ -1,14 +1,14 @@
|
|||||||
#!/bin/sh
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
if [ -z $1 ]; then
|
if [ -z "$1" ]; then
|
||||||
test_set='test'
|
test_set='test'
|
||||||
elif [ $1 = 'core' ]; then
|
elif [ "$1" = 'core' ]; then
|
||||||
test_set='not download'
|
test_set="-m not download"
|
||||||
elif [ $1 = 'download' ]; then
|
elif [ "$1" = 'download' ]; then
|
||||||
test_set='download'
|
test_set="-m download"
|
||||||
else
|
else
|
||||||
echo 'Invalid test type "'$1'". Use "core" | "download"'
|
echo 'Invalid test type "'"$1"'". Use "core" | "download"'
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
python3 -m pytest -k "$test_set"
|
python3 -bb -Werror -m pytest "$test_set"
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# Allow direct execution
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import functools
|
||||||
|
import re
|
||||||
|
|
||||||
|
from devscripts.utils import compose_functions, read_file, write_file
|
||||||
|
|
||||||
|
VERSION_FILE = 'yt_dlp/version.py'
|
||||||
|
|
||||||
|
|
||||||
|
def parse_options():
|
||||||
|
parser = argparse.ArgumentParser(description='Set the build variant of the package')
|
||||||
|
parser.add_argument('variant', help='Name of the variant')
|
||||||
|
parser.add_argument('-M', '--update-message', default=None, help='Message to show in -U')
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def property_setter(name, value):
|
||||||
|
return functools.partial(re.sub, rf'(?m)^{name}\s*=\s*.+$', f'{name} = {value!r}')
|
||||||
|
|
||||||
|
|
||||||
|
opts = parse_options()
|
||||||
|
transform = compose_functions(
|
||||||
|
property_setter('VARIANT', opts.variant),
|
||||||
|
property_setter('UPDATE_HINT', opts.update_message)
|
||||||
|
)
|
||||||
|
|
||||||
|
write_file(VERSION_FILE, transform(read_file(VERSION_FILE)))
|
@ -1,49 +0,0 @@
|
|||||||
# Unused
|
|
||||||
|
|
||||||
#!/usr/bin/env python3
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import itertools
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
|
|
||||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
||||||
|
|
||||||
from yt_dlp.compat import (
|
|
||||||
compat_print,
|
|
||||||
compat_urllib_request,
|
|
||||||
)
|
|
||||||
from yt_dlp.utils import format_bytes
|
|
||||||
|
|
||||||
|
|
||||||
def format_size(bytes):
|
|
||||||
return '%s (%d bytes)' % (format_bytes(bytes), bytes)
|
|
||||||
|
|
||||||
|
|
||||||
total_bytes = 0
|
|
||||||
|
|
||||||
for page in itertools.count(1):
|
|
||||||
releases = json.loads(compat_urllib_request.urlopen(
|
|
||||||
'https://api.github.com/repos/ytdl-org/youtube-dl/releases?page=%s' % page
|
|
||||||
).read().decode('utf-8'))
|
|
||||||
|
|
||||||
if not releases:
|
|
||||||
break
|
|
||||||
|
|
||||||
for release in releases:
|
|
||||||
compat_print(release['name'])
|
|
||||||
for asset in release['assets']:
|
|
||||||
asset_name = asset['name']
|
|
||||||
total_bytes += asset['download_count'] * asset['size']
|
|
||||||
if all(not re.match(p, asset_name) for p in (
|
|
||||||
r'^yt-dlp$',
|
|
||||||
r'^yt-dlp-\d{4}\.\d{2}\.\d{2}(?:\.\d+)?\.tar\.gz$',
|
|
||||||
r'^yt-dlp\.exe$')):
|
|
||||||
continue
|
|
||||||
compat_print(
|
|
||||||
' %s size: %s downloads: %d'
|
|
||||||
% (asset_name, format_size(asset['size']), asset['download_count']))
|
|
||||||
|
|
||||||
compat_print('total downloads traffic: %s' % format_size(total_bytes))
|
|
@ -1,37 +1,39 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import json
|
"""
|
||||||
|
Usage: python3 ./devscripts/update-formulae.py <path-to-formulae-rb> <version>
|
||||||
|
version can be either 0-aligned (yt-dlp version) or normalized (PyPi version)
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Allow direct execution
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
from yt_dlp.compat import compat_urllib_request
|
|
||||||
|
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
# usage: python3 ./devscripts/update-formulae.py <path-to-formulae-rb> <version>
|
from devscripts.utils import read_file, write_file
|
||||||
# version can be either 0-aligned (yt-dlp version) or normalized (PyPl version)
|
|
||||||
|
|
||||||
filename, version = sys.argv[1:]
|
filename, version = sys.argv[1:]
|
||||||
|
|
||||||
normalized_version = '.'.join(str(int(x)) for x in version.split('.'))
|
normalized_version = '.'.join(str(int(x)) for x in version.split('.'))
|
||||||
|
|
||||||
pypi_release = json.loads(compat_urllib_request.urlopen(
|
pypi_release = json.loads(urllib.request.urlopen(
|
||||||
'https://pypi.org/pypi/yt-dlp/%s/json' % normalized_version
|
'https://pypi.org/pypi/yt-dlp/%s/json' % normalized_version
|
||||||
).read().decode('utf-8'))
|
).read().decode())
|
||||||
|
|
||||||
tarball_file = next(x for x in pypi_release['urls'] if x['filename'].endswith('.tar.gz'))
|
tarball_file = next(x for x in pypi_release['urls'] if x['filename'].endswith('.tar.gz'))
|
||||||
|
|
||||||
sha256sum = tarball_file['digests']['sha256']
|
sha256sum = tarball_file['digests']['sha256']
|
||||||
url = tarball_file['url']
|
url = tarball_file['url']
|
||||||
|
|
||||||
with open(filename, 'r') as r:
|
formulae_text = read_file(filename)
|
||||||
formulae_text = r.read()
|
|
||||||
|
|
||||||
formulae_text = re.sub(r'sha256 "[0-9a-f]*?"', 'sha256 "%s"' % sha256sum, formulae_text)
|
formulae_text = re.sub(r'sha256 "[0-9a-f]*?"', 'sha256 "%s"' % sha256sum, formulae_text, count=1)
|
||||||
formulae_text = re.sub(r'url "[^"]*?"', 'url "%s"' % url, formulae_text)
|
formulae_text = re.sub(r'url "[^"]*?"', 'url "%s"' % url, formulae_text, count=1)
|
||||||
|
|
||||||
with open(filename, 'w') as w:
|
write_file(filename, formulae_text)
|
||||||
w.write(formulae_text)
|
|
||||||
|
@ -1,33 +1,71 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
from datetime import datetime
|
# Allow direct execution
|
||||||
# import urllib.request
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
# response = urllib.request.urlopen('https://blackjack4494.github.io/youtube-dlc/update/LATEST_VERSION')
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
# old_version = response.read().decode('utf-8')
|
|
||||||
|
|
||||||
exec(compile(open('yt_dlp/version.py').read(), 'yt_dlp/version.py', 'exec'))
|
|
||||||
old_version = locals()['__version__']
|
|
||||||
|
|
||||||
old_version_list = old_version.split(".", 4)
|
import argparse
|
||||||
|
import contextlib
|
||||||
|
import sys
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
old_ver = '.'.join(old_version_list[:3])
|
from devscripts.utils import read_version, run_process, write_file
|
||||||
old_rev = old_version_list[3] if len(old_version_list) > 3 else ''
|
|
||||||
|
|
||||||
ver = datetime.utcnow().strftime("%Y.%m.%d")
|
|
||||||
rev = str(int(old_rev or 0) + 1) if old_ver == ver else ''
|
|
||||||
|
|
||||||
VERSION = '.'.join((ver, rev)) if rev else ver
|
def get_new_version(version, revision):
|
||||||
# VERSION_LIST = [(int(v) for v in ver.split(".") + [rev or 0])]
|
if not version:
|
||||||
|
version = datetime.now(timezone.utc).strftime('%Y.%m.%d')
|
||||||
|
|
||||||
print('::set-output name=ytdlp_version::' + VERSION)
|
if revision:
|
||||||
|
assert revision.isdigit(), 'Revision must be a number'
|
||||||
|
else:
|
||||||
|
old_version = read_version().split('.')
|
||||||
|
if version.split('.') == old_version[:3]:
|
||||||
|
revision = str(int((old_version + [0])[3]) + 1)
|
||||||
|
|
||||||
file_version_py = open('yt_dlp/version.py', 'rt')
|
return f'{version}.{revision}' if revision else version
|
||||||
data = file_version_py.read()
|
|
||||||
data = data.replace(old_version, VERSION)
|
|
||||||
file_version_py.close()
|
|
||||||
|
|
||||||
file_version_py = open('yt_dlp/version.py', 'wt')
|
|
||||||
file_version_py.write(data)
|
def get_git_head():
|
||||||
file_version_py.close()
|
with contextlib.suppress(Exception):
|
||||||
|
return run_process('git', 'rev-parse', 'HEAD').stdout.strip()
|
||||||
|
|
||||||
|
|
||||||
|
VERSION_TEMPLATE = '''\
|
||||||
|
# Autogenerated by devscripts/update-version.py
|
||||||
|
|
||||||
|
__version__ = {version!r}
|
||||||
|
|
||||||
|
RELEASE_GIT_HEAD = {git_head!r}
|
||||||
|
|
||||||
|
VARIANT = None
|
||||||
|
|
||||||
|
UPDATE_HINT = None
|
||||||
|
|
||||||
|
CHANNEL = {channel!r}
|
||||||
|
'''
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
parser = argparse.ArgumentParser(description='Update the version.py file')
|
||||||
|
parser.add_argument(
|
||||||
|
'-c', '--channel', default='stable',
|
||||||
|
help='Select update channel (default: %(default)s)')
|
||||||
|
parser.add_argument(
|
||||||
|
'-o', '--output', default='yt_dlp/version.py',
|
||||||
|
help='The output file to write to (default: %(default)s)')
|
||||||
|
parser.add_argument(
|
||||||
|
'version', nargs='?', default=None,
|
||||||
|
help='A version or revision to use instead of generating one')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
git_head = get_git_head()
|
||||||
|
version = (
|
||||||
|
args.version if args.version and '.' in args.version
|
||||||
|
else get_new_version(None, args.version))
|
||||||
|
write_file(args.output, VERSION_TEMPLATE.format(
|
||||||
|
version=version, git_head=git_head, channel=args.channel))
|
||||||
|
|
||||||
|
print(f'version={version} ({args.channel}), head={git_head}')
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
import argparse
|
||||||
|
import functools
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
|
def read_file(fname):
|
||||||
|
with open(fname, encoding='utf-8') as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
|
|
||||||
|
def write_file(fname, content, mode='w'):
|
||||||
|
with open(fname, mode, encoding='utf-8') as f:
|
||||||
|
return f.write(content)
|
||||||
|
|
||||||
|
|
||||||
|
def read_version(fname='yt_dlp/version.py'):
|
||||||
|
"""Get the version without importing the package"""
|
||||||
|
exec(compile(read_file(fname), fname, 'exec'))
|
||||||
|
return locals()['__version__']
|
||||||
|
|
||||||
|
|
||||||
|
def get_filename_args(has_infile=False, default_outfile=None):
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
if has_infile:
|
||||||
|
parser.add_argument('infile', help='Input file')
|
||||||
|
kwargs = {'nargs': '?', 'default': default_outfile} if default_outfile else {}
|
||||||
|
parser.add_argument('outfile', **kwargs, help='Output file')
|
||||||
|
|
||||||
|
opts = parser.parse_args()
|
||||||
|
if has_infile:
|
||||||
|
return opts.infile, opts.outfile
|
||||||
|
return opts.outfile
|
||||||
|
|
||||||
|
|
||||||
|
def compose_functions(*functions):
|
||||||
|
return lambda x: functools.reduce(lambda y, f: f(y), functions, x)
|
||||||
|
|
||||||
|
|
||||||
|
def run_process(*args, **kwargs):
|
||||||
|
kwargs.setdefault('text', True)
|
||||||
|
kwargs.setdefault('check', True)
|
||||||
|
kwargs.setdefault('capture_output', True)
|
||||||
|
if kwargs['text']:
|
||||||
|
kwargs.setdefault('encoding', 'utf-8')
|
||||||
|
kwargs.setdefault('errors', 'replace')
|
||||||
|
return subprocess.run(args, **kwargs)
|
@ -1,58 +0,0 @@
|
|||||||
# UNUSED
|
|
||||||
|
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Run with as parameter a setup.py that works in the current directory
|
|
||||||
# e.g. no os.chdir()
|
|
||||||
# It will run twice, the first time will crash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
SCRIPT_DIR="$( cd "$( dirname "$0" )" && pwd )"
|
|
||||||
|
|
||||||
if [ ! -d wine-py2exe ]; then
|
|
||||||
|
|
||||||
sudo apt-get install wine1.3 axel bsdiff
|
|
||||||
|
|
||||||
mkdir wine-py2exe
|
|
||||||
cd wine-py2exe
|
|
||||||
export WINEPREFIX=`pwd`
|
|
||||||
|
|
||||||
axel -a "http://www.python.org/ftp/python/2.7/python-2.7.msi"
|
|
||||||
axel -a "http://downloads.sourceforge.net/project/py2exe/py2exe/0.6.9/py2exe-0.6.9.win32-py2.7.exe"
|
|
||||||
#axel -a "http://winetricks.org/winetricks"
|
|
||||||
|
|
||||||
# http://appdb.winehq.org/objectManager.php?sClass=version&iId=21957
|
|
||||||
echo "Follow python setup on screen"
|
|
||||||
wine msiexec /i python-2.7.msi
|
|
||||||
|
|
||||||
echo "Follow py2exe setup on screen"
|
|
||||||
wine py2exe-0.6.9.win32-py2.7.exe
|
|
||||||
|
|
||||||
#echo "Follow Microsoft Visual C++ 2008 Redistributable Package setup on screen"
|
|
||||||
#bash winetricks vcrun2008
|
|
||||||
|
|
||||||
rm py2exe-0.6.9.win32-py2.7.exe
|
|
||||||
rm python-2.7.msi
|
|
||||||
#rm winetricks
|
|
||||||
|
|
||||||
# http://bugs.winehq.org/show_bug.cgi?id=3591
|
|
||||||
|
|
||||||
mv drive_c/Python27/Lib/site-packages/py2exe/run.exe drive_c/Python27/Lib/site-packages/py2exe/run.exe.backup
|
|
||||||
bspatch drive_c/Python27/Lib/site-packages/py2exe/run.exe.backup drive_c/Python27/Lib/site-packages/py2exe/run.exe "$SCRIPT_DIR/SizeOfImage.patch"
|
|
||||||
mv drive_c/Python27/Lib/site-packages/py2exe/run_w.exe drive_c/Python27/Lib/site-packages/py2exe/run_w.exe.backup
|
|
||||||
bspatch drive_c/Python27/Lib/site-packages/py2exe/run_w.exe.backup drive_c/Python27/Lib/site-packages/py2exe/run_w.exe "$SCRIPT_DIR/SizeOfImage_w.patch"
|
|
||||||
|
|
||||||
cd -
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
export WINEPREFIX="$( cd wine-py2exe && pwd )"
|
|
||||||
|
|
||||||
fi
|
|
||||||
|
|
||||||
wine "C:\\Python27\\python.exe" "$1" py2exe > "py2exe.log" 2>&1 || true
|
|
||||||
echo '# Copying python27.dll' >> "py2exe.log"
|
|
||||||
cp "$WINEPREFIX/drive_c/windows/system32/python27.dll" build/bdist.win32/winexe/bundle-2.7/
|
|
||||||
wine "C:\\Python27\\python.exe" "$1" py2exe >> "py2exe.log" 2>&1
|
|
||||||
|
|
@ -1 +0,0 @@
|
|||||||
_build/
|
|
@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
orphan: true
|
|
||||||
---
|
|
||||||
```{include} ../Changelog.md
|
|
||||||
```
|
|
@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
orphan: true
|
|
||||||
---
|
|
||||||
```{include} ../Collaborators.md
|
|
||||||
```
|
|
@ -1,6 +0,0 @@
|
|||||||
---
|
|
||||||
orphan: true
|
|
||||||
---
|
|
||||||
# LICENSE
|
|
||||||
```{include} ../LICENSE
|
|
||||||
```
|
|
@ -1,177 +0,0 @@
|
|||||||
# Makefile for Sphinx documentation
|
|
||||||
#
|
|
||||||
|
|
||||||
# You can set these variables from the command line.
|
|
||||||
SPHINXOPTS =
|
|
||||||
SPHINXBUILD = sphinx-build
|
|
||||||
PAPER =
|
|
||||||
BUILDDIR = _build
|
|
||||||
|
|
||||||
# User-friendly check for sphinx-build
|
|
||||||
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
|
|
||||||
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
|
|
||||||
endif
|
|
||||||
|
|
||||||
# Internal variables.
|
|
||||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
|
||||||
PAPEROPT_letter = -D latex_paper_size=letter
|
|
||||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
|
||||||
# the i18n builder cannot share the environment and doctrees with the others
|
|
||||||
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
|
||||||
|
|
||||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
|
|
||||||
|
|
||||||
help:
|
|
||||||
@echo "Please use \`make <target>' where <target> is one of"
|
|
||||||
@echo " html to make standalone HTML files"
|
|
||||||
@echo " dirhtml to make HTML files named index.html in directories"
|
|
||||||
@echo " singlehtml to make a single large HTML file"
|
|
||||||
@echo " pickle to make pickle files"
|
|
||||||
@echo " json to make JSON files"
|
|
||||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
|
||||||
@echo " qthelp to make HTML files and a qthelp project"
|
|
||||||
@echo " devhelp to make HTML files and a Devhelp project"
|
|
||||||
@echo " epub to make an epub"
|
|
||||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
|
||||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
|
||||||
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
|
|
||||||
@echo " text to make text files"
|
|
||||||
@echo " man to make manual pages"
|
|
||||||
@echo " texinfo to make Texinfo files"
|
|
||||||
@echo " info to make Texinfo files and run them through makeinfo"
|
|
||||||
@echo " gettext to make PO message catalogs"
|
|
||||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
|
||||||
@echo " xml to make Docutils-native XML files"
|
|
||||||
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
|
|
||||||
@echo " linkcheck to check all external links for integrity"
|
|
||||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -rf $(BUILDDIR)/*
|
|
||||||
|
|
||||||
html:
|
|
||||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
|
||||||
|
|
||||||
dirhtml:
|
|
||||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
|
||||||
|
|
||||||
singlehtml:
|
|
||||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
|
||||||
|
|
||||||
pickle:
|
|
||||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
|
||||||
@echo
|
|
||||||
@echo "Build finished; now you can process the pickle files."
|
|
||||||
|
|
||||||
json:
|
|
||||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
|
||||||
@echo
|
|
||||||
@echo "Build finished; now you can process the JSON files."
|
|
||||||
|
|
||||||
htmlhelp:
|
|
||||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
|
||||||
@echo
|
|
||||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
|
||||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
|
||||||
|
|
||||||
qthelp:
|
|
||||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
|
||||||
@echo
|
|
||||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
|
||||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
|
||||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/yt-dlp.qhcp"
|
|
||||||
@echo "To view the help file:"
|
|
||||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/yt-dlp.qhc"
|
|
||||||
|
|
||||||
devhelp:
|
|
||||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
|
||||||
@echo
|
|
||||||
@echo "Build finished."
|
|
||||||
@echo "To view the help file:"
|
|
||||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/yt-dlp"
|
|
||||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/yt-dlp"
|
|
||||||
@echo "# devhelp"
|
|
||||||
|
|
||||||
epub:
|
|
||||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
|
||||||
|
|
||||||
latex:
|
|
||||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
|
||||||
@echo
|
|
||||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
|
||||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
|
||||||
"(use \`make latexpdf' here to do that automatically)."
|
|
||||||
|
|
||||||
latexpdf:
|
|
||||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
|
||||||
@echo "Running LaTeX files through pdflatex..."
|
|
||||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf
|
|
||||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
|
||||||
|
|
||||||
latexpdfja:
|
|
||||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
|
||||||
@echo "Running LaTeX files through platex and dvipdfmx..."
|
|
||||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
|
|
||||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
|
||||||
|
|
||||||
text:
|
|
||||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
|
||||||
|
|
||||||
man:
|
|
||||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
|
||||||
|
|
||||||
texinfo:
|
|
||||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
|
|
||||||
@echo "Run \`make' in that directory to run these through makeinfo" \
|
|
||||||
"(use \`make info' here to do that automatically)."
|
|
||||||
|
|
||||||
info:
|
|
||||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
|
||||||
@echo "Running Texinfo files through makeinfo..."
|
|
||||||
make -C $(BUILDDIR)/texinfo info
|
|
||||||
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
|
||||||
|
|
||||||
gettext:
|
|
||||||
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
|
|
||||||
|
|
||||||
changes:
|
|
||||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
|
||||||
@echo
|
|
||||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
|
||||||
|
|
||||||
linkcheck:
|
|
||||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
|
||||||
@echo
|
|
||||||
@echo "Link check complete; look for any errors in the above output " \
|
|
||||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
|
||||||
|
|
||||||
doctest:
|
|
||||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
|
||||||
@echo "Testing of doctests in the sources finished, look at the " \
|
|
||||||
"results in $(BUILDDIR)/doctest/output.txt."
|
|
||||||
|
|
||||||
xml:
|
|
||||||
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
|
|
||||||
|
|
||||||
pseudoxml:
|
|
||||||
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
|
|
@ -1,2 +0,0 @@
|
|||||||
```{include} ../README.md
|
|
||||||
```
|
|
@ -1,68 +0,0 @@
|
|||||||
# coding: utf-8
|
|
||||||
#
|
|
||||||
# yt-dlp documentation build configuration file
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
|
|
||||||
# Allows to import yt-dlp
|
|
||||||
sys.path.insert(0, os.path.abspath('..'))
|
|
||||||
|
|
||||||
# -- General configuration ------------------------------------------------
|
|
||||||
|
|
||||||
# Add any Sphinx extension module names here, as strings. They can be
|
|
||||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
|
||||||
# ones.
|
|
||||||
extensions = [
|
|
||||||
'myst_parser',
|
|
||||||
]
|
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
|
||||||
templates_path = ['_templates']
|
|
||||||
|
|
||||||
# The master toctree document.
|
|
||||||
master_doc = 'README'
|
|
||||||
|
|
||||||
# General information about the project.
|
|
||||||
project = u'yt-dlp'
|
|
||||||
author = u'yt-dlp'
|
|
||||||
copyright = u'UNLICENSE'
|
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
|
||||||
# |version| and |release|, also used in various other places throughout the
|
|
||||||
# built documents.
|
|
||||||
#
|
|
||||||
# The short X.Y version.
|
|
||||||
from yt_dlp.version import __version__
|
|
||||||
version = __version__
|
|
||||||
# The full version, including alpha/beta/rc tags.
|
|
||||||
release = version
|
|
||||||
|
|
||||||
# List of patterns, relative to source directory, that match files and
|
|
||||||
# directories to ignore when looking for source files.
|
|
||||||
exclude_patterns = ['_build']
|
|
||||||
|
|
||||||
# The name of the Pygments (syntax highlighting) style to use.
|
|
||||||
pygments_style = 'sphinx'
|
|
||||||
|
|
||||||
# -- Options for HTML output ----------------------------------------------
|
|
||||||
|
|
||||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
|
||||||
# a list of builtin themes.
|
|
||||||
html_theme = 'default'
|
|
||||||
|
|
||||||
# Disable highlights
|
|
||||||
highlight_language = 'none'
|
|
||||||
|
|
||||||
# Add any paths that contain custom static files (such as style sheets) here,
|
|
||||||
# relative to this directory. They are copied after the builtin static files,
|
|
||||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
|
||||||
# html_static_path = ['_static']
|
|
||||||
|
|
||||||
# Enable heading anchors
|
|
||||||
myst_heading_anchors = 4
|
|
||||||
|
|
||||||
# Suppress heading warnings
|
|
||||||
suppress_warnings = [
|
|
||||||
'myst.header',
|
|
||||||
]
|
|
@ -1 +0,0 @@
|
|||||||
myst-parser
|
|
@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
orphan: true
|
|
||||||
---
|
|
||||||
```{include} ../supportedsites.md
|
|
||||||
```
|
|
@ -1,6 +0,0 @@
|
|||||||
---
|
|
||||||
orphan: true
|
|
||||||
---
|
|
||||||
# ytdlp_plugins
|
|
||||||
|
|
||||||
See [https://github.com/yt-dlp/yt-dlp/tree/master/ytdlp_plugins](https://github.com/yt-dlp/yt-dlp/tree/master/ytdlp_plugins).
|
|
@ -0,0 +1,29 @@
|
|||||||
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
|
mQINBGP78C4BEAD0rF9zjGPAt0thlt5C1ebzccAVX7Nb1v+eqQjk+WEZdTETVCg3
|
||||||
|
WAM5ngArlHdm/fZqzUgO+pAYrB60GKeg7ffUDf+S0XFKEZdeRLYeAaqqKhSibVal
|
||||||
|
DjvOBOztu3W607HLETQAqA7wTPuIt2WqmpL60NIcyr27LxqmgdN3mNvZ2iLO+bP0
|
||||||
|
nKR/C+PgE9H4ytywDa12zMx6PmZCnVOOOu6XZEFmdUxxdQ9fFDqd9LcBKY2LDOcS
|
||||||
|
Yo1saY0YWiZWHtzVoZu1kOzjnS5Fjq/yBHJLImDH7pNxHm7s/PnaurpmQFtDFruk
|
||||||
|
t+2lhDnpKUmGr/I/3IHqH/X+9nPoS4uiqQ5HpblB8BK+4WfpaiEg75LnvuOPfZIP
|
||||||
|
KYyXa/0A7QojMwgOrD88ozT+VCkKkkJ+ijXZ7gHNjmcBaUdKK7fDIEOYI63Lyc6Q
|
||||||
|
WkGQTigFffSUXWHDCO9aXNhP3ejqFWgGMtCUsrbkcJkWuWY7q5ARy/05HbSM3K4D
|
||||||
|
U9eqtnxmiV1WQ8nXuI9JgJQRvh5PTkny5LtxqzcmqvWO9TjHBbrs14BPEO9fcXxK
|
||||||
|
L/CFBbzXDSvvAgArdqqlMoncQ/yicTlfL6qzJ8EKFiqW14QMTdAn6SuuZTodXCTi
|
||||||
|
InwoT7WjjuFPKKdvfH1GP4bnqdzTnzLxCSDIEtfyfPsIX+9GI7Jkk/zZjQARAQAB
|
||||||
|
tDdTaW1vbiBTYXdpY2tpICh5dC1kbHAgc2lnbmluZyBrZXkpIDxjb250YWN0QGdy
|
||||||
|
dWI0ay54eXo+iQJOBBMBCgA4FiEErAy75oSNaoc0ZK9OV89lkztadYEFAmP78C4C
|
||||||
|
GwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQV89lkztadYEVqQ//cW7TxhXg
|
||||||
|
7Xbh2EZQzXml0egn6j8QaV9KzGragMiShrlvTO2zXfLXqyizrFP4AspgjSn/4NrI
|
||||||
|
8mluom+Yi+qr7DXT4BjQqIM9y3AjwZPdywe912Lxcw52NNoPZCm24I9T7ySc8lmR
|
||||||
|
FQvZC0w4H/VTNj/2lgJ1dwMflpwvNRiWa5YzcFGlCUeDIPskLx9++AJE+xwU3LYm
|
||||||
|
jQQsPBqpHHiTBEJzMLl+rfd9Fg4N+QNzpFkTDW3EPerLuvJniSBBwZthqxeAtw4M
|
||||||
|
UiAXh6JvCc2hJkKCoygRfM281MeolvmsGNyQm+axlB0vyldiPP6BnaRgZlx+l6MU
|
||||||
|
cPqgHblb7RW5j9lfr6OYL7SceBIHNv0CFrt1OnkGo/tVMwcs8LH3Ae4a7UJlIceL
|
||||||
|
V54aRxSsZU7w4iX+PB79BWkEsQzwKrUuJVOeL4UDwWajp75OFaUqbS/slDDVXvK5
|
||||||
|
OIeuth3mA/adjdvgjPxhRQjA3l69rRWIJDrqBSHldmRsnX6cvXTDy8wSXZgy51lP
|
||||||
|
m4IVLHnCy9m4SaGGoAsfTZS0cC9FgjUIyTyrq9M67wOMpUxnuB0aRZgJE1DsI23E
|
||||||
|
qdvcSNVlO+39xM/KPWUEh6b83wMn88QeW+DCVGWACQq5N3YdPnAJa50617fGbY6I
|
||||||
|
gXIoRHXkDqe23PZ/jURYCv0sjVtjPoVC+bg=
|
||||||
|
=bJkn
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
@ -1,92 +1,132 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# coding: utf-8
|
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
# Allow direct execution
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
# import os
|
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
import platform
|
import platform
|
||||||
|
|
||||||
from PyInstaller.utils.hooks import collect_submodules
|
from PyInstaller.__main__ import run as run_pyinstaller
|
||||||
from PyInstaller.utils.win32.versioninfo import (
|
|
||||||
VarStruct, VarFileInfo, StringStruct, StringTable,
|
from devscripts.utils import read_version
|
||||||
StringFileInfo, FixedFileInfo, VSVersionInfo, SetVersion,
|
|
||||||
)
|
OS_NAME, MACHINE, ARCH = sys.platform, platform.machine().lower(), platform.architecture()[0][:2]
|
||||||
import PyInstaller.__main__
|
if MACHINE in ('x86', 'x86_64', 'amd64', 'i386', 'i686'):
|
||||||
|
MACHINE = 'x86' if ARCH == '32' else ''
|
||||||
arch = platform.architecture()[0][:2]
|
|
||||||
assert arch in ('32', '64')
|
|
||||||
_x86 = '_x86' if arch == '32' else ''
|
def main():
|
||||||
|
opts, version = parse_options(), read_version()
|
||||||
# Compatability with older arguments
|
|
||||||
opts = sys.argv[1:]
|
onedir = '--onedir' in opts or '-D' in opts
|
||||||
if opts[0:1] in (['32'], ['64']):
|
if not onedir and '-F' not in opts and '--onefile' not in opts:
|
||||||
if arch != opts[0]:
|
opts.append('--onefile')
|
||||||
raise Exception(f'{opts[0]}bit executable cannot be built on a {arch}bit system')
|
|
||||||
opts = opts[1:]
|
name, final_file = exe(onedir)
|
||||||
opts = opts or ['--onefile']
|
print(f'Building yt-dlp v{version} for {OS_NAME} {platform.machine()} with options {opts}')
|
||||||
|
print('Remember to update the version using "devscripts/update-version.py"')
|
||||||
print(f'Building {arch}bit version with options {opts}')
|
if not os.path.isfile('yt_dlp/extractor/lazy_extractors.py'):
|
||||||
|
print('WARNING: Building without lazy_extractors. Run '
|
||||||
FILE_DESCRIPTION = 'yt-dlp%s' % (' (32 Bit)' if _x86 else '')
|
'"devscripts/make_lazy_extractors.py" to build lazy extractors', file=sys.stderr)
|
||||||
|
print(f'Destination: {final_file}\n')
|
||||||
# root_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
|
||||||
# print('Changing working directory to %s' % root_dir)
|
opts = [
|
||||||
# os.chdir(root_dir)
|
f'--name={name}',
|
||||||
|
'--icon=devscripts/logo.ico',
|
||||||
exec(compile(open('yt_dlp/version.py').read(), 'yt_dlp/version.py', 'exec'))
|
'--upx-exclude=vcruntime140.dll',
|
||||||
VERSION = locals()['__version__']
|
'--noconfirm',
|
||||||
|
'--additional-hooks-dir=yt_dlp/__pyinstaller',
|
||||||
VERSION_LIST = VERSION.split('.')
|
*opts,
|
||||||
VERSION_LIST = list(map(int, VERSION_LIST)) + [0] * (4 - len(VERSION_LIST))
|
'yt_dlp/__main__.py',
|
||||||
|
|
||||||
print('Version: %s%s' % (VERSION, _x86))
|
|
||||||
print('Remember to update the version using devscipts\\update-version.py')
|
|
||||||
|
|
||||||
VERSION_FILE = VSVersionInfo(
|
|
||||||
ffi=FixedFileInfo(
|
|
||||||
filevers=VERSION_LIST,
|
|
||||||
prodvers=VERSION_LIST,
|
|
||||||
mask=0x3F,
|
|
||||||
flags=0x0,
|
|
||||||
OS=0x4,
|
|
||||||
fileType=0x1,
|
|
||||||
subtype=0x0,
|
|
||||||
date=(0, 0),
|
|
||||||
),
|
|
||||||
kids=[
|
|
||||||
StringFileInfo([
|
|
||||||
StringTable(
|
|
||||||
'040904B0', [
|
|
||||||
StringStruct('Comments', 'yt-dlp%s Command Line Interface.' % _x86),
|
|
||||||
StringStruct('CompanyName', 'https://github.com/yt-dlp'),
|
|
||||||
StringStruct('FileDescription', FILE_DESCRIPTION),
|
|
||||||
StringStruct('FileVersion', VERSION),
|
|
||||||
StringStruct('InternalName', 'yt-dlp%s' % _x86),
|
|
||||||
StringStruct(
|
|
||||||
'LegalCopyright',
|
|
||||||
'pukkandan.ytdlp@gmail.com | UNLICENSE',
|
|
||||||
),
|
|
||||||
StringStruct('OriginalFilename', 'yt-dlp%s.exe' % _x86),
|
|
||||||
StringStruct('ProductName', 'yt-dlp%s' % _x86),
|
|
||||||
StringStruct(
|
|
||||||
'ProductVersion',
|
|
||||||
'%s%s on Python %s' % (VERSION, _x86, platform.python_version())),
|
|
||||||
])]),
|
|
||||||
VarFileInfo([VarStruct('Translation', [0, 1200])])
|
|
||||||
]
|
]
|
||||||
)
|
|
||||||
|
print(f'Running PyInstaller with {opts}')
|
||||||
dependancies = ['Crypto', 'mutagen'] + collect_submodules('websockets')
|
run_pyinstaller(opts)
|
||||||
excluded_modules = ['test', 'ytdlp_plugins', 'youtube-dl', 'youtube-dlc']
|
set_version_info(final_file, version)
|
||||||
|
|
||||||
PyInstaller.__main__.run([
|
|
||||||
'--name=yt-dlp%s' % _x86,
|
def parse_options():
|
||||||
'--icon=devscripts/logo.ico',
|
# Compatibility with older arguments
|
||||||
*[f'--exclude-module={module}' for module in excluded_modules],
|
opts = sys.argv[1:]
|
||||||
*[f'--hidden-import={module}' for module in dependancies],
|
if opts[0:1] in (['32'], ['64']):
|
||||||
'--upx-exclude=vcruntime140.dll',
|
if ARCH != opts[0]:
|
||||||
'--noconfirm',
|
raise Exception(f'{opts[0]}bit executable cannot be built on a {ARCH}bit system')
|
||||||
*opts,
|
opts = opts[1:]
|
||||||
'yt_dlp/__main__.py',
|
return opts
|
||||||
])
|
|
||||||
SetVersion('dist/%syt-dlp%s.exe' % ('yt-dlp/' if '--onedir' in opts else '', _x86), VERSION_FILE)
|
|
||||||
|
def exe(onedir):
|
||||||
|
"""@returns (name, path)"""
|
||||||
|
name = '_'.join(filter(None, (
|
||||||
|
'yt-dlp',
|
||||||
|
{'win32': '', 'darwin': 'macos'}.get(OS_NAME, OS_NAME),
|
||||||
|
MACHINE,
|
||||||
|
)))
|
||||||
|
return name, ''.join(filter(None, (
|
||||||
|
'dist/',
|
||||||
|
onedir and f'{name}/',
|
||||||
|
name,
|
||||||
|
OS_NAME == 'win32' and '.exe'
|
||||||
|
)))
|
||||||
|
|
||||||
|
|
||||||
|
def version_to_list(version):
|
||||||
|
version_list = version.split('.')
|
||||||
|
return list(map(int, version_list)) + [0] * (4 - len(version_list))
|
||||||
|
|
||||||
|
|
||||||
|
def set_version_info(exe, version):
|
||||||
|
if OS_NAME == 'win32':
|
||||||
|
windows_set_version(exe, version)
|
||||||
|
|
||||||
|
|
||||||
|
def windows_set_version(exe, version):
|
||||||
|
from PyInstaller.utils.win32.versioninfo import (
|
||||||
|
FixedFileInfo,
|
||||||
|
StringFileInfo,
|
||||||
|
StringStruct,
|
||||||
|
StringTable,
|
||||||
|
VarFileInfo,
|
||||||
|
VarStruct,
|
||||||
|
VSVersionInfo,
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
from PyInstaller.utils.win32.versioninfo import SetVersion
|
||||||
|
except ImportError: # Pyinstaller >= 5.8
|
||||||
|
from PyInstaller.utils.win32.versioninfo import write_version_info_to_executable as SetVersion
|
||||||
|
|
||||||
|
version_list = version_to_list(version)
|
||||||
|
suffix = MACHINE and f'_{MACHINE}'
|
||||||
|
SetVersion(exe, VSVersionInfo(
|
||||||
|
ffi=FixedFileInfo(
|
||||||
|
filevers=version_list,
|
||||||
|
prodvers=version_list,
|
||||||
|
mask=0x3F,
|
||||||
|
flags=0x0,
|
||||||
|
OS=0x4,
|
||||||
|
fileType=0x1,
|
||||||
|
subtype=0x0,
|
||||||
|
date=(0, 0),
|
||||||
|
),
|
||||||
|
kids=[
|
||||||
|
StringFileInfo([StringTable('040904B0', [
|
||||||
|
StringStruct('Comments', 'yt-dlp%s Command Line Interface' % suffix),
|
||||||
|
StringStruct('CompanyName', 'https://github.com/yt-dlp'),
|
||||||
|
StringStruct('FileDescription', 'yt-dlp%s' % (MACHINE and f' ({MACHINE})')),
|
||||||
|
StringStruct('FileVersion', version),
|
||||||
|
StringStruct('InternalName', f'yt-dlp{suffix}'),
|
||||||
|
StringStruct('LegalCopyright', 'pukkandan.ytdlp@gmail.com | UNLICENSE'),
|
||||||
|
StringStruct('OriginalFilename', f'yt-dlp{suffix}.exe'),
|
||||||
|
StringStruct('ProductName', f'yt-dlp{suffix}'),
|
||||||
|
StringStruct(
|
||||||
|
'ProductVersion', f'{version}{suffix} on Python {platform.python_version()}'),
|
||||||
|
])]), VarFileInfo([VarStruct('Translation', [0, 1200])])
|
||||||
|
]
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
[build-system]
|
||||||
|
build-backend = 'setuptools.build_meta'
|
||||||
|
# https://github.com/yt-dlp/yt-dlp/issues/5941
|
||||||
|
# https://github.com/pypa/distutils/issues/17
|
||||||
|
requires = ['setuptools > 50']
|
@ -1,4 +0,0 @@
|
|||||||
[pytest]
|
|
||||||
addopts = -ra -v --strict-markers
|
|
||||||
markers =
|
|
||||||
download
|
|
@ -1,3 +1,8 @@
|
|||||||
mutagen
|
mutagen
|
||||||
pycryptodome
|
pycryptodomex
|
||||||
websockets
|
websockets
|
||||||
|
brotli; platform_python_implementation=='CPython'
|
||||||
|
brotlicffi; platform_python_implementation!='CPython'
|
||||||
|
certifi
|
||||||
|
requests>=2.31.0,<3
|
||||||
|
urllib3>=1.26.17,<3
|
@ -1,6 +1,49 @@
|
|||||||
[wheel]
|
[wheel]
|
||||||
universal = True
|
universal = true
|
||||||
|
|
||||||
|
|
||||||
[flake8]
|
[flake8]
|
||||||
exclude = yt_dlp/extractor/__init__.py,devscripts/buildserver.py,devscripts/lazy_load_template.py,devscripts/make_issue_template.py,setup.py,build,.git,venv,devscripts/create-github-release.py,devscripts/release.sh,devscripts/show-downloads-statistics.py
|
exclude = build,venv,.tox,.git,.pytest_cache
|
||||||
ignore = E402,E501,E731,E741,W503
|
ignore = E402,E501,E731,E741,W503
|
||||||
|
max_line_length = 120
|
||||||
|
per_file_ignores =
|
||||||
|
devscripts/lazy_load_template.py: F401
|
||||||
|
|
||||||
|
|
||||||
|
[autoflake]
|
||||||
|
ignore-init-module-imports = true
|
||||||
|
ignore-pass-after-docstring = true
|
||||||
|
remove-all-unused-imports = true
|
||||||
|
remove-duplicate-keys = true
|
||||||
|
remove-unused-variables = true
|
||||||
|
|
||||||
|
|
||||||
|
[tool:pytest]
|
||||||
|
addopts = -ra -v --strict-markers
|
||||||
|
markers =
|
||||||
|
download
|
||||||
|
|
||||||
|
|
||||||
|
[tox:tox]
|
||||||
|
skipsdist = true
|
||||||
|
envlist = py{36,37,38,39,310,311},pypy{36,37,38,39}
|
||||||
|
skip_missing_interpreters = true
|
||||||
|
|
||||||
|
[testenv] # tox
|
||||||
|
deps =
|
||||||
|
pytest
|
||||||
|
commands = pytest {posargs:"-m not download"}
|
||||||
|
passenv = HOME # For test_compat_expanduser
|
||||||
|
setenv =
|
||||||
|
# PYTHONWARNINGS = error # Catches PIP's warnings too
|
||||||
|
|
||||||
|
|
||||||
|
[isort]
|
||||||
|
py_version = 37
|
||||||
|
multi_line_output = VERTICAL_HANGING_INDENT
|
||||||
|
line_length = 80
|
||||||
|
reverse_relative = true
|
||||||
|
ensure_newline_before_comments = true
|
||||||
|
include_trailing_comma = true
|
||||||
|
known_first_party =
|
||||||
|
test
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,21 @@
|
|||||||
|
import functools
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from yt_dlp.networking import RequestHandler
|
||||||
|
from yt_dlp.networking.common import _REQUEST_HANDLERS
|
||||||
|
from yt_dlp.utils._utils import _YDLLogger as FakeLogger
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def handler(request):
|
||||||
|
RH_KEY = request.param
|
||||||
|
if inspect.isclass(RH_KEY) and issubclass(RH_KEY, RequestHandler):
|
||||||
|
handler = RH_KEY
|
||||||
|
elif RH_KEY in _REQUEST_HANDLERS:
|
||||||
|
handler = _REQUEST_HANDLERS[RH_KEY]
|
||||||
|
else:
|
||||||
|
pytest.skip(f'{RH_KEY} request handler is not available')
|
||||||
|
|
||||||
|
return functools.partial(handler, logger=FakeLogger)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue