mirror of
https://github.com/yt-dlp/yt-dlp.git
synced 2024-10-04 08:11:25 +02:00
Compare commits
No commits in common. "f78814923748277e7067b796f25870686fb46205" and "fb44020fa98e47620b3aa1dab94b4c5b7bfb40bd" have entirely different histories.
f788149237
...
fb44020fa9
21
Makefile
21
Makefile
|
@ -38,13 +38,11 @@ MANDIR ?= $(PREFIX)/man
|
||||||
SHAREDIR ?= $(PREFIX)/share
|
SHAREDIR ?= $(PREFIX)/share
|
||||||
PYTHON ?= /usr/bin/env python3
|
PYTHON ?= /usr/bin/env python3
|
||||||
|
|
||||||
# $(shell) and $(error) are no-ops in BSD Make and the != variable assignment operator is not supported by GNU Make <4.0
|
# set SYSCONFDIR to /etc if PREFIX=/usr or PREFIX=/usr/local
|
||||||
VERSION_CHECK != echo supported
|
SYSCONFDIR = $(shell if [ $(PREFIX) = /usr -o $(PREFIX) = /usr/local ]; then echo /etc; else echo $(PREFIX)/etc; fi)
|
||||||
VERSION_CHECK ?= $(error GNU Make 4+ or BSD Make is required)
|
|
||||||
CHECK_VERSION := $(VERSION_CHECK)
|
|
||||||
|
|
||||||
# set markdown input format to "markdown-smart" for pandoc version 2+ and to "markdown" for pandoc prior to version 2
|
# set markdown input format to "markdown-smart" for pandoc version 2 and to "markdown" for pandoc prior to version 2
|
||||||
MARKDOWN != if [ "`pandoc -v | head -n1 | cut -d' ' -f2 | head -c1`" -ge "2" ]; then echo markdown-smart; else echo markdown; fi
|
MARKDOWN = $(shell if [ `pandoc -v | head -n1 | cut -d" " -f2 | head -c1` -ge "2" ]; then echo markdown-smart; else echo markdown; fi)
|
||||||
|
|
||||||
install: lazy-extractors yt-dlp yt-dlp.1 completions
|
install: lazy-extractors yt-dlp yt-dlp.1 completions
|
||||||
mkdir -p $(DESTDIR)$(BINDIR)
|
mkdir -p $(DESTDIR)$(BINDIR)
|
||||||
|
@ -75,17 +73,17 @@ test:
|
||||||
offlinetest: codetest
|
offlinetest: codetest
|
||||||
$(PYTHON) -m pytest -k "not download"
|
$(PYTHON) -m pytest -k "not download"
|
||||||
|
|
||||||
CODE_FOLDERS != find yt_dlp -type f -name '__init__.py' -exec dirname {} \+ | grep -v '/__' | sort
|
CODE_FOLDERS := $(shell find yt_dlp -type d -not -name '__*' -exec sh -c 'test -e "$$1"/__init__.py' sh {} \; -print)
|
||||||
CODE_FILES != for f in $(CODE_FOLDERS) ; do echo "$$f" | sed 's,$$,/*.py,' ; done
|
CODE_FILES := $(shell for f in $(CODE_FOLDERS); do echo "$$f" | awk '{gsub(/\/[^\/]+/,"/*"); print $$1"/*.py"}'; done | sort -u)
|
||||||
yt-dlp: $(CODE_FILES)
|
yt-dlp: $(CODE_FILES)
|
||||||
mkdir -p zip
|
mkdir -p zip
|
||||||
for d in $(CODE_FOLDERS) ; do \
|
for d in $(CODE_FOLDERS) ; do \
|
||||||
mkdir -p zip/$$d ;\
|
mkdir -p zip/$$d ;\
|
||||||
cp -pPR $$d/*.py zip/$$d/ ;\
|
cp -pPR $$d/*.py zip/$$d/ ;\
|
||||||
done
|
done
|
||||||
(cd zip && touch -t 200001010101 $(CODE_FILES))
|
cd zip ; touch -t 200001010101 $(CODE_FILES)
|
||||||
mv zip/yt_dlp/__main__.py zip/
|
mv zip/yt_dlp/__main__.py zip/
|
||||||
(cd zip && zip -q ../yt-dlp $(CODE_FILES) __main__.py)
|
cd zip ; zip -q ../yt-dlp $(CODE_FILES) __main__.py
|
||||||
rm -rf zip
|
rm -rf zip
|
||||||
echo '#!$(PYTHON)' > yt-dlp
|
echo '#!$(PYTHON)' > yt-dlp
|
||||||
cat yt-dlp.zip >> yt-dlp
|
cat yt-dlp.zip >> yt-dlp
|
||||||
|
@ -129,7 +127,7 @@ completions/fish/yt-dlp.fish: $(CODE_FILES) devscripts/fish-completion.in
|
||||||
mkdir -p completions/fish
|
mkdir -p completions/fish
|
||||||
$(PYTHON) devscripts/fish-completion.py
|
$(PYTHON) devscripts/fish-completion.py
|
||||||
|
|
||||||
_EXTRACTOR_FILES != find yt_dlp/extractor -name '*.py' -and -not -name 'lazy_extractors.py'
|
_EXTRACTOR_FILES = $(shell find yt_dlp/extractor -name '*.py' -and -not -name 'lazy_extractors.py')
|
||||||
yt_dlp/extractor/lazy_extractors.py: devscripts/make_lazy_extractors.py devscripts/lazy_load_template.py $(_EXTRACTOR_FILES)
|
yt_dlp/extractor/lazy_extractors.py: devscripts/make_lazy_extractors.py devscripts/lazy_load_template.py $(_EXTRACTOR_FILES)
|
||||||
$(PYTHON) devscripts/make_lazy_extractors.py $@
|
$(PYTHON) devscripts/make_lazy_extractors.py $@
|
||||||
|
|
||||||
|
@ -143,7 +141,6 @@ yt-dlp.tar.gz: all
|
||||||
--exclude '__pycache__' \
|
--exclude '__pycache__' \
|
||||||
--exclude '.pytest_cache' \
|
--exclude '.pytest_cache' \
|
||||||
--exclude '.git' \
|
--exclude '.git' \
|
||||||
--exclude '__pyinstaller' \
|
|
||||||
-- \
|
-- \
|
||||||
README.md supportedsites.md Changelog.md LICENSE \
|
README.md supportedsites.md Changelog.md LICENSE \
|
||||||
CONTRIBUTING.md Collaborators.md CONTRIBUTORS AUTHORS \
|
CONTRIBUTING.md Collaborators.md CONTRIBUTORS AUTHORS \
|
||||||
|
|
|
@ -500,7 +500,6 @@ class FacebookIE(InfoExtractor):
|
||||||
webpage, 'description', default=None)
|
webpage, 'description', default=None)
|
||||||
uploader_data = (
|
uploader_data = (
|
||||||
get_first(media, ('owner', {dict}))
|
get_first(media, ('owner', {dict}))
|
||||||
or get_first(post, ('video', 'creation_story', 'attachments', ..., 'media', lambda k, v: k == 'owner' and v['name']))
|
|
||||||
or get_first(post, (..., 'video', lambda k, v: k == 'owner' and v['name']))
|
or get_first(post, (..., 'video', lambda k, v: k == 'owner' and v['name']))
|
||||||
or get_first(post, ('node', 'actors', ..., {dict}))
|
or get_first(post, ('node', 'actors', ..., {dict}))
|
||||||
or get_first(post, ('event', 'event_creator', {dict})) or {})
|
or get_first(post, ('event', 'event_creator', {dict})) or {})
|
||||||
|
@ -584,8 +583,8 @@ class FacebookIE(InfoExtractor):
|
||||||
def extract_relay_prefetched_data(_filter):
|
def extract_relay_prefetched_data(_filter):
|
||||||
return traverse_obj(extract_relay_data(_filter), (
|
return traverse_obj(extract_relay_data(_filter), (
|
||||||
'require', (None, (..., ..., ..., '__bbox', 'require')),
|
'require', (None, (..., ..., ..., '__bbox', 'require')),
|
||||||
lambda _, v: any(key.startswith('RelayPrefetchedStreamCache') for key in v),
|
lambda _, v: 'RelayPrefetchedStreamCache' in v, ..., ...,
|
||||||
..., ..., '__bbox', 'result', 'data', {dict}), get_all=False) or {}
|
'__bbox', 'result', 'data', {dict}), get_all=False) or {}
|
||||||
|
|
||||||
if not video_data:
|
if not video_data:
|
||||||
server_js_data = self._parse_json(self._search_regex([
|
server_js_data = self._parse_json(self._search_regex([
|
||||||
|
|
|
@ -3,15 +3,16 @@ import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
ExtractorError,
|
clean_html,
|
||||||
extract_attributes,
|
extract_attributes,
|
||||||
|
ExtractorError,
|
||||||
float_or_none,
|
float_or_none,
|
||||||
|
get_element_by_class,
|
||||||
int_or_none,
|
int_or_none,
|
||||||
srt_subtitles_timecode,
|
srt_subtitles_timecode,
|
||||||
|
strip_or_none,
|
||||||
mimetype2ext,
|
mimetype2ext,
|
||||||
traverse_obj,
|
|
||||||
try_get,
|
try_get,
|
||||||
url_or_none,
|
|
||||||
urlencode_postdata,
|
urlencode_postdata,
|
||||||
urljoin,
|
urljoin,
|
||||||
)
|
)
|
||||||
|
@ -82,29 +83,15 @@ class LinkedInLearningBaseIE(LinkedInBaseIE):
|
||||||
|
|
||||||
|
|
||||||
class LinkedInIE(LinkedInBaseIE):
|
class LinkedInIE(LinkedInBaseIE):
|
||||||
_VALID_URL = r'https?://(?:www\.)?linkedin\.com/posts/[^/?#]+-(?P<id>\d+)-\w{4}/?(?:[?#]|$)'
|
_VALID_URL = r'https?://(?:www\.)?linkedin\.com/posts/.+?(?P<id>\d+)'
|
||||||
_TESTS = [{
|
_TESTS = [{
|
||||||
'url': 'https://www.linkedin.com/posts/mishalkhawaja_sendinblueviews-toronto-digitalmarketing-ugcPost-6850898786781339649-mM20',
|
'url': 'https://www.linkedin.com/posts/mishalkhawaja_sendinblueviews-toronto-digitalmarketing-ugcPost-6850898786781339649-mM20',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '6850898786781339649',
|
'id': '6850898786781339649',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Mishal K. on LinkedIn: #sendinblueviews #toronto #digitalmarketing #nowhiring #sendinblue…',
|
'title': 'Mishal K. on LinkedIn: #sendinblueviews #toronto #digitalmarketing',
|
||||||
'description': 'md5:2998a31f6f479376dd62831f53a80f71',
|
'description': 'md5:be125430bab1c574f16aeb186a4d5b19',
|
||||||
'uploader': 'Mishal K.',
|
'creator': 'Mishal K.'
|
||||||
'thumbnail': 're:^https?://media.licdn.com/dms/image/.*$',
|
|
||||||
'like_count': int
|
|
||||||
},
|
|
||||||
}, {
|
|
||||||
'url': 'https://www.linkedin.com/posts/the-mathworks_2_what-is-mathworks-cloud-center-activity-7151241570371948544-4Gu7',
|
|
||||||
'info_dict': {
|
|
||||||
'id': '7151241570371948544',
|
|
||||||
'ext': 'mp4',
|
|
||||||
'title': 'MathWorks on LinkedIn: What Is MathWorks Cloud Center?',
|
|
||||||
'description': 'md5:95f9d4eeb6337882fb47eefe13d7a40c',
|
|
||||||
'uploader': 'MathWorks',
|
|
||||||
'thumbnail': 're:^https?://media.licdn.com/dms/image/.*$',
|
|
||||||
'like_count': int,
|
|
||||||
'subtitles': 'mincount:1'
|
|
||||||
},
|
},
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
@ -112,30 +99,26 @@ class LinkedInIE(LinkedInBaseIE):
|
||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
webpage = self._download_webpage(url, video_id)
|
webpage = self._download_webpage(url, video_id)
|
||||||
|
|
||||||
video_attrs = extract_attributes(self._search_regex(r'(<video[^>]+>)', webpage, 'video'))
|
title = self._html_extract_title(webpage)
|
||||||
sources = self._parse_json(video_attrs['data-sources'], video_id)
|
description = clean_html(get_element_by_class('share-update-card__update-text', webpage))
|
||||||
|
like_count = int_or_none(get_element_by_class('social-counts-reactions__social-counts-numRections', webpage))
|
||||||
|
creator = strip_or_none(clean_html(get_element_by_class('comment__actor-name', webpage)))
|
||||||
|
|
||||||
|
sources = self._parse_json(extract_attributes(self._search_regex(r'(<video[^>]+>)', webpage, 'video'))['data-sources'], video_id)
|
||||||
formats = [{
|
formats = [{
|
||||||
'url': source['src'],
|
'url': source['src'],
|
||||||
'ext': mimetype2ext(source.get('type')),
|
'ext': mimetype2ext(source.get('type')),
|
||||||
'tbr': float_or_none(source.get('data-bitrate'), scale=1000),
|
'tbr': float_or_none(source.get('data-bitrate'), scale=1000),
|
||||||
} for source in sources]
|
} for source in sources]
|
||||||
subtitles = {'en': [{
|
|
||||||
'url': video_attrs['data-captions-url'],
|
|
||||||
'ext': 'vtt',
|
|
||||||
}]} if url_or_none(video_attrs.get('data-captions-url')) else {}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
'title': self._og_search_title(webpage, default=None) or self._html_extract_title(webpage),
|
'title': title,
|
||||||
'like_count': int_or_none(self._search_regex(
|
'like_count': like_count,
|
||||||
r'\bdata-num-reactions="(\d+)"', webpage, 'reactions', default=None)),
|
'creator': creator,
|
||||||
'uploader': traverse_obj(
|
|
||||||
self._yield_json_ld(webpage, video_id),
|
|
||||||
(lambda _, v: v['@type'] == 'SocialMediaPosting', 'author', 'name', {str}), get_all=False),
|
|
||||||
'thumbnail': self._og_search_thumbnail(webpage),
|
'thumbnail': self._og_search_thumbnail(webpage),
|
||||||
'description': self._og_search_description(webpage, default=None),
|
'description': description,
|
||||||
'subtitles': subtitles,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from .common import InfoExtractor
|
from .common import InfoExtractor
|
||||||
from ..networking import HEADRequest
|
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
clean_html,
|
clean_html,
|
||||||
determine_ext,
|
determine_ext,
|
||||||
|
@ -92,7 +91,7 @@ class RaiBaseIE(InfoExtractor):
|
||||||
self.raise_geo_restricted(countries=self._GEO_COUNTRIES, metadata_available=True)
|
self.raise_geo_restricted(countries=self._GEO_COUNTRIES, metadata_available=True)
|
||||||
|
|
||||||
if not audio_only and not is_live:
|
if not audio_only and not is_live:
|
||||||
formats.extend(self._create_http_urls(media_url, relinker_url, formats, video_id))
|
formats.extend(self._create_http_urls(media_url, relinker_url, formats))
|
||||||
|
|
||||||
return filter_dict({
|
return filter_dict({
|
||||||
'is_live': is_live,
|
'is_live': is_live,
|
||||||
|
@ -100,7 +99,7 @@ class RaiBaseIE(InfoExtractor):
|
||||||
'formats': formats,
|
'formats': formats,
|
||||||
})
|
})
|
||||||
|
|
||||||
def _create_http_urls(self, manifest_url, relinker_url, fmts, video_id):
|
def _create_http_urls(self, manifest_url, relinker_url, fmts):
|
||||||
_MANIFEST_REG = r'/(?P<id>\w+)(?:_(?P<quality>[\d\,]+))?(?:\.mp4)?(?:\.csmil)?/playlist\.m3u8'
|
_MANIFEST_REG = r'/(?P<id>\w+)(?:_(?P<quality>[\d\,]+))?(?:\.mp4)?(?:\.csmil)?/playlist\.m3u8'
|
||||||
_MP4_TMPL = '%s&overrideUserAgentRule=mp4-%s'
|
_MP4_TMPL = '%s&overrideUserAgentRule=mp4-%s'
|
||||||
_QUALITY = {
|
_QUALITY = {
|
||||||
|
@ -167,14 +166,6 @@ class RaiBaseIE(InfoExtractor):
|
||||||
'fps': 25,
|
'fps': 25,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Check if MP4 download is available
|
|
||||||
try:
|
|
||||||
self._request_webpage(
|
|
||||||
HEADRequest(_MP4_TMPL % (relinker_url, '*')), video_id, 'Checking MP4 availability')
|
|
||||||
except ExtractorError as e:
|
|
||||||
self.to_screen(f'{video_id}: MP4 direct download is not available: {e.cause}')
|
|
||||||
return []
|
|
||||||
|
|
||||||
# filter out single-stream formats
|
# filter out single-stream formats
|
||||||
fmts = [f for f in fmts
|
fmts = [f for f in fmts
|
||||||
if not f.get('vcodec') == 'none' and not f.get('acodec') == 'none']
|
if not f.get('vcodec') == 'none' and not f.get('acodec') == 'none']
|
||||||
|
|
Loading…
Reference in New Issue
Block a user