diff --git a/youtube_dl/extractor/francetv.py b/youtube_dl/extractor/francetv.py index ca41872db..58aecd941 100644 --- a/youtube_dl/extractor/francetv.py +++ b/youtube_dl/extractor/francetv.py @@ -48,24 +48,36 @@ class PluzzIE(FranceTVBaseInfoExtractor): class FranceTvInfoIE(FranceTVBaseInfoExtractor): IE_NAME = 'francetvinfo.fr' - _VALID_URL = r'https?://www\.francetvinfo\.fr/replay.*/(?P.+)\.html' + _VALID_URL = r'https?://www\.francetvinfo\.fr/.*/(?P<title>.+)\.html' - _TEST = { + _TESTS = [{ 'url': 'http://www.francetvinfo.fr/replay-jt/france-3/soir-3/jt-grand-soir-3-lundi-26-aout-2013_393427.html', - 'file': '84981923.mp4', 'info_dict': { + 'id': '84981923', + 'ext': 'mp4', 'title': 'Soir 3', }, 'params': { 'skip_download': True, }, - } + }, { + 'url': 'http://www.francetvinfo.fr/elections/europeennes/direct-europeennes-regardez-le-debat-entre-les-candidats-a-la-presidence-de-la-commission_600639.html', + 'info_dict': { + 'id': 'EV_20019', + 'ext': 'mp4', + 'title': 'Débat des candidats à la Commission européenne', + 'description': 'Débat des candidats à la Commission européenne', + }, + 'params': { + 'skip_download': 'HLS (reqires ffmpeg)' + } + }] def _real_extract(self, url): mobj = re.match(self._VALID_URL, url) page_title = mobj.group('title') webpage = self._download_webpage(url, page_title) - video_id = self._search_regex(r'id-video=(\d+?)[@"]', webpage, 'video id') + video_id = self._search_regex(r'id-video=((?:[^0-9]*?_)?[0-9]+)[@"]', webpage, 'video id') return self._extract_video(video_id) diff --git a/youtube_dl/extractor/generic.py b/youtube_dl/extractor/generic.py index 1ae55dc5a..286133282 100644 --- a/youtube_dl/extractor/generic.py +++ b/youtube_dl/extractor/generic.py @@ -672,7 +672,7 @@ class GenericIE(InfoExtractor): # HTML5 video found = re.findall(r'(?s)<video[^<]*(?:>.*?<source.*?)? src="([^"]+)"', webpage) if not found: - found = re.findall( + found = re.search( r'(?i)<meta\s+(?=(?:[a-z-]+="[^"]+"\s+)*http-equiv="refresh")' r'(?:[a-z-]+="[^"]+"\s+)*?content="[0-9]{,2};url=\'([^\']+)\'"', webpage) diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index 64f68a243..7ad0a718b 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -1138,6 +1138,10 @@ class YoutubeIE(YoutubeBaseInfoExtractor, SubtitlesInfoExtractor): # upload date upload_date = None mobj = re.search(r'(?s)id="eow-date.*?>(.*?)</span>', video_webpage) + if mobj is None: + mobj = re.search( + r'(?s)id="watch-uploader-info".*?>.*?Published on (.*?)</strong>', + video_webpage) if mobj is not None: upload_date = ' '.join(re.sub(r'[/,-]', r' ', mobj.group(1)).split()) upload_date = unified_strdate(upload_date) @@ -1771,9 +1775,12 @@ class YoutubeFeedsInfoExtractor(YoutubeBaseInfoExtractor): feed_entries.extend( self.url_result(video_id, 'Youtube', video_id=video_id) for video_id in ids) - if info['paging'] is None: + mobj = re.search( + r'data-uix-load-more-href="/?[^"]+paging=(?P<paging>\d+)', + feed_html) + if mobj is None: break - paging = info['paging'] + paging = mobj.group('paging') return self.playlist_result(feed_entries, playlist_title=self._PLAYLIST_TITLE) class YoutubeSubscriptionsIE(YoutubeFeedsInfoExtractor): diff --git a/youtube_dl/postprocessor/ffmpeg.py b/youtube_dl/postprocessor/ffmpeg.py index 602e370f4..45328ed43 100644 --- a/youtube_dl/postprocessor/ffmpeg.py +++ b/youtube_dl/postprocessor/ffmpeg.py @@ -9,6 +9,7 @@ from .common import AudioConversionError, PostProcessor from ..utils import ( check_executable, compat_subprocess_get_DEVNULL, + encodeArgument, encodeFilename, PostProcessingError, prepend_extension, @@ -48,7 +49,7 @@ class FFmpegPostProcessor(PostProcessor): for path in input_paths: files_cmd.extend(['-i', encodeFilename(path, True)]) cmd = ([self._get_executable(), '-y'] + files_cmd - + opts + + + [encodeArgument(o) for o in opts] + [encodeFilename(self._ffmpeg_filename_argument(out_path), True)]) if self._downloader.params.get('verbose', False): diff --git a/youtube_dl/postprocessor/xattrpp.py b/youtube_dl/postprocessor/xattrpp.py index 18979241c..f6940940b 100644 --- a/youtube_dl/postprocessor/xattrpp.py +++ b/youtube_dl/postprocessor/xattrpp.py @@ -6,6 +6,7 @@ from .common import PostProcessor from ..utils import ( check_executable, hyphenate_date, + subprocess_check_output ) @@ -57,7 +58,7 @@ class XAttrMetadataPP(PostProcessor): elif user_has_xattr: cmd = ['xattr', '-w', key, value, path] - subprocess.check_output(cmd) + subprocess_check_output(cmd) else: # On Unix, and can't find pyxattr, setfattr, or xattr. diff --git a/youtube_dl/utils.py b/youtube_dl/utils.py index 5e0ea866c..883ef90a4 100644 --- a/youtube_dl/utils.py +++ b/youtube_dl/utils.py @@ -540,6 +540,16 @@ def encodeFilename(s, for_subprocess=False): encoding = 'utf-8' return s.encode(encoding, 'ignore') + +def encodeArgument(s): + if not isinstance(s, compat_str): + # Legacy code that uses byte strings + # Uncomment the following line after fixing all post processors + #assert False, 'Internal error: %r should be of type %r, is %r' % (s, compat_str, type(s)) + s = s.decode('ascii') + return encodeFilename(s, True) + + def decodeOption(optval): if optval is None: return optval @@ -1429,3 +1439,15 @@ def qualities(quality_ids): DEFAULT_OUTTMPL = '%(title)s-%(id)s.%(ext)s' + +try: + subprocess_check_output = subprocess.check_output +except AttributeError: + def subprocess_check_output(*args, **kwargs): + assert 'input' not in kwargs + p = subprocess.Popen(*args, stdout=subprocess.PIPE, **kwargs) + output, _ = p.communicate() + ret = p.poll() + if ret: + raise subprocess.CalledProcessError(ret, p.args, output=output) + return output diff --git a/youtube_dl/version.py b/youtube_dl/version.py index fc78be37d..04a04f579 100644 --- a/youtube_dl/version.py +++ b/youtube_dl/version.py @@ -1,2 +1,2 @@ -__version__ = '2014.05.13' +__version__ = '2014.05.16.1'