diff --git a/.gitignore b/.gitignore index 0cc78b3..36538b7 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ dss/celery_settings.py dss/devsettings.py dss.conf dss/debugsettings.py +logs/ mysql test.py mysql2pgsql.yml @@ -34,4 +35,4 @@ reload reset __krud/ celerybeat-schedule -private/ \ No newline at end of file +private/ diff --git a/Dockerfile b/Dockerfile index 52f10e5..fd64e60 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,26 +1,12 @@ -FROM python:latest -ENV PYTHONBUFFERED 1 +FROM fergalmoran/django -RUN mkdir /code -RUN mkdir /srv/logs -RUN mkdir /files -RUN mkdir /files/static -RUN mkdir /files/media -RUN mkdir /files/cache -RUN mkdir /files/cache/mixes -RUN mkdir /files/cache/waveforms -RUN mkdir /files/tmp - -WORKDIR /code - -ADD requirements.txt /code/ ADD . /code/ -RUN apt-get update && apt-get install -y sox lame vim ccze node npm \ - libboost-program-options-dev libsox-fmt-mp3 postgresql-client rsync openssh-client -RUN npm install -g yuglify -RUN pip install -r requirements.txt +RUN mkdir /files/static +RUN mkdir /files/media +RUN mkdir /files/cache/mixes +RUN mkdir /files/cache/waveforms +RUN touch /files/tmp/dss.log -RUN adduser --disabled-password --gecos '' djworker -RUN chown djworker /files -R +WORKDIR /code diff --git a/api/auth.py b/api/auth.py index ad9b373..6b4469c 100644 --- a/api/auth.py +++ b/api/auth.py @@ -1,20 +1,19 @@ import datetime import json -import logging from calendar import timegm from urllib.parse import parse_qsl import requests from allauth.socialaccount import models as aamodels from requests_oauthlib import OAuth1 -from rest_framework import parsers -from rest_framework import permissions -from rest_framework import renderers +from rest_framework import parsers, renderers +from rest_framework import status from rest_framework.authtoken.models import Token from rest_framework.authtoken.serializers import AuthTokenSerializer +from rest_framework.permissions import AllowAny from rest_framework.response import Response from rest_framework.views import APIView -from rest_framework.views import status +from rest_framework_jwt.authentication import JSONWebTokenAuthentication from rest_framework_jwt.settings import api_settings from rest_framework_jwt.utils import jwt_payload_handler, jwt_encode_handler @@ -22,14 +21,6 @@ from dss import settings from spa.models import UserProfile from spa.models.socialaccountlink import SocialAccountLink -logger = logging.getLogger('dss') - -BACKENDS = { - 'google': 'google-oauth2', - 'facebook': 'facebook', - 'twitter': 'twitter' -} - def _temp_reverse_user(uid, provider, access_token, access_token_secret, payload): """ @@ -45,7 +36,7 @@ def _temp_reverse_user(uid, provider, access_token, access_token_secret, payload sa.access_token_secret = access_token_secret sa.provider_data = payload sa.save() - user = UserProfile.objects.get(user__id=sa.user.id) + user = UserProfile.objects.get(id=sa.user.id) except SocialAccountLink.DoesNotExist: # try allauth try: @@ -70,7 +61,8 @@ def _temp_reverse_user(uid, provider, access_token, access_token_secret, payload class SocialLoginHandler(APIView): - permission_classes = (permissions.AllowAny,) + """View to authenticate users through social media.""" + permission_classes = (AllowAny,) def post(self, request): uid = None @@ -190,7 +182,8 @@ class ObtainUser(APIView): 'name': request.user.username, 'session': request.user.userprofile.get_session_id(), 'slug': request.user.userprofile.slug, - 'userRole': 'user' + 'session': request.user.userprofile.get_session_id(), + 'userRole': 'user', }) else: return Response(status=status.HTTP_401_UNAUTHORIZED) diff --git a/api/serializers.py b/api/serializers.py index dd51523..e377265 100755 --- a/api/serializers.py +++ b/api/serializers.py @@ -34,6 +34,9 @@ class InlineUserProfileSerializer(serializers.ModelSerializer): profile_image_small = serializers.SerializerMethodField() profile_image_medium = serializers.SerializerMethodField() profile_image_header = serializers.SerializerMethodField() + first_name = serializers.ReadOnlyField(source='get_first_name') + last_name = serializers.ReadOnlyField(source='get_last_name') + display_name = serializers.SerializerMethodField() class Meta: model = UserProfile @@ -48,15 +51,12 @@ class InlineUserProfileSerializer(serializers.ModelSerializer): 'profile_image_header', ) - first_name = serializers.ReadOnlyField(source='get_first_name') - last_name = serializers.ReadOnlyField(source='get_last_name') - display_name = serializers.ReadOnlyField(source='get_nice_name') def get_avatar_image(self, obj): - return obj.get_sized_avatar_image(64, 64) + return obj.get_sized_avatar_image(32, 32) def get_avatar_image_tiny(self, obj): - return obj.get_sized_avatar_image(64, 64) + return obj.get_sized_avatar_image(32, 32) def to_representation(self, instance): if instance.user.is_anonymous(): @@ -72,14 +72,20 @@ class InlineUserProfileSerializer(serializers.ModelSerializer): return obj.is_follower(self.context['request'].user) def get_profile_image_small(self, obj): - return obj.get_sized_avatar_image(64, 64) + return obj.get_sized_avatar_image(32, 32) def get_profile_image_medium(self, obj): - return obj.get_sized_avatar_image(170, 170) + return obj.get_sized_avatar_image(253, 157) def get_profile_image_header(self, obj): return obj.get_sized_avatar_image(1200, 150) + def get_display_name(self, obj): + n = obj.get_nice_name() + if not n or n == "": + n = "%s %s".format(obj.first_name, obj.last_name) + + return n class LikeSerializer(serializers.ModelSerializer): class Meta: @@ -196,6 +202,18 @@ class MixSerializer(serializers.ModelSerializer): except UserProfile.DoesNotExist: pass + genres = self.initial_data['genres'] + instance.genres.clear() + for genre in genres: + try: + g = Genre.objects.get(slug=genre.get('slug')) + instance.genres.add(g) + except Genre.DoesNotExist: + """ Possibly allow adding genres here """ + pass + + validated_data.pop('genres', None) + # get any likes that aren't in passed bundle if 'downloads' in validated_data: plays = validated_data['downloads'] or [] @@ -203,12 +221,6 @@ class MixSerializer(serializers.ModelSerializer): instance.add_play(play) validated_data.pop('downloads', None) - if 'genres' in validated_data: - genres = validated_data['genres'] or [] - for genre in genres: - instance.add_genre(genre) - validated_data.pop('genres', None) - return super(MixSerializer, self).update(instance, validated_data) except MixUpdateException as ex: raise ex @@ -217,7 +229,7 @@ class MixSerializer(serializers.ModelSerializer): return super(MixSerializer, self).is_valid(raise_exception) def get_avatar_image(self, obj): - return obj.user.get_sized_avatar_image(64, 64) + return obj.user.get_sized_avatar_image(32, 32) def get_can_edit(self, obj): user = self.context['request'].user @@ -254,6 +266,7 @@ class UserProfileSerializer(serializers.ModelSerializer): profile_image_small = serializers.SerializerMethodField() profile_image_medium = serializers.SerializerMethodField() profile_image_header = serializers.SerializerMethodField() + display_name = serializers.SerializerMethodField(source='get_display_name') top_tags = serializers.SerializerMethodField() @@ -304,6 +317,13 @@ class UserProfileSerializer(serializers.ModelSerializer): pass return super(UserProfileSerializer, self).update(instance, validated_data) + def get_display_name(self, obj): + n = obj.get_nice_name() + if not n or n == "": + n = "%s %s".format(obj.first_name, obj.last_name) + + return n + def get_title(self, obj): try: if obj.description: @@ -346,10 +366,10 @@ class UserProfileSerializer(serializers.ModelSerializer): values('total', 'description', 'slug')[0:3]) def get_profile_image_small(self, obj): - return obj.get_sized_avatar_image(64, 64) + return obj.get_sized_avatar_image(32, 32) def get_profile_image_medium(self, obj): - return obj.get_sized_avatar_image(170, 170) + return obj.get_sized_avatar_image(253, 157) def get_profile_image_header(self, obj): return obj.get_sized_avatar_image(1200, 150) @@ -402,8 +422,11 @@ class CommentSerializer(serializers.HyperlinkedModelSerializer): def get_can_edit(self, obj): user = self.context['request'].user - if user is not None and obj.user is not None and user.is_authenticated(): - return user.is_staff or obj.user.id == user.userprofile.id + if user is not None: + if user.is_staff: + return True + if obj.user is not None and user.is_authenticated(): + return obj.user.id == user.userprofile.id return False @@ -425,7 +448,7 @@ class HitlistSerializer(serializers.ModelSerializer): return obj.get_nice_name() def get_avatar_image(self, obj): - return obj.get_sized_avatar_image(170, 170) + return obj.get_sized_avatar_image(253, 157) class ActivitySerializer(serializers.HyperlinkedModelSerializer): @@ -454,7 +477,7 @@ class NotificationSerializer(serializers.HyperlinkedModelSerializer): from_user = InlineUserProfileSerializer(source='get_from_user', read_only=True) notification_url = serializers.ReadOnlyField() verb = serializers.ReadOnlyField() - target = serializers.ReadOnlyField() + target = serializers.SerializerMethodField() date = serializers.ReadOnlyField() class Meta: @@ -475,7 +498,10 @@ class NotificationSerializer(serializers.HyperlinkedModelSerializer): return settings.DEFAULT_USER_NAME if obj.from_user is None else obj.from_user.get_nice_name() def get_avatar_image(self, obj): - return settings.DEFAULT_USER_IMAGE if obj.from_user is None else obj.from_user.get_sized_avatar_image(170, 170) + return settings.DEFAULT_USER_IMAGE if obj.from_user is None else obj.get_sized_avatar_image(253, 157) + + def get_target(self, obj): + return "/{}/{}".format(obj.to_user.slug, obj.target) class MessageSerializer(serializers.ModelSerializer): diff --git a/api/urls.py b/api/urls.py index d438afd..bfe20b4 100755 --- a/api/urls.py +++ b/api/urls.py @@ -1,20 +1,17 @@ from django.conf.urls import patterns, url, include -from rest_framework import permissions from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response from rest_framework.routers import DefaultRouter from rest_framework.views import APIView +from rest_framework.views import status from rest_framework_jwt.authentication import JSONWebTokenAuthentication from api import views, auth, helpers from api.auth import SocialLoginHandler -from rest_framework.views import status -from rest_framework.response import Response from core.realtime import activity router = DefaultRouter() # trailing_slash=True) -router.register(r'user', views.UserProfileViewSet) -router.register(r'mix', views.MixViewSet) router.register(r'notification', views.NotificationViewSet) router.register(r'hitlist', views.HitlistViewSet) @@ -26,6 +23,8 @@ router.register(r'shows', views.ShowViewSet, base_name='shows') router.register(r'blog', views.BlogViewSet, base_name='shows') router.register(r'playlist', views.PlaylistViewSet, base_name='playlists') +router.register(r'user', views.UserProfileViewSet) +router.register(r'mix', views.MixViewSet) class DebugView(APIView): #permission_classes = (IsAuthenticated,) diff --git a/api/views.py b/api/views.py index ee0e980..d78d47f 100755 --- a/api/views.py +++ b/api/views.py @@ -101,6 +101,7 @@ class MixViewSet(viewsets.ModelViewSet): 'slug', 'user__slug', 'is_featured', + 'genres__slug', ) ordering_fields = ( @@ -121,7 +122,7 @@ class MixViewSet(viewsets.ModelViewSet): raise PermissionDenied("Not allowed") if 'random' in self.request.query_params: return self.queryset.order_by('?').all() - if 'slug' or 'user__slug' in self.kwargs: + if 'slug' in self.kwargs or 'user__slug' in self.kwargs: """ could be private mix so don't filter """ return self.queryset else: @@ -181,6 +182,7 @@ class SearchResultsView(views.APIView): 'image': mix.get_image_url(size='48x48'), 'user': mix.user.slug, 'slug': mix.slug, + 'user': mix.user.slug, 'url': mix.get_absolute_url(), 'description': mix.description } for mix in Mix.objects.filter(title__icontains=q)[0:10]] diff --git a/core/realtime/activity.py b/core/realtime/activity.py index d202b62..ea44e06 100755 --- a/core/realtime/activity.py +++ b/core/realtime/activity.py @@ -7,10 +7,13 @@ logger = logging.getLogger('dss') def post_activity(channel, message, session=''): - r = redis.StrictRedis(host=settings.REDIS_HOST, port=6379, db=0) - payload = json.dumps({'session': session, 'message': message}) - response = r.publish(channel, payload) - logger.debug("Message sent: {0}".format(payload)) + try: + r = redis.StrictRedis(host=settings.REDIS_HOST, port=6379, db=0) + payload = json.dumps({'session': session, 'message': message}) + response = r.publish(channel, payload) + logger.debug("Message sent: {0}".format(payload)) + except Exception as ex: + logger.error(ex) if __name__ == '__main__': post_activity('site:broadcast', 'bargle', '3a596ca6c97065a67aca3dc4a3ba230d688cf413') diff --git a/core/utils/cdn.py b/core/utils/cdn.py index 9e62ff4..0553d62 100755 --- a/core/utils/cdn.py +++ b/core/utils/cdn.py @@ -26,7 +26,7 @@ def set_azure_details(blob_name, download_name, container_name=AZURE_CONTAINER): blob_service.set_blob_properties( container_name, blob_name, - x_ms_blob_content_type='application/octet-stream', + x_ms_blob_content_type='audio/mpeg', x_ms_blob_content_disposition='attachment;filename="{0}"'.format(download_name) ) print("Processed: %s" % download_name) @@ -61,4 +61,4 @@ def enumerate_objects(container): def delete_object(container, name): blob_service = BlobService(AZURE_ACCOUNT_NAME, AZURE_ACCOUNT_KEY) - blob_service.delete_blob(container, name) \ No newline at end of file + blob_service.delete_blob(container, name) diff --git a/core/utils/url.py b/core/utils/url.py index 03dd048..6400655 100755 --- a/core/utils/url.py +++ b/core/utils/url.py @@ -1,5 +1,6 @@ -import urllib.parse import re +import urllib + from django.contrib.sites.models import Site from django.template.defaultfilters import slugify diff --git a/dss/logsettings.py b/dss/logsettings.py index f591327..57b375a 100755 --- a/dss/logsettings.py +++ b/dss/logsettings.py @@ -3,7 +3,13 @@ import sys from dss import localsettings if os.name == 'posix': - LOG_FILE = localsettings.DSS_TEMP_PATH + '/dss.log' + LOG_FILE = localsettings.DSS_TEMP_PATH + 'dss.log' + if not os.path.exists(LOG_FILE): + print("Creating {}".format(LOG_FILE)) + open(LOG_FILE, 'a').close() + else: + print("{} already exists.".format(LOG_FILE)) + else: LOG_FILE = 'c:\\temp\\dss.log' diff --git a/dss/settings.py b/dss/settings.py index 176b100..33ba9ab 100755 --- a/dss/settings.py +++ b/dss/settings.py @@ -20,7 +20,7 @@ DEVELOPMENT = DEBUG # AUTH_USER_MODEL = 'spa.UserProfile' TEMPLATE_DEBUG = DEBUG -VERSION = '2.13.04' +VERSION = '3.0.1' ADMINS = ( ('Fergal Moran', 'fergal.moran@gmail.com'), @@ -212,7 +212,7 @@ THUMBNAIL_PREFIX = '_tn/' # THUMBNAIL_STORAGE = 'storages.backends.azure_storage.AzureStorage' JWT_AUTH = { - 'JWT_EXPIRATION_DELTA': timedelta(seconds=900), + 'JWT_EXPIRATION_DELTA': timedelta(seconds=(60 * 60 * 24) * 14), # 'JWT_EXPIRATION_DELTA': timedelta(seconds=5), 'JWT_ALLOW_REFRESH': True, 'JWT_REFRESH_EXPIRATION_DELTA': timedelta(days=30), @@ -244,3 +244,12 @@ CORS_ALLOW_HEADERS = ( ) """ End static settings """ +SOCIAL_AUTH_AUTHENTICATION_BACKENDS = ( + 'social.backends.open_id.OpenIdAuth', + 'social.backends.google.GoogleOpenId', + 'social.backends.google.GoogleOAuth2', + 'social.backends.google.GoogleOAuth', + 'social.backends.twitter.TwitterOAuth', + 'social.backends.yahoo.YahooOpenId' +) + diff --git a/spa/management/commands/backup.py b/spa/management/commands/backup.py index 1fd999b..8a917ed 100644 --- a/spa/management/commands/backup.py +++ b/spa/management/commands/backup.py @@ -10,47 +10,8 @@ from dropbox.rest import ErrorResponse from dss import settings -""" Monkey patch dropbox upload chunked """ - - -def __upload_chunked(self, chunk_size = 4 * 1024 * 1024): - """Uploads data from this ChunkedUploader's file_obj in chunks, until - an error occurs. Throws an exception when an error occurs, and can - be called again to resume the upload. - - Parameters - chunk_size - The number of bytes to put in each chunk. (Default 4 MB.) - """ - - while self.offset < self.target_length: - next_chunk_size = min(chunk_size, self.target_length - self.offset) - if self.last_block == None: - self.last_block = self.file_obj.read(next_chunk_size) - - try: - (self.offset, self.upload_id) = self.client.upload_chunk( - self.last_block, next_chunk_size, self.offset, self.upload_id) - self.last_block = None - except ErrorResponse as e: - # Handle the case where the server tells us our offset is wrong. - must_reraise = True - if e.status == 400: - reply = e.body - if "offset" in reply and reply['offset'] != 0 and reply['offset'] > self.offset: - self.last_block = None - self.offset = reply['offset'] - must_reraise = False - if must_reraise: - raise - -ChunkedUploader.upload_chunked = __upload_chunked - -from dropbox.client import ChunkedUploader """ Monkey patch dropbox upload chunked """ - - def __upload_chunked(self, chunk_size = 4 * 1024 * 1024): """Uploads data from this ChunkedUploader's file_obj in chunks, until an error occurs. Throws an exception when an error occurs, and can @@ -122,7 +83,8 @@ def _create_backup_bundle(remote_file, location): backup_file = "{0}/{1}".format(settings.DSS_TEMP_PATH, remote_file) tar = tarfile.open(backup_file, "w:gz") - tar.add(location) + print("Remote file: {}".format(remote_file.replace(".tar.gz", ""))) + tar.add(tarfile.TarInfo(remote_file.replace(".tar.gz", "")), location) tar.close() return backup_file diff --git a/spa/management/commands/helpers.py b/spa/management/commands/helpers.py index d3dc6ff..de5c5e4 100644 --- a/spa/management/commands/helpers.py +++ b/spa/management/commands/helpers.py @@ -21,3 +21,4 @@ def download_file( url, file_name): print(status, end=' ') f.close() + diff --git a/spa/management/commands/restore.py b/spa/management/commands/restore.py index f9c7bc9..1bc5804 100644 --- a/spa/management/commands/restore.py +++ b/spa/management/commands/restore.py @@ -1,11 +1,50 @@ +import os +import tarfile + from django.core.management.base import LabelCommand import dropbox from dss import settings +import sys + +from utils import query_yes_no + def _restore_database(): """ find latest database backup """ - client = dropbox.client.DropboxClient(settings.DSS_DB_BACKUP_TOKEN) + client = dropbox.Dropbox(settings.DSS_DB_BACKUP_TOKEN) + files = client.files_list_folder('/media') + latest = None + for f in files.entries: + print(f.server_modified) + if latest is None or f.server_modified > latest.server_modified: + latest = f + + if latest is not None: + #if query_yes_no("Restoring backing from: {}\nProceed (y/n?)".format(latest)): + print("Restoring database") + backup_file = '/tmp/{}'.format(latest.name) + result = client.files_download_to_file(backup_file, latest.path_lower) + print("Downloaded {}".format(result)) + + if os.path.exists(backup_file): + print("Download completed") + o = tarfile.open(backup_file) + o.extract() + else: + print("Unable to download file") + + +""" +import requests +... +headers = ... # set up auth +... +params = { 'list' : 'true' } +response = requests.get('https://api.dropbox.com/1/metadata/dropbox/', params=params, headers=headers) +subdirs = [d['path'] for d in response.json()['contents'] if d['is_dir'] == True] +print(subdirs) +""" class Command(LabelCommand): diff --git a/spa/migrations/0021_auto_20151112_2017.py b/spa/migrations/0021_auto_20151112_2017.py index f2399b7..46f8b5b 100644 --- a/spa/migrations/0021_auto_20151112_2017.py +++ b/spa/migrations/0021_auto_20151112_2017.py @@ -23,6 +23,6 @@ class Migration(migrations.Migration): migrations.AddField( model_name='mix', name='is_private', - field=models.BooleanField(default=True), + field=models.BooleanField(default=False), ), ] diff --git a/spa/models/mix.py b/spa/models/mix.py index 2b348e0..8a0e835 100755 --- a/spa/models/mix.py +++ b/spa/models/mix.py @@ -96,35 +96,32 @@ class Mix(BaseModel): return self.__unicode__() def __unicode__(self): - return "{} - {}".format(self.user.get_nice_name(), self.title) + return "{} - {}".format(self.user.get_nice_name(), self.title) def save(self, force_insert=False, force_update=False, using=None, update_fields=None): if not self.id: self.slug = unique_slugify(self, self.title) self.clean_image('mix_image', Mix) - # Check for the unlikely event that the waveform has been generated - if cdn.file_exists('{0}{1}.png'.format(settings.WAVEFORM_URL, self.uid)): - self.waveform_generated = True - try: - self.duration = mp3_length(self.get_absolute_path()) - except Mp3FileNotFoundException: - # Not really bothered about this in save as it can be called before we have an mp3 - pass - super(Mix, self).save(force_insert, force_update, using, update_fields) + def set_cdn_details(self, path): + self.waveform_generated = True + self.duration = mp3_length(path) + self.save(update_fields=["waveform_generated", "duration"]) + self.update_file_http_headers(self.uid, self.title) + def create_mp3_tags(self, prefix=""): try: tag_mp3( - self.get_absolute_path(), - artist=self.user.get_nice_name(), - title=self.title, - url=self.get_full_url(), - album="Deep South Sounds Mixes", - year=self.upload_date.year, - comment=self.description, - genres=self.get_nice_genres()) + self.get_absolute_path(), + artist=self.user.get_nice_name(), + title=self.title, + url=self.get_full_url(), + album="Deep South Sounds Mixes", + year=self.upload_date.year, + comment=self.description, + genres=self.get_nice_genres()) except Exception as ex: self.logger.exception("Mix: error creating tags: %s" % ex) pass @@ -176,7 +173,7 @@ class Mix(BaseModel): ret = get_thumbnail(self.mix_image, size, crop='center') return url.urlclean("%s/%s" % (settings.MEDIA_URL, ret.name)) else: - return self.user.get_sized_avatar_image(170, 170) + return self.user.get_sized_avatar_image(253, 157) except Exception as ex: pass diff --git a/spa/models/userprofile.py b/spa/models/userprofile.py index 8977fb3..f0e8590 100755 --- a/spa/models/userprofile.py +++ b/spa/models/userprofile.py @@ -174,6 +174,7 @@ class UserProfile(BaseModel): def get_sized_avatar_image(self, width, height): try: + #import ipdb; ipdb.set_trace() image = self.get_avatar_image() logger.debug("get_sized_avatar_image: %s".format(image)) sized = thumbnail.get_thumbnail(image, "%sx%s" % (width, height), crop="center") diff --git a/spa/signals.py b/spa/signals.py index 0a209a2..504806d 100755 --- a/spa/signals.py +++ b/spa/signals.py @@ -1,16 +1,13 @@ +from django.contrib.auth.models import User from django.contrib.sessions.models import Session from django.core.exceptions import ObjectDoesNotExist from django.db.models.signals import post_save, pre_save, m2m_changed from django.dispatch import Signal, receiver -from django.contrib.auth.models import User -from core.realtime import activity - -from core.utils.audio.mp3 import mp3_length from spa.models import SocialAccountLink from spa.models.activity import ActivityFollow -from spa.models.userprofile import UserProfile from spa.models.mix import Mix +from spa.models.userprofile import UserProfile waveform_generated_signal = Signal() @@ -23,10 +20,7 @@ def _waveform_generated_callback(sender, **kwargs): if uid is not None: mix = Mix.objects.get(uid=uid) if mix is not None: - mix.waveform_generated = True - mix.duration = mp3_length(path) - mix.save(update_fields=["waveform_generated", "duration"]) - + mix.set_cdn_details(path) except ObjectDoesNotExist: print("Mix has still not been uploaded") pass diff --git a/spa/tasks.py b/spa/tasks.py index d98688a..1d87cd9 100755 --- a/spa/tasks.py +++ b/spa/tasks.py @@ -36,6 +36,14 @@ def create_waveform_task(in_file, uid): logger.error("Outfile is missing") +@task(time_limit=3600) +def update_file_http_headers(uid, title): + cdn.set_azure_details( + blob_name='{0}.mp3'.format(uid), + download_name='Deep South Sounds - {0}.mp3'.format(title), + container_name='mixes') + + @task(time_limit=3600) def upload_to_cdn_task(filetype, uid, container_name): source_file = os.path.join(settings.CACHE_ROOT, '{0}/{1}.{2}'.format(container_name, uid, filetype))