diff options
Diffstat (limited to 'devscripts')
| -rwxr-xr-x | devscripts/bash-completion.py | 1 | ||||
| -rw-r--r-- | devscripts/buildserver.py | 75 | ||||
| -rw-r--r-- | devscripts/create-github-release.py | 120 | ||||
| -rwxr-xr-x | devscripts/fish-completion.py | 1 | ||||
| -rw-r--r-- | devscripts/generate_aes_testdata.py | 1 | ||||
| -rwxr-xr-x | devscripts/gh-pages/generate-download.py | 4 | ||||
| -rwxr-xr-x | devscripts/gh-pages/update-sites.py | 1 | ||||
| -rw-r--r-- | devscripts/lazy_load_template.py | 19 | ||||
| -rwxr-xr-x | devscripts/make_contributing.py | 1 | ||||
| -rw-r--r-- | devscripts/make_lazy_extractors.py | 99 | ||||
| -rw-r--r-- | devscripts/make_supportedsites.py | 1 | ||||
| -rw-r--r-- | devscripts/prepare_manpage.py | 86 | ||||
| -rwxr-xr-x | devscripts/release.sh | 61 | ||||
| -rw-r--r-- | devscripts/show-downloads-statistics.py | 47 | ||||
| -rwxr-xr-x | devscripts/zsh-completion.py | 1 | 
15 files changed, 442 insertions, 76 deletions
diff --git a/devscripts/bash-completion.py b/devscripts/bash-completion.py index ce68f26f9..3d1391334 100755 --- a/devscripts/bash-completion.py +++ b/devscripts/bash-completion.py @@ -25,5 +25,6 @@ def build_completion(opt_parser):          filled_template = template.replace("{{flags}}", " ".join(opts_flag))          f.write(filled_template) +  parser = youtube_dl.parseOpts()[0]  build_completion(parser) diff --git a/devscripts/buildserver.py b/devscripts/buildserver.py index 7c2f49f8b..fc99c3213 100644 --- a/devscripts/buildserver.py +++ b/devscripts/buildserver.py @@ -1,17 +1,38 @@  #!/usr/bin/python3 -from http.server import HTTPServer, BaseHTTPRequestHandler -from socketserver import ThreadingMixIn  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 youtube_dl.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 -class BuildHTTPServer(ThreadingMixIn, HTTPServer): +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 @@ -191,7 +212,7 @@ def main(args=None):                          action='store_const', dest='action', const='service',                          help='Run as a Windows service')      parser.add_argument('-b', '--bind', metavar='<host:port>', -                        action='store', default='localhost:8142', +                        action='store', default='0.0.0.0:8142',                          help='Bind to host:port (default %default)')      options = parser.parse_args(args=args) @@ -216,7 +237,7 @@ def main(args=None):      srv = BuildHTTPServer((host, port), BuildHTTPRequestHandler)      thr = threading.Thread(target=srv.serve_forever)      thr.start() -    input('Press ENTER to shut down') +    compat_input('Press ENTER to shut down')      srv.shutdown()      thr.join() @@ -231,8 +252,6 @@ def rmtree(path):              os.remove(fname)      os.rmdir(path) -#============================================================================== -  class BuildError(Exception):      def __init__(self, output, code=500): @@ -249,15 +268,25 @@ class HTTPError(BuildError):  class PythonBuilder(object):      def __init__(self, **kwargs): -        pythonVersion = kwargs.pop('python', '2.7') -        try: -            key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, r'SOFTWARE\Python\PythonCore\%s\InstallPath' % pythonVersion) +        python_version = kwargs.pop('python', '3.4') +        python_path = None +        for node in ('Wow6432Node\\', ''):              try: -                self.pythonPath, _ = _winreg.QueryValueEx(key, '') -            finally: -                _winreg.CloseKey(key) -        except Exception: -            raise BuildError('No such Python version: %s' % pythonVersion) +                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) @@ -305,8 +334,10 @@ class YoutubeDLBuilder(object):      def build(self):          try: -            subprocess.check_output([os.path.join(self.pythonPath, 'python.exe'), 'setup.py', 'py2exe'], -                                    cwd=self.buildPath) +            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) @@ -369,12 +400,12 @@ class Builder(PythonBuilder, GITBuilder, YoutubeDLBuilder, DownloadBuilder, Clea      pass -class BuildHTTPRequestHandler(BaseHTTPRequestHandler): +class BuildHTTPRequestHandler(compat_http_server.BaseHTTPRequestHandler):      actionDict = {'build': Builder, 'download': Builder}  # They're the same, no more caching.      def do_GET(self): -        path = urlparse.urlparse(self.path) -        paramDict = dict([(key, value[0]) for key, value in urlparse.parse_qs(path.query).items()]) +        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('/') @@ -388,7 +419,7 @@ class BuildHTTPRequestHandler(BaseHTTPRequestHandler):                          builder.close()                  except BuildError as e:                      self.send_response(e.code) -                    msg = unicode(e).encode('UTF-8') +                    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() @@ -400,7 +431,5 @@ class BuildHTTPRequestHandler(BaseHTTPRequestHandler):          else:              self.send_response(500, 'Malformed URL') -#============================================================================== -  if __name__ == '__main__':      main() diff --git a/devscripts/create-github-release.py b/devscripts/create-github-release.py new file mode 100644 index 000000000..30716ad8e --- /dev/null +++ b/devscripts/create-github-release.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python +from __future__ import unicode_literals + +import base64 +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 youtube_dl.compat import ( +    compat_basestring, +    compat_input, +    compat_getpass, +    compat_print, +    compat_urllib_request, +) +from youtube_dl.utils import ( +    make_HTTPS_handler, +    sanitized_Request, +) + + +class GitHubReleaser(object): +    _API_URL = 'https://api.github.com/repos/rg3/youtube-dl/releases' +    _UPLOADS_URL = 'https://uploads.github.com/repos/rg3/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._username = info[0] +                self._password = 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._username = compat_input( +            'Type your GitHub username or email address and press [Return]: ') +        self._password = compat_getpass( +            'Type your GitHub password and press [Return]: ') + +    def _call(self, req): +        if isinstance(req, compat_basestring): +            req = sanitized_Request(req) +        # Authorizing manually since GitHub does not response with 401 with +        # WWW-Authenticate header set (see +        # https://developer.github.com/v3/#basic-authentication) +        b64 = base64.b64encode( +            ('%s:%s' % (self._username, self._password)).encode('utf-8')).decode('ascii') +        req.add_header('Authorization', 'Basic %s' % b64) +        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='youtube-dl %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() diff --git a/devscripts/fish-completion.py b/devscripts/fish-completion.py index 41629d87d..51d19dd33 100755 --- a/devscripts/fish-completion.py +++ b/devscripts/fish-completion.py @@ -44,5 +44,6 @@ def build_completion(opt_parser):      with open(FISH_COMPLETION_FILE, 'w') as f:          f.write(filled_template) +  parser = youtube_dl.parseOpts()[0]  build_completion(parser) diff --git a/devscripts/generate_aes_testdata.py b/devscripts/generate_aes_testdata.py index 2e389fc8e..e3df42cc2 100644 --- a/devscripts/generate_aes_testdata.py +++ b/devscripts/generate_aes_testdata.py @@ -23,6 +23,7 @@ def openssl_encode(algo, key, iv):      out, _ = prog.communicate(secret_msg)      return out +  iv = key = [0x20, 0x15] + 14 * [0]  r = openssl_encode('aes-128-cbc', key, iv) diff --git a/devscripts/gh-pages/generate-download.py b/devscripts/gh-pages/generate-download.py index 392e3ba21..fcd7e1dff 100755 --- a/devscripts/gh-pages/generate-download.py +++ b/devscripts/gh-pages/generate-download.py @@ -15,13 +15,9 @@ data = urllib.request.urlopen(URL).read()  with open('download.html.in', 'r', encoding='utf-8') as tmplf:      template = tmplf.read() -md5sum = hashlib.md5(data).hexdigest() -sha1sum = hashlib.sha1(data).hexdigest()  sha256sum = hashlib.sha256(data).hexdigest()  template = template.replace('@PROGRAM_VERSION@', version)  template = template.replace('@PROGRAM_URL@', URL) -template = template.replace('@PROGRAM_MD5SUM@', md5sum) -template = template.replace('@PROGRAM_SHA1SUM@', sha1sum)  template = template.replace('@PROGRAM_SHA256SUM@', sha256sum)  template = template.replace('@EXE_URL@', versions_info['versions'][version]['exe'][0])  template = template.replace('@EXE_SHA256SUM@', versions_info['versions'][version]['exe'][1]) diff --git a/devscripts/gh-pages/update-sites.py b/devscripts/gh-pages/update-sites.py index 503c1372f..531c93c70 100755 --- a/devscripts/gh-pages/update-sites.py +++ b/devscripts/gh-pages/update-sites.py @@ -32,5 +32,6 @@ def main():      with open('supportedsites.html', 'w', encoding='utf-8') as sitesf:          sitesf.write(template) +  if __name__ == '__main__':      main() diff --git a/devscripts/lazy_load_template.py b/devscripts/lazy_load_template.py new file mode 100644 index 000000000..c4e5fc1f4 --- /dev/null +++ b/devscripts/lazy_load_template.py @@ -0,0 +1,19 @@ +# coding: utf-8 +from __future__ import unicode_literals + +import re + + +class LazyLoadExtractor(object): +    _module = None + +    @classmethod +    def ie_key(cls): +        return cls.__name__[:-2] + +    def __new__(cls, *args, **kwargs): +        mod = __import__(cls._module, fromlist=(cls.__name__,)) +        real_cls = getattr(mod, cls.__name__) +        instance = real_cls.__new__(real_cls) +        instance.__init__(*args, **kwargs) +        return instance diff --git a/devscripts/make_contributing.py b/devscripts/make_contributing.py index 5e454a429..226d1a5d6 100755 --- a/devscripts/make_contributing.py +++ b/devscripts/make_contributing.py @@ -28,5 +28,6 @@ def main():      with io.open(outfile, 'w', encoding='utf-8') as outf:          outf.write(out) +  if __name__ == '__main__':      main() diff --git a/devscripts/make_lazy_extractors.py b/devscripts/make_lazy_extractors.py new file mode 100644 index 000000000..19114d30d --- /dev/null +++ b/devscripts/make_lazy_extractors.py @@ -0,0 +1,99 @@ +from __future__ import unicode_literals, print_function + +from inspect import getsource +import os +from os.path import dirname as dirn +import sys + +print('WARNING: Lazy loading extractors is an experimental feature that may not always work', file=sys.stderr) + +sys.path.insert(0, dirn(dirn((os.path.abspath(__file__))))) + +lazy_extractors_filename = sys.argv[1] +if os.path.exists(lazy_extractors_filename): +    os.remove(lazy_extractors_filename) + +from youtube_dl.extractor import _ALL_CLASSES +from youtube_dl.extractor.common import InfoExtractor, SearchInfoExtractor + +with open('devscripts/lazy_load_template.py', 'rt') as f: +    module_template = f.read() + +module_contents = [ +    module_template + '\n' + getsource(InfoExtractor.suitable) + '\n', +    'class LazyLoadSearchExtractor(LazyLoadExtractor):\n    pass\n'] + +ie_template = ''' +class {name}({bases}): +    _VALID_URL = {valid_url!r} +    _module = '{module}' +''' + +make_valid_template = ''' +    @classmethod +    def _make_valid_url(cls): +        return {valid_url!r} +''' + + +def get_base_name(base): +    if base is InfoExtractor: +        return 'LazyLoadExtractor' +    elif base is SearchInfoExtractor: +        return 'LazyLoadSearchExtractor' +    else: +        return base.__name__ + + +def build_lazy_ie(ie, name): +    valid_url = getattr(ie, '_VALID_URL', None) +    s = ie_template.format( +        name=name, +        bases=', '.join(map(get_base_name, ie.__bases__)), +        valid_url=valid_url, +        module=ie.__module__) +    if ie.suitable.__func__ is not InfoExtractor.suitable.__func__: +        s += '\n' + getsource(ie.suitable) +    if hasattr(ie, '_make_valid_url'): +        # search extractors +        s += make_valid_template.format(valid_url=ie._make_valid_url()) +    return s + + +# find the correct sorting and add the required base classes so that sublcasses +# can be correctly created +classes = _ALL_CLASSES[:-1] +ordered_cls = [] +while classes: +    for c in classes[:]: +        bases = set(c.__bases__) - set((object, InfoExtractor, SearchInfoExtractor)) +        stop = False +        for b in bases: +            if b not in classes and b not in ordered_cls: +                if b.__name__ == 'GenericIE': +                    exit() +                classes.insert(0, b) +                stop = True +        if stop: +            break +        if all(b in ordered_cls for b in bases): +            ordered_cls.append(c) +            classes.remove(c) +            break +ordered_cls.append(_ALL_CLASSES[-1]) + +names = [] +for ie in ordered_cls: +    name = ie.__name__ +    src = build_lazy_ie(ie, name) +    module_contents.append(src) +    if ie in _ALL_CLASSES: +        names.append(name) + +module_contents.append( +    '_ALL_CLASSES = [{0}]'.format(', '.join(names))) + +module_src = '\n'.join(module_contents) + '\n' + +with open(lazy_extractors_filename, 'wt') as f: +    f.write(module_src) diff --git a/devscripts/make_supportedsites.py b/devscripts/make_supportedsites.py index 8cb4a4638..764795bc5 100644 --- a/devscripts/make_supportedsites.py +++ b/devscripts/make_supportedsites.py @@ -41,5 +41,6 @@ def main():      with io.open(outfile, 'w', encoding='utf-8') as outf:          outf.write(out) +  if __name__ == '__main__':      main() diff --git a/devscripts/prepare_manpage.py b/devscripts/prepare_manpage.py index 776e6556e..f9fe63f1f 100644 --- a/devscripts/prepare_manpage.py +++ b/devscripts/prepare_manpage.py @@ -1,13 +1,46 @@  from __future__ import unicode_literals  import io +import optparse  import os.path -import sys  import re  ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))  README_FILE = os.path.join(ROOT_DIR, 'README.md') +PREFIX = '''%YOUTUBE-DL(1) + +# NAME + +youtube\-dl \- download videos from youtube.com or other video platforms + +# SYNOPSIS + +**youtube-dl** \[OPTIONS\] URL [URL...] + +''' + + +def main(): +    parser = optparse.OptionParser(usage='%prog OUTFILE.md') +    options, args = parser.parse_args() +    if len(args) != 1: +        parser.error('Expected an output filename') + +    outfile, = args + +    with io.open(README_FILE, encoding='utf-8') as f: +        readme = f.read() + +    readme = re.sub(r'(?s)^.*?(?=# DESCRIPTION)', '', readme) +    readme = re.sub(r'\s+youtube-dl \[OPTIONS\] URL \[URL\.\.\.\]', '', readme) +    readme = PREFIX + readme + +    readme = filter_options(readme) + +    with io.open(outfile, 'w', encoding='utf-8') as outf: +        outf.write(readme) +  def filter_options(readme):      ret = '' @@ -21,43 +54,26 @@ def filter_options(readme):          if in_options:              if line.lstrip().startswith('-'): -                option, description = re.split(r'\s{2,}', line.lstrip()) -                split_option = option.split(' ') - -                if not split_option[-1].startswith('-'):  # metavar -                    option = ' '.join(split_option[:-1] + ['*%s*' % split_option[-1]]) - -                # Pandoc's definition_lists. See http://pandoc.org/README.html -                # for more information. -                ret += '\n%s\n:   %s\n' % (option, description) -            else: -                ret += line.lstrip() + '\n' +                split = re.split(r'\s{2,}', line.lstrip()) +                # Description string may start with `-` as well. If there is +                # only one piece then it's a description bit not an option. +                if len(split) > 1: +                    option, description = split +                    split_option = option.split(' ') + +                    if not split_option[-1].startswith('-'):  # metavar +                        option = ' '.join(split_option[:-1] + ['*%s*' % split_option[-1]]) + +                    # Pandoc's definition_lists. See http://pandoc.org/README.html +                    # for more information. +                    ret += '\n%s\n:   %s\n' % (option, description) +                    continue +            ret += line.lstrip() + '\n'          else:              ret += line + '\n'      return ret -with io.open(README_FILE, encoding='utf-8') as f: -    readme = f.read() - -PREFIX = '''%YOUTUBE-DL(1) - -# NAME - -youtube\-dl \- download videos from youtube.com or other video platforms - -# SYNOPSIS - -**youtube-dl** \[OPTIONS\] URL [URL...] - -''' -readme = re.sub(r'(?s)^.*?(?=# DESCRIPTION)', '', readme) -readme = re.sub(r'\s+youtube-dl \[OPTIONS\] URL \[URL\.\.\.\]', '', readme) -readme = PREFIX + readme - -readme = filter_options(readme) -if sys.version_info < (3, 0): -    print(readme.encode('utf-8')) -else: -    print(readme) +if __name__ == '__main__': +    main() diff --git a/devscripts/release.sh b/devscripts/release.sh index 6718ce39b..4db5def5d 100755 --- a/devscripts/release.sh +++ b/devscripts/release.sh @@ -6,7 +6,7 @@  # * the git config user.signingkey is properly set  # You will need -# pip install coverage nose rsa +# pip install coverage nose rsa wheel  # TODO  # release notes @@ -15,10 +15,33 @@  set -e  skip_tests=true -if [ "$1" = '--run-tests' ]; then -    skip_tests=false -    shift -fi +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" @@ -33,6 +56,12 @@ if [ ! -z "`git status --porcelain | grep -v CHANGELOG`" ]; then echo 'ERROR: th  useless_files=$(find youtube_dl -type f -not -name '*.py')  if [ ! -z "$useless_files" ]; then echo "ERROR: Non-.py files in youtube_dl: $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 @@ -45,10 +74,13 @@ fi  /bin/echo -e "\n### Changing version in version.py..."  sed -i "s/__version__ = '.*'/__version__ = '$version'/" youtube_dl/version.py +/bin/echo -e "\n### Changing version in ChangeLog..." +sed -i "s/<unreleased>/$version/" ChangeLog +  /bin/echo -e "\n### Committing documentation, templates and youtube_dl/version.py..." -make README.md CONTRIBUTING.md ISSUE_TEMPLATE.md supportedsites -git add README.md CONTRIBUTING.md .github/ISSUE_TEMPLATE.md docs/supportedsites.md youtube_dl/version.py -git commit -m "release $version" +make README.md CONTRIBUTING.md .github/ISSUE_TEMPLATE.md supportedsites +git add README.md CONTRIBUTING.md .github/ISSUE_TEMPLATE.md docs/supportedsites.md youtube_dl/version.py ChangeLog +git commit $gpg_sign_commits -m "release $version"  /bin/echo -e "\n### Now tagging, signing and pushing..."  git tag -s -m "Release $version" "$version" @@ -64,7 +96,7 @@ git push origin "$version"  REV=$(git rev-parse HEAD)  make youtube-dl youtube-dl.tar.gz  read -p "VM running? (y/n) " -n 1 -wget "http://localhost:8142/build/rg3/youtube-dl/youtube-dl.exe?rev=$REV" -O youtube-dl.exe +wget "http://$buildserver/build/rg3/youtube-dl/youtube-dl.exe?rev=$REV" -O youtube-dl.exe  mkdir -p "build/$version"  mv youtube-dl youtube-dl.exe "build/$version"  mv youtube-dl.tar.gz "build/$version/youtube-dl-$version.tar.gz" @@ -74,15 +106,16 @@ RELEASE_FILES="youtube-dl youtube-dl.exe youtube-dl-$version.tar.gz"  (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 yt-dl.org ..." +/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 -scp -r "build/$version" ytdl@yt-dl.org:html/tmp/ -ssh ytdl@yt-dl.org "mv html/tmp/$version html/downloads/" + +ROOT=$(pwd) +python devscripts/create-github-release.py ChangeLog $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 -ROOT=$(pwd)  (      set -e      ORIGIN_URL=$(git config --get remote.origin.url) @@ -94,7 +127,7 @@ ROOT=$(pwd)      "$ROOT/devscripts/gh-pages/update-copyright.py"      "$ROOT/devscripts/gh-pages/update-sites.py"      git add *.html *.html.in update -    git commit -m "release $version" +    git commit $gpg_sign_commits -m "release $version"      git push "$ROOT" gh-pages      git push "$ORIGIN_URL" gh-pages  ) diff --git a/devscripts/show-downloads-statistics.py b/devscripts/show-downloads-statistics.py new file mode 100644 index 000000000..e25d28411 --- /dev/null +++ b/devscripts/show-downloads-statistics.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python +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 youtube_dl.compat import ( +    compat_print, +    compat_urllib_request, +) +from youtube_dl.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/rg3/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'^youtube-dl$', +                    r'^youtube-dl-\d{4}\.\d{2}\.\d{2}(?:\.\d+)?\.tar\.gz$', +                    r'^youtube-dl\.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)) diff --git a/devscripts/zsh-completion.py b/devscripts/zsh-completion.py index 04728e8e2..60aaf76cc 100755 --- a/devscripts/zsh-completion.py +++ b/devscripts/zsh-completion.py @@ -44,5 +44,6 @@ def build_completion(opt_parser):      with open(ZSH_COMPLETION_FILE, "w") as f:          f.write(template) +  parser = youtube_dl.parseOpts()[0]  build_completion(parser)  | 
