diff options
Diffstat (limited to 'youtube_dl')
| -rw-r--r-- | youtube_dl/__init__.py | 1 | ||||
| -rw-r--r-- | youtube_dl/extractor/common.py | 9 | ||||
| -rw-r--r-- | youtube_dl/extractor/youtube.py | 26 | ||||
| -rw-r--r-- | youtube_dl/options.py | 4 | 
4 files changed, 40 insertions, 0 deletions
diff --git a/youtube_dl/__init__.py b/youtube_dl/__init__.py index f5f064241..79b389840 100644 --- a/youtube_dl/__init__.py +++ b/youtube_dl/__init__.py @@ -355,6 +355,7 @@ def _real_main(argv=None):          'youtube_include_dash_manifest': opts.youtube_include_dash_manifest,          'encoding': opts.encoding,          'extract_flat': opts.extract_flat, +        'mark_watched': opts.mark_watched,          'merge_output_format': opts.merge_output_format,          'postprocessors': postprocessors,          'fixup': opts.fixup, diff --git a/youtube_dl/extractor/common.py b/youtube_dl/extractor/common.py index 3f16b1b9e..a7c700099 100644 --- a/youtube_dl/extractor/common.py +++ b/youtube_dl/extractor/common.py @@ -1620,6 +1620,15 @@ class InfoExtractor(object):      def _get_automatic_captions(self, *args, **kwargs):          raise NotImplementedError('This method must be implemented by subclasses') +    def mark_watched(self, *args, **kwargs): +        if (self._downloader.params.get('mark_watched', False) and +                (self._get_login_info()[0] is not None or +                    self._downloader.params.get('cookiefile') is not None)): +            self._mark_watched(*args, **kwargs) + +    def _mark_watched(self, *args, **kwargs): +        raise NotImplementedError('This method must be implemented by subclasses') +  class SearchInfoExtractor(InfoExtractor):      """ diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index ec90c2111..ba339b67d 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals  import itertools  import json  import os.path +import random  import re  import time  import traceback @@ -1046,6 +1047,29 @@ class YoutubeIE(YoutubeBaseInfoExtractor):              self._downloader.report_warning(err_msg)              return {} +    def _mark_watched(self, video_id, video_info): +        playback_url = video_info.get('videostats_playback_base_url', [None])[0] +        if not playback_url: +            return +        parsed_playback_url = compat_urlparse.urlparse(playback_url) +        qs = compat_urlparse.parse_qs(parsed_playback_url.query) + +        # cpn generation algorithm is reverse engineered from base.js. +        # In fact it works even with dummy cpn. +        CPN_ALPHABET = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_' +        cpn = ''.join((CPN_ALPHABET[random.randint(0, 256) & 63] for _ in range(0, 16))) + +        qs.update({ +            'ver': ['2'], +            'cpn': [cpn], +        }) +        playback_url = compat_urlparse.urlunparse( +            parsed_playback_url._replace(query=compat_urllib_parse.urlencode(qs, True))) + +        self._download_webpage( +            playback_url, video_id, 'Marking watched', +            'Unable to mark watched', fatal=False) +      @classmethod      def extract_id(cls, url):          mobj = re.match(cls._VALID_URL, url, re.VERBOSE) @@ -1555,6 +1579,8 @@ class YoutubeIE(YoutubeBaseInfoExtractor):          self._sort_formats(formats) +        self.mark_watched(video_id, video_info) +          return {              'id': video_id,              'uploader': video_uploader, diff --git a/youtube_dl/options.py b/youtube_dl/options.py index 3afa8bb6f..048dee881 100644 --- a/youtube_dl/options.py +++ b/youtube_dl/options.py @@ -171,6 +171,10 @@ def parseOpts(overrideArguments=None):          default=False,          help='Do not extract the videos of a playlist, only list them.')      general.add_option( +        '--mark-watched', +        action='store_true', dest='mark_watched', default=False, +        help='Mark videos watched (YouTube only)') +    general.add_option(          '--no-color', '--no-colors',          action='store_true', dest='no_color',          default=False,  | 
