From c90996937f371301ccd17e2c75a2207717261ba9 Mon Sep 17 00:00:00 2001 From: remitamine Date: Thu, 5 May 2016 21:40:19 +0100 Subject: [PATCH 1/3] [common] introduce chapters field --- youtube_dl/extractor/common.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/youtube_dl/extractor/common.py b/youtube_dl/extractor/common.py index 0843d89af..9e765eca3 100644 --- a/youtube_dl/extractor/common.py +++ b/youtube_dl/extractor/common.py @@ -211,6 +211,10 @@ class InfoExtractor(object): specified in the URL. end_time: Time in seconds where the reproduction should end, as specified in the URL. + chapters: A list of dictionaries, with the following entries: + * "start_time" - The start time of the chapter in seconds + * "end_time" - The end time of the chapter in seconds + * "title" (optional, string) The following fields should only be used when the video belongs to some logical chapter or section: From 726aefb27a2d2804feac14966a01d82f30fff303 Mon Sep 17 00:00:00 2001 From: remitamine Date: Thu, 5 May 2016 21:41:30 +0100 Subject: [PATCH 2/3] [ffmpeg] add support for chapters field postprocessing --- youtube_dl/postprocessor/ffmpeg.py | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/youtube_dl/postprocessor/ffmpeg.py b/youtube_dl/postprocessor/ffmpeg.py index fa99b0c2a..b29c5be86 100644 --- a/youtube_dl/postprocessor/ffmpeg.py +++ b/youtube_dl/postprocessor/ffmpeg.py @@ -4,6 +4,7 @@ import io import os import subprocess import time +import re from .common import AudioConversionError, PostProcessor @@ -22,6 +23,7 @@ from ..utils import ( subtitles_filename, dfxp2srt, ISO639Utils, + replace_extension, ) @@ -420,17 +422,36 @@ class FFmpegMetadataPP(FFmpegPostProcessor): filename = info['filepath'] temp_filename = prepend_extension(filename, 'temp') + metadata_filename = replace_extension(filename, 'meta') + + options = ['-map_metadata', '1'] if info['ext'] == 'm4a': - options = ['-vn', '-acodec', 'copy'] + options.extend(['-vn', '-acodec', 'copy']) else: - options = ['-c', 'copy'] + options.extend(['-c', 'copy']) + def ffmpeg_escape(text): + return re.sub(r'(=|;|#|\\|\n)', r'\\\1', text) + + metadata_file_content = ';FFMETADATA1\n' for (name, value) in metadata.items(): - options.extend(['-metadata', '%s=%s' % (name, value)]) + metadata_file_content += '%s=%s\n' % (name, ffmpeg_escape(value)) + + for chapter in info.get('chapters', []): + metadata_file_content += '[CHAPTER]\nTIMEBASE=1/1000\n' + metadata_file_content += 'START=%d\n' % (chapter['start_time'] * 1000) + metadata_file_content += 'END=%d\n' % (chapter['end_time'] * 1000) + chapter_title = chapter.get('title') + if chapter_title: + metadata_file_content += 'title=%s\n' % ffmpeg_escape(chapter_title) + + with io.open(metadata_filename, 'wt', encoding='utf-8') as f: + f.write(metadata_file_content) self._downloader.to_screen('[ffmpeg] Adding metadata to \'%s\'' % filename) - self.run_ffmpeg(filename, temp_filename, options) + self.run_ffmpeg_multiple_files([filename, metadata_filename], temp_filename, options) + os.remove(encodeFilename(metadata_filename)) os.remove(encodeFilename(filename)) os.rename(encodeFilename(temp_filename), encodeFilename(filename)) return [], info From 7b7687fc588494b261bf957c8dfe1f7874042d61 Mon Sep 17 00:00:00 2001 From: remitamine Date: Thu, 5 May 2016 21:42:37 +0100 Subject: [PATCH 3/3] [pbs] extract chapters information --- youtube_dl/extractor/pbs.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/youtube_dl/extractor/pbs.py b/youtube_dl/extractor/pbs.py index 81918ac6e..6a5fbb5c7 100644 --- a/youtube_dl/extractor/pbs.py +++ b/youtube_dl/extractor/pbs.py @@ -9,6 +9,7 @@ from ..utils import ( ExtractorError, determine_ext, int_or_none, + float_or_none, js_to_json, strip_jsonp, unified_strdate, @@ -459,6 +460,7 @@ class PBSIE(InfoExtractor): if not isinstance(e.cause, compat_HTTPError) or e.cause.code != 404: raise + chapters = [] # Player pages may also serve different qualities for page in ('widget/partnerplayer', 'portalplayer'): player = self._download_webpage( @@ -474,6 +476,21 @@ class PBSIE(InfoExtractor): extract_redirect_urls(video_info) if not info: info = video_info + if not chapters: + chapters_data = re.findall(r'(?s)chapters\.push\(({.*?})\)', player) or [] + for chapter_data in chapters_data: + chapter = self._parse_json(chapter_data, video_id, js_to_json, fatal=False) + if not chapter: + continue + start_time = float_or_none(chapter.get('start_time'), 1000) + duration = float_or_none(chapter.get('duration'), 1000) + if start_time is None or duration is None: + continue + chapters.append({ + 'start_time': start_time, + 'end_time': start_time + duration, + 'title': chapter.get('title'), + }) formats = [] http_url = None @@ -568,4 +585,5 @@ class PBSIE(InfoExtractor): 'upload_date': upload_date, 'formats': formats, 'subtitles': subtitles, + 'chapters': chapters, }