web/lib/django_extensions/management/commands/sync_media_s3.py
author ymh <ymh.work@gmail.com>
Wed, 20 Jan 2010 12:37:40 +0100
changeset 3 526ebd3988b0
permissions -rw-r--r--
replace pocketfilms occurence by blinkster
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
3
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
     1
"""
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
     2
Sync Media to S3
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
     3
================
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
     4
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
     5
Django command that scans all files in your settings.MEDIA_ROOT folder and
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
     6
uploads them to S3 with the same directory structure.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
     7
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
     8
This command can optionally do the following but it is off by default:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
     9
* gzip compress any CSS and Javascript files it finds and adds the appropriate
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    10
  'Content-Encoding' header.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    11
* set a far future 'Expires' header for optimal caching.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    12
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    13
Note: This script requires the Python boto library and valid Amazon Web
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    14
Services API keys.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    15
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    16
Required settings.py variables:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    17
AWS_ACCESS_KEY_ID = ''
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    18
AWS_SECRET_ACCESS_KEY = ''
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    19
AWS_BUCKET_NAME = ''
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    20
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    21
Command options are:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    22
  -p PREFIX, --prefix=PREFIX
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    23
                        The prefix to prepend to the path on S3.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    24
  --gzip                Enables gzipping CSS and Javascript files.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    25
  --expires             Enables setting a far future expires header.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    26
  --force               Skip the file mtime check to force upload of all
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    27
                        files.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    28
  --filter-list         Override default directory and file exclusion
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    29
                        filters. (enter as comma seperated line)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    30
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    31
TODO:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    32
 * Use fnmatch (or regex) to allow more complex FILTER_LIST rules.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    33
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    34
"""
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    35
import datetime
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    36
import email
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    37
import mimetypes
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    38
import optparse
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    39
import os
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    40
import sys
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    41
import time
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    42
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    43
from django.conf import settings
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    44
from django.core.management.base import BaseCommand, CommandError
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    45
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    46
# Make sure boto is available
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    47
try:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    48
    import boto
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    49
    import boto.exception
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    50
except ImportError:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    51
    raise ImportError, "The boto Python library is not installed."
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    52
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    53
class Command(BaseCommand):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    54
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    55
    # Extra variables to avoid passing these around
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    56
    AWS_ACCESS_KEY_ID = ''
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    57
    AWS_SECRET_ACCESS_KEY = ''
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    58
    AWS_BUCKET_NAME = ''
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    59
    DIRECTORY = ''
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    60
    FILTER_LIST = ['.DS_Store', '.svn', '.hg', '.git', 'Thumbs.db']
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    61
    GZIP_CONTENT_TYPES = (
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    62
        'text/css',
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    63
        'application/javascript',
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    64
        'application/x-javascript'
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    65
    )
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    66
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    67
    upload_count = 0
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    68
    skip_count = 0
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    69
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    70
    option_list = BaseCommand.option_list + (
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    71
        optparse.make_option('-p', '--prefix',
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    72
            dest='prefix', default='',
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    73
            help="The prefix to prepend to the path on S3."),
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    74
        optparse.make_option('-d', '--dir',
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    75
            dest='dir', default=settings.MEDIA_ROOT,
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    76
            help="The root directory to use instead of your MEDIA_ROOT"),
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    77
        optparse.make_option('--gzip',
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    78
            action='store_true', dest='gzip', default=False,
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    79
            help="Enables gzipping CSS and Javascript files."),
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    80
        optparse.make_option('--expires',
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    81
            action='store_true', dest='expires', default=False,
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    82
            help="Enables setting a far future expires header."),
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    83
        optparse.make_option('--force',
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    84
            action='store_true', dest='force', default=False,
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    85
            help="Skip the file mtime check to force upload of all files."),
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    86
        optparse.make_option('--filter-list', dest='filter_list',
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    87
            action='store', default='',
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    88
            help="Override default directory and file exclusion filters. (enter as comma seperated line)"),
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    89
    )
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    90
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    91
    help = 'Syncs the complete MEDIA_ROOT structure and files to S3 into the given bucket name.'
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    92
    args = 'bucket_name'
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    93
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    94
    can_import_settings = True
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    95
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    96
    def handle(self, *args, **options):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    97
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    98
        # Check for AWS keys in settings
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
    99
        if not hasattr(settings, 'AWS_ACCESS_KEY_ID') or \
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   100
           not hasattr(settings, 'AWS_SECRET_ACCESS_KEY'):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   101
           raise CommandError('Missing AWS keys from settings file.  Please' +
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   102
                     'supply both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.')
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   103
        else:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   104
            self.AWS_ACCESS_KEY_ID = settings.AWS_ACCESS_KEY_ID
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   105
            self.AWS_SECRET_ACCESS_KEY = settings.AWS_SECRET_ACCESS_KEY
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   106
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   107
        if not hasattr(settings, 'AWS_BUCKET_NAME'):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   108
            raise CommandError('Missing bucket name from settings file. Please' +
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   109
                ' add the AWS_BUCKET_NAME to your settings file.')
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   110
        else:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   111
            if not settings.AWS_BUCKET_NAME:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   112
                raise CommandError('AWS_BUCKET_NAME cannot be empty.')
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   113
        self.AWS_BUCKET_NAME = settings.AWS_BUCKET_NAME
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   114
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   115
        if not hasattr(settings, 'MEDIA_ROOT'):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   116
            raise CommandError('MEDIA_ROOT must be set in your settings.')
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   117
        else:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   118
            if not settings.MEDIA_ROOT:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   119
                raise CommandError('MEDIA_ROOT must be set in your settings.')
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   120
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   121
        self.verbosity = int(options.get('verbosity'))
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   122
        self.prefix = options.get('prefix')
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   123
        self.do_gzip = options.get('gzip')
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   124
        self.do_expires = options.get('expires')
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   125
        self.do_force = options.get('force')
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   126
        self.DIRECTORY = options.get('dir')
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   127
        self.FILTER_LIST = getattr(settings, 'FILTER_LIST', self.FILTER_LIST)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   128
        filter_list = options.get('filter_list').split(',')
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   129
        if filter_list:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   130
            # command line option overrides default filter_list and
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   131
            # settings.filter_list
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   132
            self.FILTER_LIST = filter_list
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   133
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   134
        # Now call the syncing method to walk the MEDIA_ROOT directory and
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   135
        # upload all files found.
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   136
        self.sync_s3()
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   137
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   138
        print
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   139
        print "%d files uploaded." % (self.upload_count)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   140
        print "%d files skipped." % (self.skip_count)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   141
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   142
    def sync_s3(self):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   143
        """
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   144
        Walks the media directory and syncs files to S3
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   145
        """
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   146
        bucket, key = self.open_s3()
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   147
        os.path.walk(self.DIRECTORY, self.upload_s3,
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   148
            (bucket, key, self.AWS_BUCKET_NAME, self.DIRECTORY))
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   149
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   150
    def compress_string(self, s):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   151
        """Gzip a given string."""
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   152
        import cStringIO, gzip
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   153
        zbuf = cStringIO.StringIO()
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   154
        zfile = gzip.GzipFile(mode='wb', compresslevel=6, fileobj=zbuf)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   155
        zfile.write(s)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   156
        zfile.close()
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   157
        return zbuf.getvalue()
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   158
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   159
    def open_s3(self):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   160
        """
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   161
        Opens connection to S3 returning bucket and key
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   162
        """
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   163
        conn = boto.connect_s3(self.AWS_ACCESS_KEY_ID, self.AWS_SECRET_ACCESS_KEY)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   164
        try:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   165
            bucket = conn.get_bucket(self.AWS_BUCKET_NAME)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   166
        except boto.exception.S3ResponseError:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   167
            bucket = conn.create_bucket(self.AWS_BUCKET_NAME)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   168
        return bucket, boto.s3.key.Key(bucket)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   169
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   170
    def upload_s3(self, arg, dirname, names):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   171
        """
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   172
        This is the callback to os.path.walk and where much of the work happens
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   173
        """
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   174
        bucket, key, bucket_name, root_dir = arg # expand arg tuple
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   175
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   176
        # Skip directories we don't want to sync
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   177
        if os.path.basename(dirname) in self.FILTER_LIST:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   178
            # prevent walk from processing subfiles/subdirs below the ignored one
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   179
            del names[:]
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   180
            return 
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   181
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   182
        # Later we assume the MEDIA_ROOT ends with a trailing slash
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   183
        if not root_dir.endswith(os.path.sep):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   184
            root_dir = root_dir + os.path.sep
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   185
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   186
        for file in names:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   187
            headers = {}
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   188
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   189
            if file in self.FILTER_LIST:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   190
                continue # Skip files we don't want to sync
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   191
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   192
            filename = os.path.join(dirname, file)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   193
            if os.path.isdir(filename):
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   194
                continue # Don't try to upload directories
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   195
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   196
            file_key = filename[len(root_dir):]
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   197
            if self.prefix:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   198
                file_key = '%s/%s' % (self.prefix, file_key)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   199
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   200
            # Check if file on S3 is older than local file, if so, upload
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   201
            if not self.do_force:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   202
                s3_key = bucket.get_key(file_key)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   203
                if s3_key:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   204
                    s3_datetime = datetime.datetime(*time.strptime(
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   205
                        s3_key.last_modified, '%a, %d %b %Y %H:%M:%S %Z')[0:6])
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   206
                    local_datetime = datetime.datetime.utcfromtimestamp(
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   207
                        os.stat(filename).st_mtime)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   208
                    if local_datetime < s3_datetime:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   209
                        self.skip_count += 1
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   210
                        if self.verbosity > 1:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   211
                            print "File %s hasn't been modified since last " \
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   212
                                "being uploaded" % (file_key)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   213
                        continue
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   214
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   215
            # File is newer, let's process and upload
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   216
            if self.verbosity > 0:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   217
                print "Uploading %s..." % (file_key)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   218
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   219
            content_type = mimetypes.guess_type(filename)[0]
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   220
            if content_type:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   221
                headers['Content-Type'] = content_type
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   222
            file_obj = open(filename, 'rb')
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   223
            file_size = os.fstat(file_obj.fileno()).st_size
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   224
            filedata = file_obj.read()
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   225
            if self.do_gzip:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   226
                # Gzipping only if file is large enough (>1K is recommended) 
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   227
                # and only if file is a common text type (not a binary file)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   228
                if file_size > 1024 and content_type in self.GZIP_CONTENT_TYPES:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   229
                    filedata = self.compress_string(filedata)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   230
                    headers['Content-Encoding'] = 'gzip'
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   231
                    if self.verbosity > 1:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   232
                        print "\tgzipped: %dk to %dk" % \
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   233
                            (file_size/1024, len(filedata)/1024)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   234
            if self.do_expires:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   235
                # HTTP/1.0
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   236
                headers['Expires'] = '%s GMT' % (email.Utils.formatdate(
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   237
                    time.mktime((datetime.datetime.now() +
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   238
                    datetime.timedelta(days=365*2)).timetuple())))
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   239
                # HTTP/1.1
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   240
                headers['Cache-Control'] = 'max-age %d' % (3600 * 24 * 365 * 2)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   241
                if self.verbosity > 1:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   242
                    print "\texpires: %s" % (headers['Expires'])
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   243
                    print "\tcache-control: %s" % (headers['Cache-Control'])
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   244
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   245
            try:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   246
                key.name = file_key
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   247
                key.set_contents_from_string(filedata, headers, replace=True)
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   248
                key.set_acl('public-read')
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   249
            except boto.s3.connection.S3CreateError, e:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   250
                print "Failed: %s" % e
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   251
            except Exception, e:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   252
                print e
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   253
                raise
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   254
            else:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   255
                self.upload_count += 1
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   256
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   257
            file_obj.close()
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   258
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   259
# Backwards compatibility for Django r9110
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   260
if not [opt for opt in Command.option_list if opt.dest=='verbosity']:
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   261
    Command.option_list += (
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   262
        optparse.make_option('-v', '--verbosity',
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   263
            dest='verbosity', default=1, action='count',
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   264
            help="Verbose mode. Multiple -v options increase the verbosity."),
526ebd3988b0 replace pocketfilms occurence by blinkster
ymh <ymh.work@gmail.com>
parents:
diff changeset
   265
    )