mirror of
https://github.com/fergalmoran/dss.git
synced 2026-02-05 07:34:13 +00:00
Added custom timeruler and duration calculation management command
This commit is contained in:
1
core/utils/audio/__init__.py
Normal file
1
core/utils/audio/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
class Mp3FileNotFoundException(Exception): pass
|
||||
10
core/utils/audio/mp3.py
Normal file
10
core/utils/audio/mp3.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from mutagen.mp3 import MP3
|
||||
from core.utils.audio import Mp3FileNotFoundException
|
||||
|
||||
|
||||
def mp3_length(source_file):
|
||||
try:
|
||||
audio = MP3(source_file)
|
||||
return audio.info.length
|
||||
except IOError:
|
||||
raise Mp3FileNotFoundException("Audio file not found: %s" % source_file)
|
||||
@@ -124,13 +124,14 @@ AUTHENTICATION_BACKENDS = global_settings.AUTHENTICATION_BACKENDS + (
|
||||
)
|
||||
|
||||
MIDDLEWARE_CLASSES = (
|
||||
'django.middleware.gzip.GZipMiddleware',
|
||||
'htmlmin.middleware.HtmlMinifyMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'django.middleware.gzip.GZipMiddleware',
|
||||
'spa.middleware.uploadify.SWFUploadMiddleware',
|
||||
#'spa.middleware.sqlprinter.SqlPrintingMiddleware',
|
||||
#'debug_toolbar.middleware.DebugToolbarMiddleware',
|
||||
@@ -251,3 +252,5 @@ COMPRESS_CSS_FILTERS = [
|
||||
import mimetypes
|
||||
|
||||
mimetypes.add_type("text/xml", ".plist", False)
|
||||
|
||||
HTML_MINIFY = localsettings.HTML_MINIFY
|
||||
@@ -1,9 +0,0 @@
|
||||
from shutil import copyfile
|
||||
from django.core.management.base import NoArgsCommand
|
||||
import os
|
||||
from spa.models import Mix
|
||||
|
||||
class Command(NoArgsCommand):
|
||||
def handle_noargs(self, **options):
|
||||
mixes = Mix.objects.all()
|
||||
|
||||
26
spa/management/commands/processaudio.py
Normal file
26
spa/management/commands/processaudio.py
Normal file
@@ -0,0 +1,26 @@
|
||||
import os
|
||||
from django.core.management.base import NoArgsCommand, CommandError
|
||||
from core.utils.audio import Mp3FileNotFoundException
|
||||
from core.utils.audio.mp3 import mp3_length
|
||||
from dss import settings
|
||||
from spa.models import Mix
|
||||
|
||||
|
||||
class Command(NoArgsCommand):
|
||||
help = "Updates audio files with their durations"
|
||||
|
||||
def handle(self, *args, **options):
|
||||
try:
|
||||
candidates = Mix.objects.filter(duration__isnull=True)
|
||||
for mix in candidates:
|
||||
try:
|
||||
print "Finding duration for: %s" % mix.title
|
||||
length = mp3_length(mix.get_absolute_path())
|
||||
print "\tLength: %d" % length
|
||||
mix.duration = length
|
||||
mix.save()
|
||||
except Mp3FileNotFoundException, me:
|
||||
mix.delete()
|
||||
print me.message
|
||||
except Exception, ex:
|
||||
raise CommandError(ex.message)
|
||||
@@ -1,10 +1,8 @@
|
||||
import os
|
||||
from dss import settings
|
||||
from core.utils.waveform import generate_waveform
|
||||
from django.core.management.base import NoArgsCommand
|
||||
|
||||
from spa.models import Tracklist
|
||||
from spa.models.Mix import Mix
|
||||
from spa.models.Release import ReleaseAudio
|
||||
from django.core.management.base import NoArgsCommand
|
||||
|
||||
|
||||
class Command(NoArgsCommand):
|
||||
help = "Create tracklists for all mixes"
|
||||
|
||||
@@ -12,10 +12,7 @@ class Command(NoArgsCommand):
|
||||
help = "Generate all outstanding waveforms"
|
||||
|
||||
def _generateWaveform(self, mix):
|
||||
fileName, extension = os.path.splitext(mix.local_file.name)
|
||||
if extension == "" or extension == ".":
|
||||
extension = ".mp3"
|
||||
in_file = '%s/%s%s' % (settings.CACHE_ROOT, mix.uid, extension)
|
||||
in_file = mix.get_absolute_path()
|
||||
try:
|
||||
if os.path.isfile(in_file):
|
||||
create_waveform_task.delay(in_file=in_file, mix_uid=mix.uid)
|
||||
|
||||
39
spa/middleware/stripwhitespace.py
Normal file
39
spa/middleware/stripwhitespace.py
Normal file
@@ -0,0 +1,39 @@
|
||||
"""
|
||||
Tightens up response content by removed superflous line breaks and whitespace.
|
||||
By Doug Van Horn
|
||||
|
||||
---- CHANGES ----
|
||||
v1.1 - 31st May 2011
|
||||
Cal Leeming [Simplicity Media Ltd]
|
||||
Modified regex to strip leading/trailing white space from every line, not just those with blank \n.
|
||||
|
||||
---- TODO ----
|
||||
* Ensure whitespace isn't stripped from within <pre> or <code> or <textarea> tags.
|
||||
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
class StripWhitespaceMiddleware(object):
|
||||
"""
|
||||
Strips leading and trailing whitespace from response content.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.whitespace = re.compile('^\s*\n', re.MULTILINE)
|
||||
# self.whitespace_lead = re.compile('^\s+', re.MULTILINE)
|
||||
# self.whitespace_trail = re.compile('\s+$', re.MULTILINE)
|
||||
|
||||
|
||||
def process_response(self, request, response):
|
||||
if "text" in response['Content-Type']:
|
||||
if hasattr(self, 'whitespace_lead'):
|
||||
response.content = self.whitespace_lead.sub('', response.content)
|
||||
if hasattr(self, 'whitespace_trail'):
|
||||
response.content = self.whitespace_trail.sub('\n', response.content)
|
||||
# Uncomment the next line to remove empty lines
|
||||
if hasattr(self, 'whitespace'):
|
||||
response.content = self.whitespace.sub('', response.content)
|
||||
return response
|
||||
else:
|
||||
return response
|
||||
@@ -64,6 +64,12 @@ class Mix(_BaseModel):
|
||||
self.clean_image('mix_image', Mix)
|
||||
super(Mix, self).save(force_insert, force_update, using)
|
||||
|
||||
def get_absolute_path(self):
|
||||
fileName, extension = os.path.splitext(self.local_file.name)
|
||||
if extension == "" or extension == ".":
|
||||
extension = ".mp3"
|
||||
return '%s/mixes/%s%s' % (settings.MEDIA_ROOT, self.uid, extension)
|
||||
|
||||
def get_absolute_url(self):
|
||||
return '/mix/%i' % self.id
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
from django.shortcuts import render_to_response
|
||||
from django.template.context import RequestContext
|
||||
from htmlmin.decorators import not_minified_response
|
||||
from dss import localsettings
|
||||
from spa.forms import UserForm
|
||||
from spa.models import UserProfile
|
||||
|
||||
__author__ = 'fergalm'
|
||||
|
||||
|
||||
@not_minified_response
|
||||
def get_template(request, template_name):
|
||||
#Temporary hack here to create user profiles for zombie users
|
||||
if request.user.is_authenticated():
|
||||
@@ -19,6 +20,7 @@ def get_template(request, template_name):
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
|
||||
@not_minified_response
|
||||
def get_template_ex(request, template_name):
|
||||
html = render_to_response(
|
||||
'views/%s.html' % template_name,
|
||||
|
||||
@@ -99,7 +99,7 @@ div.player-body ul.player-controls a {
|
||||
}
|
||||
|
||||
.waveform img {
|
||||
width: 793px;
|
||||
width: 100%;
|
||||
height: 90px;
|
||||
}
|
||||
|
||||
@@ -179,3 +179,39 @@ img.mix-listing-image {
|
||||
.footer-button-right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.audio-timeline-ruler {
|
||||
background: lightYellow;
|
||||
box-shadow: 0 -1px 1em hsl(60, 60%, 84%) inset;
|
||||
border-radius: 2px;
|
||||
border: 1px solid #ccc;
|
||||
font-size: 9px;
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding-right: 1cm;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.audio-timeline-ruler, .audio-timeline-ruler li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
display: inline-block;
|
||||
}
|
||||
/* IE6-7 Fix */
|
||||
.audio-timeline-ruler, .audio-timeline-ruler li {
|
||||
*display: inline;
|
||||
}
|
||||
|
||||
.audio-timeline-ruler li {
|
||||
/*width: 2em;*/
|
||||
text-align: center;
|
||||
position: relative;
|
||||
text-shadow: 1px 1px hsl(60, 60%, 84%);
|
||||
}
|
||||
.audio-timeline-ruler li:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
border-left: 1px solid #ccc;
|
||||
height: .64em;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,9 @@ window.MixListItemView = Backbone.View.extend({
|
||||
$.each(this.model.get("genre-list"), function (data) {
|
||||
$('#genre-list', parent.el).append('<a href="/mixes/' + this.slug + '" class="btn btn-mini btn-warning"><i class="icon-tags"></i> ' + this.text + '</a>');
|
||||
});
|
||||
|
||||
com.podnoms.player.drawTimeline(
|
||||
$('#player-timeline-' + id, this.el),
|
||||
this.model.get('duration'));
|
||||
return this;
|
||||
},
|
||||
mouseOverProfile: function () {
|
||||
|
||||
@@ -11,9 +11,9 @@ if (!com) var com = {};
|
||||
if (!com.podnoms) com.podnoms = {};
|
||||
|
||||
soundManager.setup({
|
||||
url:'/static/bin/sm/',
|
||||
debugMode:true,
|
||||
wmode:'transparent'
|
||||
url: '/static/bin/sm/',
|
||||
debugMode: true,
|
||||
wmode: 'transparent'
|
||||
});
|
||||
|
||||
soundManager.usePeakData = false;
|
||||
@@ -27,34 +27,35 @@ soundManager.useHTML5Audio = true;
|
||||
|
||||
com.podnoms.player = {
|
||||
/*Members*/
|
||||
currentId:-1,
|
||||
currentPath:'',
|
||||
currentSound:null,
|
||||
waveFormEl:null,
|
||||
playHeadEl:null,
|
||||
loadingEl:null,
|
||||
seekHeadEl:null,
|
||||
waveFormRect:[-1, -1, -1, -1],
|
||||
trackLoaded:false,
|
||||
waveFormTop:-1,
|
||||
waveFormLeft:-1,
|
||||
waveFormWidth:-1,
|
||||
totalLength:-1,
|
||||
currentPosition:-1,
|
||||
currentId: -1,
|
||||
currentPath: '',
|
||||
currentSound: null,
|
||||
waveFormEl: null,
|
||||
playHeadEl: null,
|
||||
timeLineEl: null,
|
||||
loadingEl: null,
|
||||
seekHeadEl: null,
|
||||
waveFormRect: [-1, -1, -1, -1],
|
||||
trackLoaded: false,
|
||||
waveFormTop: -1,
|
||||
waveFormLeft: -1,
|
||||
waveFormWidth: -1,
|
||||
totalLength: -1,
|
||||
currentPosition: -1,
|
||||
/*Privates */
|
||||
_getDurationEstimate:function (oSound) {
|
||||
_getDurationEstimate: function (oSound) {
|
||||
if (oSound.instanceOptions.isMovieStar) {
|
||||
return (oSound.duration);
|
||||
} else {
|
||||
return (!oSound._data.metadata || !oSound._data.metadata.data.givenDuration ? (oSound.durationEstimate || 0) : oSound._data.metadata.data.givenDuration);
|
||||
}
|
||||
},
|
||||
_whileLoading:function () {
|
||||
_whileLoading: function () {
|
||||
var percentageFinished = (this.currentSound.bytesLoaded / this.currentSound.bytesTotal) * 100;
|
||||
var percentageWidth = (this.waveFormWidth / 100) * percentageFinished;
|
||||
this.loadingEl.css('width', percentageWidth);
|
||||
},
|
||||
_whilePlaying:function () {
|
||||
_whilePlaying: function () {
|
||||
if (!this.trackLoaded) {
|
||||
this.playButtonEl
|
||||
.removeClass('play-button-small-loading')
|
||||
@@ -67,7 +68,7 @@ com.podnoms.player = {
|
||||
var percentageWidth = (this.waveFormWidth / 100) * percentageFinished;
|
||||
this.playHeadEl.css('width', percentageWidth);
|
||||
},
|
||||
_mouseDown:function (event) {
|
||||
_mouseDown: function (event) {
|
||||
console.log("Got mousedown: " + event.pageX);
|
||||
if (this.currentSound != null) {
|
||||
this.currentSound.setPosition(
|
||||
@@ -75,14 +76,14 @@ com.podnoms.player = {
|
||||
}
|
||||
$(event.currentTarget).mouseup($.proxy(this._mouseDown, this));
|
||||
},
|
||||
_mouseMove:function (event) {
|
||||
_mouseMove: function (event) {
|
||||
this.seekHeadEl.show();
|
||||
this.seekHeadEl.css('left', (event.pageX) + 'px').fadeIn('fast');
|
||||
},
|
||||
_mouseLeave:function (event) {
|
||||
_mouseLeave: function (event) {
|
||||
this.seekHeadEl.hide();
|
||||
},
|
||||
_destroyCurrent:function (success) {
|
||||
_destroyCurrent: function (success) {
|
||||
if (this.currentSound != null) {
|
||||
soundManager.destroySound(this.currentSound.sID);
|
||||
}
|
||||
@@ -97,16 +98,17 @@ com.podnoms.player = {
|
||||
if (success != undefined)
|
||||
success();
|
||||
},
|
||||
_parseOptions:function (options) {
|
||||
_parseOptions: function (options) {
|
||||
this.currentId = options.id;
|
||||
this.waveFormEl = options.waveFormEl;
|
||||
this.seekHeadEl = options.seekHeadEl;
|
||||
this.playHeadEl = options.playHeadEl;
|
||||
this.loadingEl = options.loadingEl;
|
||||
this.timeLineEl = options.timeLineEl;
|
||||
this.playButtonEl = options.playButtonEl;
|
||||
this.currentPath = options.url;
|
||||
},
|
||||
_setupParams:function () {
|
||||
_setupParams: function () {
|
||||
this.waveFormTop = this.waveFormEl.position().top;
|
||||
this.waveFormLeft = this.waveFormEl.offset().left;
|
||||
this.waveFormWidth = this.waveFormEl.width();
|
||||
@@ -119,14 +121,29 @@ com.podnoms.player = {
|
||||
this.waveFormEl.mouseout($.proxy(this._mouseLeave, this));
|
||||
},
|
||||
/*Methods*/
|
||||
isPlaying:function () {
|
||||
isPlaying: function () {
|
||||
if (this.currentSound != null)
|
||||
return this.currentSound.playState == 1;
|
||||
},
|
||||
isPlayingId:function (id) {
|
||||
isPlayingId: function (id) {
|
||||
return this.isPlaying() && this.currentSound.sID == "com.podnoms.player-" + id;
|
||||
},
|
||||
setupPlayer:function (options) {
|
||||
drawTimeline: function (el, duration) {
|
||||
/*
|
||||
Assume 10 markers
|
||||
*/
|
||||
var markerDuration = duration / 10;
|
||||
var item = $(document.createElement("li"));
|
||||
for (var i = 0; i < 10; i++){
|
||||
var duration = moment.duration(markerDuration * (i+1), "seconds");
|
||||
var text = duration.hours() != 0 ?
|
||||
moment(duration).format("HH:mm") :
|
||||
moment(duration).format("mm:ss");
|
||||
el.append(item.clone().text(text).css('width', '10%'));
|
||||
|
||||
}
|
||||
},
|
||||
setupPlayer: function (options) {
|
||||
this._parseOptions(options);
|
||||
this._setupParams();
|
||||
if (this.isPlayingId(options.id)) {
|
||||
@@ -136,18 +153,18 @@ com.podnoms.player = {
|
||||
.addClass('play-button-small-pause');
|
||||
}
|
||||
},
|
||||
startPlaying:function (options) {
|
||||
startPlaying: function (options) {
|
||||
var ref = this;
|
||||
var currId = this.currentId;
|
||||
this._destroyCurrent(function () {
|
||||
ref.currentSound = soundManager.createSound({
|
||||
url:ref.currentPath,
|
||||
id:"com.podnoms.player-" + currId.toString(),
|
||||
volume:com.podnoms.settings.volume,
|
||||
whileloading:function () {
|
||||
url: ref.currentPath,
|
||||
id: "com.podnoms.player-" + currId.toString(),
|
||||
volume: com.podnoms.settings.volume,
|
||||
whileloading: function () {
|
||||
ref._whileLoading();
|
||||
},
|
||||
whileplaying:function () {
|
||||
whileplaying: function () {
|
||||
ref._whilePlaying();
|
||||
}
|
||||
});
|
||||
@@ -163,19 +180,19 @@ com.podnoms.player = {
|
||||
}
|
||||
});
|
||||
},
|
||||
stopPlaying:function () {
|
||||
stopPlaying: function () {
|
||||
this._destroyCurrent();
|
||||
},
|
||||
playLive:function () {
|
||||
playLive: function () {
|
||||
var ref = this;
|
||||
var args = arguments;
|
||||
this._destroyCurrent(function () {
|
||||
ref.currentSound = soundManager.createSound({
|
||||
id:'com.podnoms.player-live',
|
||||
url:com.podnoms.settings.liveStreamRoot,
|
||||
volume:50,
|
||||
stream:true,
|
||||
useMovieStar:true
|
||||
id: 'com.podnoms.player-live',
|
||||
url: com.podnoms.settings.liveStreamRoot,
|
||||
volume: 50,
|
||||
stream: true,
|
||||
useMovieStar: true
|
||||
});
|
||||
if (ref.currentSound) {
|
||||
ref.currentSound.play();
|
||||
@@ -187,34 +204,41 @@ com.podnoms.player = {
|
||||
});
|
||||
},
|
||||
|
||||
play:function () {
|
||||
play: function () {
|
||||
this.currentSound.play();
|
||||
this.playButtonEl
|
||||
.removeClass('play-button-small-start')
|
||||
.addClass('play-button-small-loading');
|
||||
},
|
||||
pause:function () {
|
||||
pause: function () {
|
||||
this.currentSound.pause();
|
||||
this.playButtonEl
|
||||
.removeClass('play-button-small-pause')
|
||||
.addClass('play-button-small-resume');
|
||||
},
|
||||
resume:function () {
|
||||
resume: function () {
|
||||
this.currentSound.resume();
|
||||
this.playButtonEl
|
||||
.removeClass('play-button-small-resume')
|
||||
.addClass('play-button-small-pause');
|
||||
},
|
||||
forward:function (increment) {
|
||||
forward: function (increment) {
|
||||
|
||||
},
|
||||
back:function (increment) {
|
||||
back: function (increment) {
|
||||
|
||||
},
|
||||
setPosition:function (position) {
|
||||
setPosition: function (position) {
|
||||
|
||||
},
|
||||
updateWaveform:function (position) {
|
||||
updateWaveform: function (position) {
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
;
|
||||
|
||||
com.podnoms.player.timeline = {
|
||||
setupTimeline: function (options) {
|
||||
|
||||
}
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -81,7 +81,7 @@
|
||||
<script src="{{ STATIC_URL }}js/libs/tiny_mce/jquery.tinymce.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/libs/modernizr.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/libs/backbone/underscore.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/libs/backbone/moment.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/libs/moment.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/libs/ICanHaz.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/libs/select2.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/libs/backbone/backbone.js"></script>
|
||||
|
||||
@@ -35,12 +35,16 @@
|
||||
<img id="waveform-image-<%= item.id %>" class="waveform_image"
|
||||
alt="Audio waveform"
|
||||
src="<%= item.waveform_url %>">
|
||||
.
|
||||
<div class="download-progress-overlay" id="progress-player-<%= item.id %>"></div>
|
||||
<div class="playhead" id="playhead-player-<%= item.id %>" style="width: 0px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="player-timeline" style="height: 12px; border: 1px solid #aaa">
|
||||
<ul class="audio-timeline-ruler" id="player-timeline-<%= item.id %>"></ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="player-footer">
|
||||
<div class="play-button footer-button">
|
||||
|
||||
@@ -10,12 +10,15 @@ com.podnoms.settings = {
|
||||
streamInfoUrl:'http://{{ LIVE_STREAM_INFO_URL }}',
|
||||
volume:'{{ DEFAULT_AUDIO_VOLUME }}',
|
||||
smDebugMode: false,
|
||||
|
||||
/** simple helper to take an api JSON object and initialise a player item */
|
||||
setupPlayer:function (data, id) {
|
||||
com.podnoms.player.setupPlayer({
|
||||
id:id,
|
||||
waveFormEl:$('#waveform-' + id),
|
||||
playHeadEl:$('#playhead-player-' + id),
|
||||
loadingEl:$('#progress-player-' + id),
|
||||
timeLineEl:$('#player-timeline-' + id),
|
||||
seekHeadEl:$('#player-seekhead'),
|
||||
playButtonEl:$('#play-pause-button-small-' + id),
|
||||
url:data.stream_url
|
||||
|
||||
Reference in New Issue
Block a user