Compare commits

..

No commits in common. "2d25c60606004a02297e521211f3d08e823cd8df" and "0b44b3b66b5d52c6d506b00b650520b21860c1f4" have entirely different histories.

84 changed files with 1237 additions and 1944 deletions

24
ctl
View file

@ -9,37 +9,33 @@ fi
if [ "$action" = "init" ]; then if [ "$action" = "init" ]; then
cd "`dirname "$0"`" cd "`dirname "$0"`"
BASE=`pwd` BASE=`pwd`
SUDO="" python3 -m venv --system-site-packages .
PANDORA_USER=`ls -l update.py | cut -f3 -d" "`
if [ `whoami` != $PANDORA_USER ]; then
SUDO="sudo -H -u $PANDORA_USER"
fi
$SUDO python3 -m venv --system-site-packages .
branch=`cat .git/HEAD | sed 's@/@\n@g' | tail -n1` branch=`cat .git/HEAD | sed 's@/@\n@g' | tail -n1`
# Work around broken venv module in Ubuntu 16.04 / Debian 9 # Work around broken venv module in Ubuntu 16.04 / Debian 9
if [ ! -e bin/pip ]; then if [ ! -e bin/pip ]; then
$SUDO bin/python3 -m pip install -U --ignore-installed "pip<9" bin/python3 -m pip install -U --ignore-installed "pip<9"
fi fi
if [ ! -d static/oxjs ]; then if [ ! -d static/oxjs ]; then
$SUDO git clone --depth 1 -b $branch https://git.0x2620.org/oxjs.git static/oxjs git clone --depth 1 -b $branch https://git.0x2620.org/oxjs.git static/oxjs
fi fi
$SUDO mkdir -p src mkdir -p src
if [ ! -d src/oxtimelines ]; then if [ ! -d src/oxtimelines ]; then
$SUDO git clone --depth 1 -b $branch https://git.0x2620.org/oxtimelines.git src/oxtimelines git clone --depth 1 -b $branch https://git.0x2620.org/oxtimelines.git src/oxtimelines
fi fi
for package in oxtimelines python-ox; do for package in oxtimelines python-ox; do
cd ${BASE} cd ${BASE}
if [ ! -d src/${package} ]; then if [ ! -d src/${package} ]; then
$SUDO git clone --depth 1 -b $branch https://git.0x2620.org/${package}.git src/${package} git clone --depth 1 -b $branch https://git.0x2620.org/${package}.git src/${package}
fi fi
cd ${BASE}/src/${package} cd ${BASE}/src/${package}
$SUDO ${BASE}/bin/python setup.py develop ${BASE}/bin/python setup.py develop
done done
cd ${BASE} cd ${BASE}
$SUDO ./bin/pip install -r requirements.txt ./bin/pip install -r requirements.txt
if [ ! -e pandora/gunicorn_config.py ]; then if [ ! -e pandora/gunicorn_config.py ]; then
$SUDO cp pandora/gunicorn_config.py.in pandora/gunicorn_config.py cp pandora/gunicorn_config.py.in pandora/gunicorn_config.py
fi fi
exit 0 exit 0
fi fi

View file

@ -42,7 +42,7 @@ server {
proxy_set_header Proxy ""; proxy_set_header Proxy "";
proxy_redirect off; proxy_redirect off;
proxy_buffering off; proxy_buffering off;
proxy_read_timeout 99999; proxy_read_timeout 999999999;
proxy_pass http://127.0.0.1:2622/; proxy_pass http://127.0.0.1:2622/;
} }
@ -55,11 +55,11 @@ server {
proxy_buffering off; proxy_buffering off;
proxy_read_timeout 90; #should be in sync with gunicorn timeout proxy_read_timeout 90; #should be in sync with gunicorn timeout
proxy_connect_timeout 90; #should be in sync with gunicorn timeout proxy_connect_timeout 90; #should be in sync with gunicorn timeout
client_max_body_size 32m;
if (!-f $request_filename) { if (!-f $request_filename) {
proxy_pass http://127.0.0.1:2620; proxy_pass http://127.0.0.1:2620;
break; break;
} }
client_max_body_size 32m;
} }
error_page 400 /; error_page 400 /;

View file

@ -378,8 +378,6 @@ class Annotation(models.Model):
streams = self.item.streams() streams = self.item.streams()
if streams: if streams:
j['videoRatio'] = streams[0].aspect_ratio j['videoRatio'] = streams[0].aspect_ratio
if 'clip' in keys:
j[key] = self.clip.public_id
for key in keys: for key in keys:
if key not in j: if key not in j:
if key in self._clip_keys: if key in self._clip_keys:

View file

@ -1,13 +1,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import division, print_function, absolute_import from __future__ import division, print_function, absolute_import
import codecs
import os import os
import re import sys
import shutil import shutil
import subprocess import subprocess
import sys
import time import time
import codecs
from os.path import dirname, exists, join from os.path import dirname, exists, join
from glob import glob from glob import glob
@ -72,7 +71,7 @@ def load_config(init=False):
if getattr(settings, 'SITEURL', False): if getattr(settings, 'SITEURL', False):
config['site']['url'] = settings.SITEURL config['site']['url'] = settings.SITEURL
settings.URL = config['site']['url'] settings.URL = config['site']['url']
settings.EMAIL_SUBJECT_PREFIX = '[%s]' % settings.SITENAME settings.EMAIL_SUBJECT_PREFIX = '[%s]'%settings.SITENAME
settings.DEFAULT_FROM_EMAIL = config['site']['email']['system'] settings.DEFAULT_FROM_EMAIL = config['site']['email']['system']
settings.SERVER_EMAIL = config['site']['email']['system'] settings.SERVER_EMAIL = config['site']['email']['system']
config['site']['videoprefix'] = settings.VIDEO_PREFIX config['site']['videoprefix'] = settings.VIDEO_PREFIX
@ -80,32 +79,18 @@ def load_config(init=False):
config['site']['googleapikey'] = getattr(settings, 'GOOGLE_API_KEY') config['site']['googleapikey'] = getattr(settings, 'GOOGLE_API_KEY')
config['site']['version'] = get_version() config['site']['version'] = get_version()
config['site']['dontValidateUser'] = not settings.AUTH_CHECK_USERNAME config['site']['dontValidateUser'] = not settings.AUTH_CHECK_USERNAME
if 'folderdepth' not in config['site']: if not 'folderdepth' in config['site']:
config['site']['folderdepth'] = settings.USE_IMDB and 4 or 3 config['site']['folderdepth'] = settings.USE_IMDB and 4 or 3
if 'sendReferrer' in config and 'sendReferrer' not in config['site']: if 'sendReferrer' in config and not 'sendReferrer' in config['site']:
config['site']['sendReferrer'] = config.pop('sendReferrer') config['site']['sendReferrer'] = config.pop('sendReferrer')
# enable default filters if needed # enable default filters if needed
default_filters = [f['id'] for f in config['user']['ui']['filters']] default_filters = [f['id'] for f in config['user']['ui']['filters']]
available_filters = [key['id'] for key in config['itemKeys'] if key.get('filter')]
unknown_ids = set(default_filters) - set(available_filters)
if unknown_ids:
sys.stderr.write('WARNING: unknown item keys in default filters: %s.\n' % list(unknown_ids))
unused_filters = [key for key in available_filters if key not in default_filters]
if len(unused_filters) < len(unknown_ids):
sys.stderr.write('you need at least 5 item filters')
else:
auto_filters = unused_filters[:len(unknown_ids)]
default_filters += auto_filters
for key in auto_filters:
config['user']['ui']['filters'].append({
"id": key, "sort": [{"key": "items", "operator": "-"}]
})
sys.stderr.write(' using the following document filters instead: %s.\n' % auto_filters)
for key in config['itemKeys']: for key in config['itemKeys']:
if key['id'] in default_filters and not key.get('filter'): if key['id'] in default_filters and not key.get('filter'):
key['filter'] = True key['filter'] = True
sys.stderr.write('enabled filter for "%s" since its used as default filter.\n' % (key['id'])) sys.stderr.write('enabled filter for "%s" since its used as default filter.\n' % (key['id']))
config['keys'] = {} config['keys'] = {}
for key in config['itemKeys']: for key in config['itemKeys']:
config['keys'][key['id']] = key config['keys'][key['id']] = key
@ -163,17 +148,6 @@ def load_config(init=False):
if level not in config[key]: if level not in config[key]:
config[key] = default.get(key, 0) config[key] = default.get(key, 0)
config['user']['ui']['documentsSort'] = [
s for s in config['user']['ui']['documentsSort']
if get_by_id(config['documentKeys'], s['key'])
]
if not config['user']['ui']['documentsSort']:
sort_key = [k for k in config['documentKeys'] if k['id'] != '*'][0]
config['user']['ui']['documentsSort'] = [{
"key": sort_key['id'],
"operator": sort_key.get('operator', '+')
}]
for key in ('language', 'importMetadata'): for key in ('language', 'importMetadata'):
if key not in config: if key not in config:
sys.stderr.write("adding default value:\n\t\"%s\": %s,\n\n" % (key, json.dumps(default[key]))) sys.stderr.write("adding default value:\n\t\"%s\": %s,\n\n" % (key, json.dumps(default[key])))
@ -187,32 +161,6 @@ def load_config(init=False):
if 'downloadFormat' not in config['video']: if 'downloadFormat' not in config['video']:
config['video']['downloadFormat'] = default['video']['downloadFormat'] config['video']['downloadFormat'] = default['video']['downloadFormat']
# enable default document filters if needed
default_filters = [f['id'] for f in config['user']['ui']['documentFilters']]
available_filters = [key['id'] for key in config['documentKeys'] if key.get('filter')]
unknown_ids = set(default_filters) - set(available_filters)
if unknown_ids:
sys.stderr.write('WARNING: unknown document keys in default filters: %s.\n' % list(unknown_ids))
unused_filters = [key for key in available_filters if key not in default_filters]
if len(unused_filters) < len(unknown_ids):
sys.stderr.write('you need at least 5 item filters')
else:
auto_filters = unused_filters[:len(unknown_ids)]
default_filters += auto_filters
for key in auto_filters:
config['user']['ui']['documentFilters'].append({
"id": key, "sort": [{"key": "items", "operator": "-"}]
})
sys.stderr.write(' using the following document filters instead: %s.\n' % auto_filters)
for key in config['documentKeys']:
if key['id'] in default_filters and not key.get('filter'):
key['filter'] = True
sys.stderr.write('enabled filter for document key "%s" since its used as default filter.\n' % (key['id']))
old_formats = getattr(settings, 'CONFIG', {}).get('video', {}).get('formats', []) old_formats = getattr(settings, 'CONFIG', {}).get('video', {}).get('formats', [])
formats = config.get('video', {}).get('formats') formats = config.get('video', {}).get('formats')
if set(old_formats) != set(formats): if set(old_formats) != set(formats):
@ -400,17 +348,11 @@ def update_geoip(force=False):
path = os.path.join(settings.GEOIP_PATH, 'GeoLite2-City.mmdb') path = os.path.join(settings.GEOIP_PATH, 'GeoLite2-City.mmdb')
if not os.path.exists(path) or force: if not os.path.exists(path) or force:
url = 'http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.mmdb.gz' url = 'http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.mmdb.gz'
index = ox.net.read_url('https://db-ip.com/db/download/ip-to-country-lite').decode() print('download', url)
match = re.compile('href=[\'"](http.*.mmdb.gz)').findall(index) ox.net.save_url(url, "%s.gz"%path)
if match: if os.path.exists(path):
url = match[0] os.unlink(path)
print('download', url) os.system('gunzip "%s.gz"' % path)
ox.net.save_url(url, "%s.gz" % path)
if os.path.exists(path):
os.unlink(path)
os.system('gunzip "%s.gz"' % path)
else:
print('failed to download dbip-country-lite-2020-03.mmdb.gz')
def init(): def init():
if not settings.RELOADER_RUNNING: if not settings.RELOADER_RUNNING:

View file

@ -97,17 +97,6 @@ def download(item_id, url):
tmp = tmp.decode('utf-8') tmp = tmp.decode('utf-8')
os.chdir(tmp) os.chdir(tmp)
cmd = ['youtube-dl', '-q', media['url']] cmd = ['youtube-dl', '-q', media['url']]
if settings.CONFIG['video'].get('reuseUload', False):
max_resolution = max(settings.CONFIG['video']['resolutions'])
format = settings.CONFIG['video']['formats'][0]
if format == 'mp4':
cmd += [
'-f', 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/bestvideo+bestaudio',
'--merge-output-format', 'mp4'
]
elif format == 'webm':
cmd += ['--merge-output-format', 'webm']
p = subprocess.Popen(cmd, p = subprocess.Popen(cmd,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, close_fds=True) stderr=subprocess.PIPE, close_fds=True)

View file

@ -191,7 +191,7 @@ def stream(video, target, profile, info, audio_track=0, flags={}):
w = info['video'][0]['width'] - flags['crop']['left'] - flags['crop']['right'] w = info['video'][0]['width'] - flags['crop']['left'] - flags['crop']['right']
x = flags['crop']['left'] x = flags['crop']['left']
y = flags['crop']['top'] y = flags['crop']['top']
crop = ',crop=w=%s:h=%s:x=%s:y=%s' % (w, h, x, y) crop = ',crop=w=%s:h=%s:x=%s:y=%s' (w, h, x, y)
aspect = dar * (info['video'][0]['width'] / info['video'][0]['height']) * (w/h) aspect = dar * (info['video'][0]['width'] / info['video'][0]['height']) * (w/h)
if abs(w/h - aspect) < 0.02: if abs(w/h - aspect) < 0.02:
aspect = '%s:%s' % (w, h) aspect = '%s:%s' % (w, h)
@ -216,7 +216,6 @@ def stream(video, target, profile, info, audio_track=0, flags={}):
'-vb', '%dk' % bitrate, '-vb', '%dk' % bitrate,
'-aspect', aspect, '-aspect', aspect,
# '-vf', 'yadif', # '-vf', 'yadif',
'-max_muxing_queue_size', '512',
'-vf', 'hqdn3d%s,scale=%s:%s' % (crop, width, height), '-vf', 'hqdn3d%s,scale=%s:%s' % (crop, width, height),
'-g', '%d' % int(fps*5), '-g', '%d' % int(fps*5),
] ]
@ -239,7 +238,6 @@ def stream(video, target, profile, info, audio_track=0, flags={}):
'-preset:v', 'medium', '-preset:v', 'medium',
'-profile:v', 'high', '-profile:v', 'high',
'-level', '4.0', '-level', '4.0',
'-pix_fmt', 'yuv420p',
] ]
video_settings += ['-map', '0:%s,0:0' % info['video'][0]['id']] video_settings += ['-map', '0:%s,0:0' % info['video'][0]['id']]
audio_only = False audio_only = False
@ -611,14 +609,11 @@ def timeline_strip(item, cuts, info, prefix):
timeline_image.save(timeline_file) timeline_image.save(timeline_file)
def chop(video, start, end, subtitles=None, dest=None, encode=False): def chop(video, start, end, subtitles=None):
t = end - start t = end - start
tmp = tempfile.mkdtemp()
ext = os.path.splitext(video)[1] ext = os.path.splitext(video)[1]
if dest is None: choped_video = '%s/tmp%s' % (tmp, ext)
tmp = tempfile.mkdtemp()
choped_video = '%s/tmp%s' % (tmp, ext)
else:
choped_video = dest
if subtitles and ext == '.mp4': if subtitles and ext == '.mp4':
subtitles_f = choped_video + '.full.srt' subtitles_f = choped_video + '.full.srt'
with open(subtitles_f, 'wb') as fd: with open(subtitles_f, 'wb') as fd:
@ -630,167 +625,25 @@ def chop(video, start, end, subtitles=None, dest=None, encode=False):
if subtitles_f: if subtitles_f:
os.unlink(subtitles_f) os.unlink(subtitles_f)
else: else:
if encode:
bpp = 0.17
if ext == '.mp4':
vcodec = [
'-c:v', 'libx264',
'-preset:v', 'medium',
'-profile:v', 'high',
'-level', '4.0',
]
acodec = [
'-c:a', 'aac',
'-aq', '6',
'-strict', '-2'
]
else:
vcodec = [
'-c:v', 'libvpx',
'-deadline', 'good',
'-cpu-used', '0',
'-lag-in-frames', '25',
'-auto-alt-ref', '1',
]
acodec = [
'-c:a', 'libvorbis',
'-aq', '6',
]
info = ox.avinfo(video)
if not info['audio']:
acodec = []
if not info['video']:
vcodec = []
else:
height = info['video'][0]['height']
width = info['video'][0]['width']
fps = 30
bitrate = height*width*fps*bpp/1000
vcodec += ['-vb', '%dk' % bitrate]
encoding = vcodec + acodec
else:
encoding = [
'-c:v', 'copy',
'-c:a', 'copy',
]
cmd = [ cmd = [
settings.FFMPEG, settings.FFMPEG,
'-y', '-y',
'-i', video, '-i', video,
'-ss', '%.3f' % start, '-ss', '%.3f' % start,
'-t', '%.3f' % t, '-t', '%.3f' % t,
] + encoding + [ '-c:v', 'copy',
'-c:a', 'copy',
'-f', ext[1:], '-f', ext[1:],
choped_video choped_video
] ]
print(cmd)
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, p = subprocess.Popen(cmd, stdin=subprocess.PIPE,
stdout=open('/dev/null', 'w'), stdout=open('/dev/null', 'w'),
stderr=open('/dev/null', 'w'), stderr=open('/dev/null', 'w'),
close_fds=True) close_fds=True)
p.wait() p.wait()
f = open(choped_video, 'rb')
os.unlink(choped_video)
if subtitles_f and os.path.exists(subtitles_f): if subtitles_f and os.path.exists(subtitles_f):
os.unlink(subtitles_f) os.unlink(subtitles_f)
if dest is None: os.rmdir(tmp)
f = open(choped_video, 'rb') return f
os.unlink(choped_video)
os.rmdir(tmp)
return f
else:
return None
def has_faststart(path):
cmd = [settings.FFPROBE, '-v', 'trace', '-i', path]
p = subprocess.Popen(cmd, stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
close_fds=True)
stdout, stderr = p.communicate()
moov = "type:'moov'"
mdat = "type:'mdat'"
blocks = [b for b in stdout.decode().split('\n') if moov in b or mdat in b]
if blocks and moov in blocks[0]:
return True
return False
def remux_stream(src, dst):
info = ox.avinfo(src)
if info.get('audio'):
audio = ['-c:a', 'copy']
else:
audio = []
if info.get('video'):
video = ['-c:v', 'copy']
else:
video = []
cmd = [
settings.FFMPEG,
'-nostats', '-loglevel', 'error',
'-map_metadata', '-1', '-sn',
'-i', src,
] + video + [
] + audio + [
'-movflags', '+faststart',
dst
]
p = subprocess.Popen(cmd, stdin=subprocess.PIPE,
stdout=open('/dev/null', 'w'),
stderr=open('/dev/null', 'w'),
close_fds=True)
p.wait()
return True, None
def ffprobe(path, *args):
cmd = [settings.FFPROBE, '-loglevel', 'error', '-print_format', 'json', '-i', path] + list(args)
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
stdout, stderr = p.communicate()
return json.loads(stdout.decode())
def get_chapters(path):
info = ffprobe(path, '-show_chapters')
chapters = []
n = 0
for chapter in info.get('chapters', []):
n += 1
chapters.append({
'in': chapter['start_time'],
'out': chapter['end_time'],
'value': chapter.get('tags', {}).get('title', 'Chapter %s' % n)
})
return chapters
def get_text_subtitles(path):
subtitles = []
for stream in ffprobe(path, '-show_streams')['streams']:
if stream.get('codec_name') in ('subrip', 'aas', 'text'):
subtitles.append({
'index': stream['index'],
'language': stream['tags']['language'],
})
return subtitles
def has_img_subtitles(path):
subtitles = []
for stream in ffprobe(path, '-show_streams')['streams']:
if stream.get('codec_type') == 'subtitle' and stream.get('codec_name') in ('dvbsub', 'pgssub'):
subtitles.append({
'index': stream['index'],
'language': stream['tags']['language'],
})
return subtitles
def extract_subtitles(path, language=None):
extra = []
if language:
tracks = get_text_subtitles(path)
track = [t for t in tracks if t['language'] == language]
if track:
extra = ['-map', '0:%s' % track[0]['index']]
else:
raise Exception("unknown language: %s" % language)
cmd = ['ffmpeg', '-loglevel', 'error', '-i', path] + extra + ['-f', 'srt', '-']
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
stdout, stderr = p.communicate()
return ox.srt.loads(stdout.decode())

View file

@ -376,50 +376,6 @@ class File(models.Model):
return save_chunk(stream, stream.media, chunk, offset, name, done_cb) return save_chunk(stream, stream.media, chunk, offset, name, done_cb)
return False, 0 return False, 0
def extract_text_data(self):
if self.data:
for sub in extract.get_text_subtitles(self.data.path):
srt = extract.extract_subtitles(self.data.path, sub['language'])
# fixme add subtitles, possibly with language!
chapters = extract.get_chapters(self.data.path)
if chapters:
# fixme add chapters as notes
pass
def get_codec(self, type):
track = self.info.get(type)
if track:
return track[0].get('codec')
MP4_VCODECS = ['h264']
MP4_ACODECS = ['aac', None]
WEBM_VCODECS = ['vp8', 'vp9']
WEBM_ACODECS = ['vorbis', 'opus', None]
def can_remux(self):
config = settings.CONFIG['video']
height = self.info['video'][0]['height'] if self.info.get('video') else None
max_resolution = max(config['resolutions'])
if height <= max_resolution and self.extension in ('mov', 'mkv', 'mp4', 'm4v'):
vcodec = self.get_codec('video')
acodec = self.get_codec('audio')
if vcodec in self.MP4_VCODECS and acodec in self.MP4_ACODECS:
return True
return False
def can_stream(self):
config = settings.CONFIG['video']
height = self.info['video'][0]['height'] if self.info.get('video') else None
max_resolution = max(config['resolutions'])
if height <= max_resolution and config['formats'][0] == self.extension:
vcodec = self.get_codec('video')
acodec = self.get_codec('audio')
if self.extension in ['mp4', 'm4v'] and vcodec in self.MP4_VCODECS and acodec in self.MP4_ACODECS:
return extract.has_faststart(self.data.path)
elif self.extension == 'webm' and vcodec in self.WEBM_VCODECS and acodec in self.WEBM_ACODECS:
return True
return False
def stream_resolution(self): def stream_resolution(self):
config = settings.CONFIG['video'] config = settings.CONFIG['video']
height = self.info['video'][0]['height'] if self.info.get('video') else None height = self.info['video'][0]['height'] if self.info.get('video') else None
@ -567,7 +523,7 @@ class File(models.Model):
n += 1 n += 1
profile = '%sp.%s' % (resolution, config['formats'][0]) profile = '%sp.%s' % (resolution, config['formats'][0])
target = os.path.join(tmp, language + '_' + profile) target = os.path.join(tmp, language + '_' + profile)
ok, error = extract.stream(media, target, profile, info, audio_track=i+1, flags={}) ok, error = extract.stream(media, target, profile, info, audio_track=i+1, flags=self.flags)
if ok: if ok:
tinfo = ox.avinfo(target) tinfo = ox.avinfo(target)
del tinfo['path'] del tinfo['path']
@ -791,31 +747,18 @@ class Stream(models.Model):
derivative.encode() derivative.encode()
def encode(self): def encode(self):
reuse = settings.CONFIG['video'].get('reuseUpload', False)
media = self.source.media.path if self.source else self.file.data.path media = self.source.media.path if self.source else self.file.data.path
if not self.media: if not self.media:
self.media.name = self.path(self.name()) self.media.name = self.path(self.name())
target = self.media.path target = self.media.path
info = ox.avinfo(media) info = ox.avinfo(media)
ok, error = extract.stream(media, target, self.name(), info, flags=self.flags)
done = False
if reuse and not self.source:
if self.file.can_stream():
ok, error = True, None
ox.makedirs(os.path.dirname(target))
shutil.move(self.file.data.path, target)
self.file.data.name = ''
self.file.save()
elif self.file.can_remux():
ok, error = extract.remux_stream(media, target)
done = True
if not done:
ok, error = extract.stream(media, target, self.name(), info, flags=self.flags)
# file could have been moved while encoding # file could have been moved while encoding
# get current version from db and update # get current version from db and update
self.refresh_from_db() _self = Stream.objects.get(id=self.id)
self.update_status(ok, error) _self.update_status(ok, error)
return _self
def get_index(self): def get_index(self):
index = 1 index = 1

View file

@ -128,7 +128,7 @@ def process_stream(fileId):
stream = streams[0] stream = streams[0]
stream.make_timeline() stream.make_timeline()
stream.extract_derivatives() stream.extract_derivatives()
file.refresh_from_db() file = models.File.objects.get(id=fileId)
file.encoding = False file.encoding = False
file.save() file.save()
file.item.update_selected() file.item.update_selected()
@ -158,12 +158,13 @@ def extract_stream(fileId):
if created: if created:
file.extract_frames() file.extract_frames()
stream.media.name = stream.path(stream.name()) stream.media.name = stream.path(stream.name())
stream.encode() stream = stream.encode()
if stream.available: if stream.available:
stream.make_timeline() stream.make_timeline()
stream.extract_derivatives() stream.extract_derivatives()
file.extract_tracks() file.extract_tracks()
file.refresh_from_db() # get current version from db
file = models.File.objects.get(id=fileId)
if not file.item.rendered \ if not file.item.rendered \
and not file.item.files.exclude(id=fileId).filter(Q(queued=True) | Q(encoding=True)).count(): and not file.item.files.exclude(id=fileId).filter(Q(queued=True) | Q(encoding=True)).count():
file.item.update_timeline() file.item.update_timeline()
@ -208,8 +209,7 @@ def download_media(item_id, url):
@task(queue='default') @task(queue='default')
def move_media(data, user): def move_media(data, user):
from changelog.models import add_changelog from changelog.models import add_changelog
from item.models import get_item, Item, ItemSort from item.models import get_item, Item
from item.utils import is_imdb_id
from annotation.models import Annotation from annotation.models import Annotation
user = models.User.objects.get(username=user) user = models.User.objects.get(username=user)
@ -218,7 +218,7 @@ def move_media(data, user):
i = Item.objects.get(public_id=data['item']) i = Item.objects.get(public_id=data['item'])
else: else:
data['public_id'] = data.pop('item').strip() data['public_id'] = data.pop('item').strip()
if not is_imdb_id(data['public_id']): if len(data['public_id']) != 7:
del data['public_id'] del data['public_id']
if 'director' in data and isinstance(data['director'], string_types): if 'director' in data and isinstance(data['director'], string_types):
if data['director'] == '': if data['director'] == '':
@ -228,11 +228,6 @@ def move_media(data, user):
i = get_item(data, user=user) i = get_item(data, user=user)
else: else:
i = get_item({'imdbId': data['public_id']}, user=user) i = get_item({'imdbId': data['public_id']}, user=user)
try:
i.sort
except ItemSort.DoesNotExist:
i.update_sort()
changed = [i.public_id] changed = [i.public_id]
old_item = None old_item = None
for f in models.File.objects.filter(oshash__in=data['ids']): for f in models.File.objects.filter(oshash__in=data['ids']):

View file

@ -368,7 +368,7 @@ def direct_upload(request):
return render_to_json_response(response) return render_to_json_response(response)
#@login_required_json @login_required_json
def getTaskStatus(request, data): def getTaskStatus(request, data):
''' '''
Gets the status for a given task Gets the status for a given task

View file

@ -121,9 +121,7 @@ class MetaClip(object):
annotations = annotations.filter(q) annotations = annotations.filter(q)
entity_cache = {} entity_cache = {}
j['annotations'] = [ j['annotations'] = [
a.json(keys=['value', 'id', 'layer'], entity_cache=entity_cache) a.json(keys=['value', 'id', 'layer'], entity_cache=entity_cache) for a in annotations
for a in annotations
if a.value
] ]
if 'layers' in keys: if 'layers' in keys:
j['layers'] = self.get_layers() j['layers'] = self.get_layers()

View file

@ -78,7 +78,7 @@ def findClips(request, data):
takes { takes {
query: object, // find clips, query object, see `find` query: object, // find clips, query object, see `find`
itemsQuery: object, // limit to matching items, query object, see `find` itemsQuery: object, // limit to matching items, query object, see `find`
keys: [string], // list of properties to return, include 'annotations' to get all annotations for a clip keys: [string], // list of properties to return
positions: [int], // list of positions positions: [int], // list of positions
range: [int, int], // range of results to return range: [int, int], // range of results to return
sort: [object] // list of sort objects, see `find` sort: [object] // list of sort objects, see `find`
@ -102,6 +102,8 @@ def findClips(request, data):
subtitles = utils.get_by_key(layers, 'isSubtitles', True) subtitles = utils.get_by_key(layers, 'isSubtitles', True)
layer_ids = [k['id'] for k in layers] layer_ids = [k['id'] for k in layers]
keys = list(filter(lambda k: k not in layer_ids + ['annotations'], data['keys'])) keys = list(filter(lambda k: k not in layer_ids + ['annotations'], data['keys']))
if list(filter(lambda k: k not in models.Clip.clip_keys, keys)):
qs = qs.select_related('item__sort')
clips = {} clips = {}
response['data']['items'] = clip_jsons = [] response['data']['items'] = clip_jsons = []

View file

@ -38,7 +38,6 @@
"canAddItems": {"staff": true, "admin": true}, "canAddItems": {"staff": true, "admin": true},
"canAddDocuments": {"staff": true, "admin": true}, "canAddDocuments": {"staff": true, "admin": true},
"canDownloadVideo": {"guest": -1, "member": -1, "friend": -1, "staff": -1, "admin": -1}, "canDownloadVideo": {"guest": -1, "member": -1, "friend": -1, "staff": -1, "admin": -1},
"canDownloadSource": {"guest": -1, "member": -1, "friend": -1, "staff": -1, "admin": -1},
"canEditAnnotations": {"staff": true, "admin": true}, "canEditAnnotations": {"staff": true, "admin": true},
"canEditEntities": {"staff": true, "admin": true}, "canEditEntities": {"staff": true, "admin": true},
"canEditDocuments": {"staff": true, "admin": true}, "canEditDocuments": {"staff": true, "admin": true},
@ -98,7 +97,7 @@
text of clips (in grid view, below the icon). Excluding a layer from this text of clips (in grid view, below the icon). Excluding a layer from this
list means it will not be included in find annotations. list means it will not be included in find annotations.
*/ */
"clipLayers": ["subtitles", "keywords"], "clipLayers": ["subtitles"],
"documentKeys": [ "documentKeys": [
{ {
"id": "*", "id": "*",
@ -710,14 +709,6 @@
"advanced": true, "advanced": true,
"find": true "find": true
}, },
{
"id": "tags",
"title": "Tags",
"type": "layer",
"autocomplete": true,
"filter": true,
"find": true
},
{ {
"id": "subtitles", "id": "subtitles",
"title": "Subtitles", "title": "Subtitles",
@ -1006,15 +997,6 @@
tooltip that appears on mouseover. tooltip that appears on mouseover.
*/ */
"layers": [ "layers": [
{
"id": "tags",
"title": "Tags",
"canAddAnnotations": {"member": true, "staff": true, "admin": true},
"item": "Tag",
"autocomplete": true,
"overlap": true,
"type": "string"
},
{ {
"id": "privatenotes", "id": "privatenotes",
"title": "Private Notes", "title": "Private Notes",

View file

@ -39,7 +39,6 @@
"canAddItems": {"researcher": true, "staff": true, "admin": true}, "canAddItems": {"researcher": true, "staff": true, "admin": true},
"canAddDocuments": {"researcher": true, "staff": true, "admin": true}, "canAddDocuments": {"researcher": true, "staff": true, "admin": true},
"canDownloadVideo": {"guest": -1, "member": -1, "researcher": 3, "staff": 3, "admin": 3}, "canDownloadVideo": {"guest": -1, "member": -1, "researcher": 3, "staff": 3, "admin": 3},
"canDownloadSource": {"guest": -1, "member": -1, "researcher": -1, "staff": -1, "admin": -1},
"canEditAnnotations": {"staff": true, "admin": true}, "canEditAnnotations": {"staff": true, "admin": true},
"canEditDocuments": {"researcher": true, "staff": true, "admin": true}, "canEditDocuments": {"researcher": true, "staff": true, "admin": true},
"canEditEntities": {"staff": true, "admin": true}, "canEditEntities": {"staff": true, "admin": true},
@ -76,7 +75,7 @@
"canSeeExtraItemViews": {"researcher": true, "staff": true, "admin": true}, "canSeeExtraItemViews": {"researcher": true, "staff": true, "admin": true},
"canSeeMedia": {"researcher": true, "staff": true, "admin": true}, "canSeeMedia": {"researcher": true, "staff": true, "admin": true},
"canSeeDocument": {"guest": 1, "member": 1, "researcher": 2, "staff": 3, "admin": 3}, "canSeeDocument": {"guest": 1, "member": 1, "researcher": 2, "staff": 3, "admin": 3},
"canSeeItem": {"guest": 2, "member": 2, "researcher": 2, "staff": 3, "admin": 3}, "canSeeItem": {"guest": 3, "member": 3, "researcher": 3, "staff": 3, "admin": 3},
"canSeeSize": {"researcher": true, "staff": true, "admin": true}, "canSeeSize": {"researcher": true, "staff": true, "admin": true},
"canSeeSoftwareVersion": {"researcher": true, "staff": true, "admin": true}, "canSeeSoftwareVersion": {"researcher": true, "staff": true, "admin": true},
"canSendMail": {"staff": true, "admin": true} "canSendMail": {"staff": true, "admin": true}
@ -1696,7 +1695,7 @@
"annotationsCalendarSize": 128, "annotationsCalendarSize": 128,
"annotationsHighlight": "none", "annotationsHighlight": "none",
"annotationsMapSize": 128, "annotationsMapSize": 128,
"annotationsRange": "selection", "annotationsRange": "all",
"annotationsSize": 256, "annotationsSize": 256,
"annotationsSort": "position", "annotationsSort": "position",
"calendarFind": "", "calendarFind": "",
@ -1853,7 +1852,7 @@
"videoSize": "small", "videoSize": "small",
"videoSubtitles": true, "videoSubtitles": true,
"videoSubtitlesOffset": 0, "videoSubtitlesOffset": 0,
"videoTimeline": "keyframes", "videoTimeline": "slitscan",
"videoView": "player", "videoView": "player",
"videoVolume": 1 "videoVolume": 1
}, },

View file

@ -38,7 +38,6 @@
"canAddItems": {"member": true, "staff": true, "admin": true}, "canAddItems": {"member": true, "staff": true, "admin": true},
"canAddDocuments": {"member": true, "staff": true, "admin": true}, "canAddDocuments": {"member": true, "staff": true, "admin": true},
"canDownloadVideo": {"guest": 0, "member": 0, "staff": 4, "admin": 4}, "canDownloadVideo": {"guest": 0, "member": 0, "staff": 4, "admin": 4},
"canDownloadSource": {"guest": -1, "member": -1, "staff": 4, "admin": 4},
"canEditAnnotations": {"staff": true, "admin": true}, "canEditAnnotations": {"staff": true, "admin": true},
"canEditEntities": {"staff": true, "admin": true}, "canEditEntities": {"staff": true, "admin": true},
"canEditDocuments": {"staff": true, "admin": true}, "canEditDocuments": {"staff": true, "admin": true},
@ -1161,7 +1160,7 @@
"annotationsHighlight": "none", "annotationsHighlight": "none",
"annotationsHighlight": false, "annotationsHighlight": false,
"annotationsMapSize": 128, "annotationsMapSize": 128,
"annotationsRange": "selection", "annotationsRange": "position",
"annotationsSize": 256, "annotationsSize": 256,
"annotationsSort": "position", "annotationsSort": "position",
"calendarFind": "", "calendarFind": "",
@ -1311,7 +1310,7 @@
"videoSize": "large", "videoSize": "large",
"videoSubtitles": false, "videoSubtitles": false,
"videoSubtitlesOffset": 0, "videoSubtitlesOffset": 0,
"videoTimeline": "keyframes", "videoTimeline": "antialias",
"videoView": "player", "videoView": "player",
"videoVolume": 1 "videoVolume": 1
}, },

View file

@ -45,7 +45,6 @@ examples (config.SITENAME.jsonc) that are part of this pan.do/ra distribution.
"canAddItems": {"member": true, "staff": true, "admin": true}, "canAddItems": {"member": true, "staff": true, "admin": true},
"canAddDocuments": {"member": true, "staff": true, "admin": true}, "canAddDocuments": {"member": true, "staff": true, "admin": true},
"canDownloadVideo": {"guest": 1, "member": 1, "staff": 4, "admin": 4}, "canDownloadVideo": {"guest": 1, "member": 1, "staff": 4, "admin": 4},
"canDownloadSource": {"member": 1, "staff": 4, "admin": 4},
"canEditAnnotations": {"staff": true, "admin": true}, "canEditAnnotations": {"staff": true, "admin": true},
"canEditDocuments": {"staff": true, "admin": true}, "canEditDocuments": {"staff": true, "admin": true},
"canEditEntities": {"staff": true, "admin": true}, "canEditEntities": {"staff": true, "admin": true},
@ -551,7 +550,7 @@ examples (config.SITENAME.jsonc) that are part of this pan.do/ra distribution.
{ {
"id": "country", "id": "country",
"title": "Country", "title": "Country",
"type": ["string"], "type": "string",
"autocomplete": true, "autocomplete": true,
"columnWidth": 180, "columnWidth": 180,
"filter": true, "filter": true,
@ -991,6 +990,11 @@ examples (config.SITENAME.jsonc) that are part of this pan.do/ra distribution.
{"name": "Private", "color": [255, 128, 128]} {"name": "Private", "color": [255, 128, 128]}
], ],
/* /*
"sendReferrer", if set to false, will cause all outgoing links to originate
from one single URL
*/
"sendReferrer": false,
/*
"site" contains various settings for this instance. In "email", "contact" "site" contains various settings for this instance. In "email", "contact"
if the address in the contact form (to), "system" is the address used by if the address in the contact form (to), "system" is the address used by
the system (from). the system (from).
@ -1274,6 +1278,6 @@ examples (config.SITENAME.jsonc) that are part of this pan.do/ra distribution.
"formats": ["webm", "mp4"], "formats": ["webm", "mp4"],
"previewRatio": 1.3333333333, "previewRatio": 1.3333333333,
"resolutions": [240, 480], "resolutions": [240, 480],
"torrent": false "torrent": true
} }
} }

View file

@ -1,97 +0,0 @@
import subprocess
from django.conf import settings
def extract_text(pdf):
cmd = ['pdftotext', pdf, '-']
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
stdout = stdout.decode()
return stdout.strip()
def ocr_image(path):
cmd = ['tesseract', path, '-', 'txt']
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
stdout = stdout.decode()
return stdout.strip()
class FulltextMixin:
_ES_INDEX = "document-index"
@classmethod
def elasticsearch(cls):
from elasticsearch import Elasticsearch
es = Elasticsearch(settings.ELASTICSEARCH_HOST)
return es
def extract_fulltext(self):
if self.file:
if self.extension == 'pdf':
return extract_text(self.file.path)
elif self.extension in ('png', 'jpg'):
return ocr_image(self.file.path)
elif self.extension == 'html':
return self.data.get('text', '')
return ''
def has_fulltext_key(self):
return bool([k for k in settings.CONFIG['documentKeys'] if k.get('fulltext')])
def delete_fulltext(self):
if self.has_fulltext_key():
from elasticsearch.exceptions import NotFoundError
try:
res = self.elasticsearch().delete(index=self._ES_INDEX, doc_type='document', id=self.id)
except NotFoundError:
pass
def update_fulltext(self):
if self.has_fulltext_key():
text = self.extract_fulltext()
if text:
doc = {
'text': text.lower()
}
res = self.elasticsearch().index(index=self._ES_INDEX, doc_type='document', id=self.id, body=doc)
@classmethod
def find_fulltext(cls, query):
ids = cls.find_fulltext_ids(query)
return cls.objects.filter(id__in=ids)
@classmethod
def find_fulltext_ids(cls, query):
if not query:
return []
elif query[0] == '"' and query[-1] == '"':
query = {
"match_phrase": {
"text": query.lower()[1:-1]
},
}
else:
query = {
"match": {
"text": {
"query": query.lower(),
"operator": "and"
}
}
}
ids = []
res = None
from_ = 0
es = cls.elasticsearch()
while not res or len(ids) < res['hits']['total']['value']:
res = es.search(index=cls._ES_INDEX, body={
"from": from_,
"_source": False,
"query": query
})
if not res['hits']['hits']:
break
ids += [int(r['_id']) for r in res['hits']['hits']]
from_ += len(res['hits']['hits'])
return ids

View file

@ -36,8 +36,6 @@ def get_key_type(k):
}.get(key_type, key_type) }.get(key_type, key_type)
return key_type return key_type
def parseCondition(condition, user, item=None, owner=None): def parseCondition(condition, user, item=None, owner=None):
''' '''
''' '''
@ -70,9 +68,6 @@ def buildCondition(k, op, v, user, exclude=False, owner=None):
k = 'collection' k = 'collection'
key_type = get_key_type(k) key_type = get_key_type(k)
key_config = (utils.get_by_id(settings.CONFIG['documentKeys'], k) or {'type': 'string'})
facet_keys = models.Document.facet_keys facet_keys = models.Document.facet_keys
if k == 'id': if k == 'id':
if op == '&' and isinstance(v, list): if op == '&' and isinstance(v, list):
@ -133,12 +128,6 @@ def buildCondition(k, op, v, user, exclude=False, owner=None):
else: else:
q = Q(id=0) q = Q(id=0)
return q return q
elif key_config.get('fulltext'):
qs = models.Document.find_fulltext_ids(v)
q = Q(id__in=qs)
if exclude:
q = ~Q(id__in=qs)
return q
elif key_type == 'boolean': elif key_type == 'boolean':
q = Q(**{'find__key': k, 'find__value': v}) q = Q(**{'find__key': k, 'find__value': v})
if exclude: if exclude:

View file

@ -30,8 +30,6 @@ from user.models import Group
from . import managers from . import managers
from . import utils from . import utils
from . import tasks
from .fulltext import FulltextMixin
User = get_user_model() User = get_user_model()
@ -42,7 +40,7 @@ def get_path(f, x):
return f.path(x) return f.path(x)
@python_2_unicode_compatible @python_2_unicode_compatible
class Document(models.Model, FulltextMixin): class Document(models.Model):
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True) modified = models.DateTimeField(auto_now=True)
@ -155,8 +153,6 @@ class Document(models.Model, FulltextMixin):
i = key['id'] i = key['id']
if i == 'rightslevel': if i == 'rightslevel':
save(i, self.rightslevel) save(i, self.rightslevel)
if key.get('fulltext'):
continue
elif i not in ('*', 'dimensions') and i not in self.facet_keys: elif i not in ('*', 'dimensions') and i not in self.facet_keys:
value = data.get(i) value = data.get(i)
if isinstance(value, list): if isinstance(value, list):
@ -413,8 +409,6 @@ class Document(models.Model, FulltextMixin):
and document_key['value'].get('type') == 'map' \ and document_key['value'].get('type') == 'map' \
and self.get_value(document_key['value']['key']): and self.get_value(document_key['value']['key']):
value = re.compile(document_key['value']['map']).findall(self.get_value(document_key['value']['key'])) value = re.compile(document_key['value']['map']).findall(self.get_value(document_key['value']['key']))
if value and document_key['value'].get('format'):
value = [document_key['value']['format'].format(value[0])]
return value[0] if value else default return value[0] if value else default
elif key == 'user': elif key == 'user':
return self.user.username return self.user.username
@ -508,7 +502,6 @@ class Document(models.Model, FulltextMixin):
self.oshash = ox.oshash(self.file.path) self.oshash = ox.oshash(self.file.path)
self.save() self.save()
self.delete_cache() self.delete_cache()
tasks.extract_fulltext.delay(self.id)
return True, self.file.size return True, self.file.size
return save_chunk(self, self.file, chunk, offset, name, done_cb) return save_chunk(self, self.file, chunk, offset, name, done_cb)
@ -525,14 +518,8 @@ class Document(models.Model, FulltextMixin):
else: else:
path = src path = src
if self.extension == 'pdf': if self.extension == 'pdf':
crop = []
if page: if page:
if ',' in page: page = int(page)
crop = list(map(int, page.split(',')))
page = crop[0]
crop = crop[1:]
else:
page = int(page)
if page and page > 1 and page <= self.pages: if page and page > 1 and page <= self.pages:
src = os.path.join(folder, '1024p%d.jpg' % page) src = os.path.join(folder, '1024p%d.jpg' % page)
else: else:
@ -542,18 +529,6 @@ class Document(models.Model, FulltextMixin):
self.extract_page(page) self.extract_page(page)
if size: if size:
path = os.path.join(folder, '%dp%d.jpg' % (size, page)) path = os.path.join(folder, '%dp%d.jpg' % (size, page))
if len(crop) == 4:
path = os.path.join(folder, '%dp%d,%s.jpg' % (1024, page, ','.join(map(str, crop))))
if not os.path.exists(path):
img = Image.open(src).crop(crop)
img.save(path)
else:
img = Image.open(path)
src = path
if size < max(img.size):
path = os.path.join(folder, '%dp%d,%s.jpg' % (size, page, ','.join(map(str, crop))))
if not os.path.exists(path):
resize_image(src, path, size=size)
elif self.extension in ('jpg', 'png', 'gif'): elif self.extension in ('jpg', 'png', 'gif'):
if os.path.exists(src): if os.path.exists(src):
if size and page: if size and page:
@ -674,7 +649,6 @@ def delete_document(sender, **kwargs):
if t.file: if t.file:
t.delete_cache() t.delete_cache()
t.file.delete(save=False) t.file.delete(save=False)
t.delete_fulltext()
pre_delete.connect(delete_document, sender=Document) pre_delete.connect(delete_document, sender=Document)
class ItemProperties(models.Model): class ItemProperties(models.Model):

View file

@ -1,8 +0,0 @@
# -*- coding: utf-8 -*-
from celery.task import task
@task(queue="encoding")
def extract_fulltext(id):
from . import models
d = models.Document.objects.get(id=id)
d.update_fulltext()

View file

@ -15,7 +15,7 @@ def pdfinfo(pdf):
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True) p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
stdout, stderr = p.communicate() stdout, stderr = p.communicate()
data = {} data = {}
for line in stdout.decode('utf-8', 'replace').strip().split('\n'): for line in stdout.decode('utf-8').strip().split('\n'):
parts = line.split(':') parts = line.split(':')
key = parts[0].lower().strip() key = parts[0].lower().strip()
if key: if key:

View file

@ -210,7 +210,7 @@ def parse_query(data, user):
for key in ('keys', 'group', 'file', 'range', 'position', 'positions', 'sort'): for key in ('keys', 'group', 'file', 'range', 'position', 'positions', 'sort'):
if key in data: if key in data:
query[key] = data[key] query[key] = data[key]
#print(query.get('sort'), data.get('sort')) print(query.get('sort'), data.get('sort'))
query['qs'] = models.Document.objects.find(data, user) query['qs'] = models.Document.objects.find(data, user)
query['item'] = get_item(data.get('query', {})) query['item'] = get_item(data.get('query', {}))
return query return query
@ -439,7 +439,7 @@ def upload(request):
def autocompleteDocuments(request, data): def autocompleteDocuments(request, data):
''' '''
Returns autocomplete strings for a given document key and search string Returns autocomplete strings for a given documeny key and search string
takes { takes {
key: string, // document key key: string, // document key
value: string, // search string value: string, // search string

View file

@ -248,7 +248,7 @@ class Edit(models.Model):
clips_query = self.clip_query() clips_query = self.clip_query()
if clips_query['conditions']: if clips_query['conditions']:
clips = clip.models.Clip.objects.find({'query': clips_query}, user) clips = clip.models.Clip.objects.find({'query': clips_query}, user)
items = self.get_items(user).values('id') items = [i['id'] for i in self.get_items(user).values('id')]
clips = clips.filter(item__in=items) clips = clips.filter(item__in=items)
else: else:
clips = clip.models.Clip.objects.filter(id=None) clips = clip.models.Clip.objects.filter(id=None)

View file

@ -107,8 +107,6 @@ class Command(BaseCommand):
print(sql) print(sql)
cursor.execute(sql) cursor.execute(sql)
transaction.commit() transaction.commit()
for i in models.Item.objects.filter(sort=None):
i.save()
if rebuild: if rebuild:
print("Updating sort values...") print("Updating sort values...")
ids = [i['id'] for i in models.Item.objects.all().values('id')] ids = [i['id'] for i in models.Item.objects.all().values('id')]
@ -117,5 +115,3 @@ class Command(BaseCommand):
if options['debug']: if options['debug']:
print(i) print(i)
i.update_sort() i.update_sort()
for i in models.Item.objects.filter(sort=None):
i.save()

View file

@ -17,9 +17,9 @@ class Command(BaseCommand):
def add_arguments(self, parser): def add_arguments(self, parser):
parser.add_argument('--all', action='store_true', dest='all', parser.add_argument('--all', action='store_true', dest='all',
default=False, help='update all items, otherwise oldes N') default=False, help='update all items, otherwise oldes N'),
parser.add_argument('-n', '--items', action='store', dest='items', type=int, parser.add_argument('-n', '--items', action='store', dest='items', type=int,
default=30, help='number of items ot update') default=30, help='number of items ot update'),
def handle(self, **options): def handle(self, **options):
offset = 0 offset = 0

View file

@ -165,10 +165,7 @@ def parseCondition(condition, user, owner=None):
else: else:
q = Q(id__in=l.items.all()) q = Q(id__in=l.items.all())
if exclude: if exclude:
if isinstance(q, list): q = ~q
q = [~x for x in q]
else:
q = ~q
else: else:
q = Q(id=0) q = Q(id=0)
return q return q

View file

@ -14,15 +14,14 @@ from glob import glob
from six import PY2, string_types from six import PY2, string_types
from six.moves.urllib.parse import quote from six.moves.urllib.parse import quote
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.files.temp import NamedTemporaryFile
from django.db import models, transaction, connection from django.db import models, transaction, connection
from django.db.models import Q, Sum, Max from django.db.models import Q, Sum, Max
from django.conf import settings
from django.contrib.auth import get_user_model
from django.db.models.signals import pre_delete from django.db.models.signals import pre_delete
from django.utils.encoding import python_2_unicode_compatible
from django.utils import datetime_safe from django.utils import datetime_safe
from django.utils.encoding import python_2_unicode_compatible
import ox import ox
from oxdjango.fields import JSONField, to_json from oxdjango.fields import JSONField, to_json
@ -215,8 +214,6 @@ class Item(models.Model):
and item_key['value'].get('type') == 'map' \ and item_key['value'].get('type') == 'map' \
and self.get(item_key['value']['key']): and self.get(item_key['value']['key']):
value = re.compile(item_key['value']['map']).findall(self.get(item_key['value']['key'])) value = re.compile(item_key['value']['map']).findall(self.get(item_key['value']['key']))
if value and item_key['value'].get('format'):
value = [item_key['value']['format'].format(value[0])]
return value[0] if value else default return value[0] if value else default
return default return default
@ -390,9 +387,8 @@ class Item(models.Model):
if self.oxdbId != oxdbId: if self.oxdbId != oxdbId:
q = Item.objects.filter(oxdbId=oxdbId).exclude(id=self.id) q = Item.objects.filter(oxdbId=oxdbId).exclude(id=self.id)
if q.count() != 0: if q.count() != 0:
if utils.is_imdb_id(self.public_id): if len(self.public_id) == 7:
self.oxdbId = None self.oxdbId = None
self.update_sort()
q[0].merge_with(self, save=False) q[0].merge_with(self, save=False)
else: else:
n = 1 n = 1
@ -405,14 +401,14 @@ class Item(models.Model):
q = Item.objects.filter(oxdbId=oxdbId).exclude(id=self.id) q = Item.objects.filter(oxdbId=oxdbId).exclude(id=self.id)
self.oxdbId = oxdbId self.oxdbId = oxdbId
update_poster = True update_poster = True
if not utils.is_imdb_id(self.public_id): if len(self.public_id) != 7:
update_ids = True update_ids = True
# id changed, what about existing item with new id? # id changed, what about existing item with new id?
if settings.USE_IMDB and not utils.is_imdb_id(self.public_id) and self.oxdbId != self.public_id: if settings.USE_IMDB and len(self.public_id) != 7 and self.oxdbId != self.public_id:
self.public_id = self.oxdbId self.public_id = self.oxdbId
# FIXME: move files to new id here # FIXME: move files to new id here
if settings.USE_IMDB and utils.is_imdb_id(self.public_id): if settings.USE_IMDB and len(self.public_id) == 7:
for key in ('title', 'year', 'director', 'season', 'episode', for key in ('title', 'year', 'director', 'season', 'episode',
'seriesTitle', 'episodeTitle'): 'seriesTitle', 'episodeTitle'):
if key in self.data: if key in self.data:
@ -422,7 +418,7 @@ class Item(models.Model):
if settings.USE_IMDB: if settings.USE_IMDB:
defaults = list(filter(lambda k: 'default' in k, settings.CONFIG['itemKeys'])) defaults = list(filter(lambda k: 'default' in k, settings.CONFIG['itemKeys']))
for k in defaults: for k in defaults:
if utils.is_imdb_id(self.public_id): if len(self.public_id) == 7:
if k['id'] in self.data and self.data[k['id']] == k['default']: if k['id'] in self.data and self.data[k['id']] == k['default']:
del self.data[k['id']] del self.data[k['id']]
else: else:
@ -641,9 +637,6 @@ class Item(models.Model):
if self.poster_height: if self.poster_height:
i['posterRatio'] = self.poster_width / self.poster_height i['posterRatio'] = self.poster_width / self.poster_height
if keys and 'source' in keys:
i['source'] = self.streams().exclude(file__data='').exists()
streams = self.streams() streams = self.streams()
i['durations'] = [s.duration for s in streams] i['durations'] = [s.duration for s in streams]
i['duration'] = sum(i['durations']) i['duration'] = sum(i['durations'])
@ -945,8 +938,6 @@ class Item(models.Model):
s.oxdbId = self.oxdbId s.oxdbId = self.oxdbId
if not settings.USE_IMDB and s.public_id.isupper() and s.public_id.isalpha(): if not settings.USE_IMDB and s.public_id.isupper() and s.public_id.isalpha():
s.public_id = ox.sort_string(str(ox.fromAZ(s.public_id))) s.public_id = ox.sort_string(str(ox.fromAZ(s.public_id)))
else:
s.public_id = ox.sort_string(s.public_id)
s.modified = self.modified or datetime.now() s.modified = self.modified or datetime.now()
s.created = self.created or datetime.now() s.created = self.created or datetime.now()
s.rightslevel = self.level s.rightslevel = self.level
@ -1050,8 +1041,6 @@ class Item(models.Model):
set_value(s, name, value) set_value(s, name, value)
elif sort_type == 'year': elif sort_type == 'year':
value = self.get(source) value = self.get(source)
if isinstance(value, str):
value = value[:4]
set_value(s, name, value) set_value(s, name, value)
elif sort_type == 'date': elif sort_type == 'date':
value = value_ = self.get(source) value = value_ = self.get(source)
@ -1190,37 +1179,6 @@ class Item(models.Model):
return None return None
return path return path
def extract_clip(self, in_, out, resolution, format, track=None, force=False):
streams = self.streams(track)
stream = streams[0].get(resolution, format)
if streams.count() > 1 and stream.info['duration'] < out:
video = NamedTemporaryFile(suffix='.%s' % format)
r = self.merge_streams(video.name, resolution, format)
if not r:
return False
path = video.name
duration = sum(item.cache['durations'])
else:
path = stream.media.path
duration = stream.info['duration']
cache_name = '%s_%sp_%s.%s' % (self.public_id, resolution, '%s,%s' % (in_, out), format)
cache_path = os.path.join(settings.MEDIA_ROOT, self.path('cache/%s' % cache_name))
if os.path.exists(cache_path) and not force:
return cache_path
if duration >= out:
subtitles = utils.get_by_key(settings.CONFIG['layers'], 'isSubtitles', True)
if subtitles:
srt = self.srt(subtitles['id'], encoder=ox.srt)
if len(srt) < 4:
srt = None
else:
srt = None
ox.makedirs(os.path.dirname(cache_path))
extract.chop(path, in_, out, subtitles=srt, dest=cache_path, encode=True)
return cache_path
return False
@property @property
def timeline_prefix(self): def timeline_prefix(self):
videos = self.streams() videos = self.streams()

View file

@ -22,7 +22,6 @@ def cronjob(**kwargs):
if limit_rate('item.tasks.cronjob', 8 * 60 * 60): if limit_rate('item.tasks.cronjob', 8 * 60 * 60):
update_random_sort() update_random_sort()
update_random_clip_sort() update_random_clip_sort()
clear_cache.delay()
def update_random_sort(): def update_random_sort():
from . import models from . import models
@ -126,33 +125,6 @@ def load_subtitles(public_id):
item.update_sort() item.update_sort()
item.update_facets() item.update_facets()
@task(queue="encoding")
def extract_clip(public_id, in_, out, resolution, format, track=None):
from . import models
try:
item = models.Item.objects.get(public_id=public_id)
except models.Item.DoesNotExist:
return False
if item.extract_clip(in_, out, resolution, format, track):
return True
return False
@task(queue="encoding")
def clear_cache(days=60):
import subprocess
path = os.path.join(settings.MEDIA_ROOT, 'media')
cmd = ['find', path, '-iregex', '.*/frames/.*', '-atime', '+%s' % days, '-type', 'f', '-exec', 'rm', '{}', ';']
subprocess.check_output(cmd)
path = os.path.join(settings.MEDIA_ROOT, 'items')
cmd = ['find', path, '-iregex', '.*/cache/.*', '-atime', '+%s' % days, '-type', 'f', '-exec', 'rm', '{}', ';']
subprocess.check_output(cmd)
path = settings.MEDIA_ROOT
cmd = ['find', path, '-type', 'd', '-size', '0', '-prune', '-exec', 'rmdir', '{}', ';']
subprocess.check_output(cmd)
@task(ignore_results=True, queue='default') @task(ignore_results=True, queue='default')
def update_sitemap(base_url): def update_sitemap(base_url):
from . import models from . import models
@ -161,47 +133,13 @@ def update_sitemap(base_url):
def absolute_url(url): def absolute_url(url):
return base_url + url return base_url + url
state = {} urlset = ET.Element('urlset')
state['part'] = 1 urlset.attrib['xmlns'] = "http://www.sitemaps.org/schemas/sitemap/0.9"
state['count'] = 0 urlset.attrib['xmlns:xsi'] = "http://www.w3.org/2001/XMLSchema-instance"
urlset.attrib['xsi:schemaLocation'] = "http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"
urlset.attrib['xmlns:video'] = "http://www.google.com/schemas/sitemap-video/1.1"
def new_urlset(): url = ET.SubElement(urlset, "url")
urlset = ET.Element('urlset')
urlset.attrib['xmlns'] = "http://www.sitemaps.org/schemas/sitemap/0.9"
urlset.attrib['xmlns:xsi'] = "http://www.w3.org/2001/XMLSchema-instance"
urlset.attrib['xsi:schemaLocation'] = "http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"
urlset.attrib['xmlns:video'] = "http://www.google.com/schemas/sitemap-video/1.1"
return urlset
def save_urlset():
s = ET.SubElement(sitemap_index, "sitemap")
loc = ET.SubElement(s, "loc")
loc.text = absolute_url("sitemap%06d.xml" % state['part'])
lastmod = ET.SubElement(s, "lastmod")
lastmod.text = datetime.now().strftime("%Y-%m-%d")
data = b'<?xml version="1.0" encoding="UTF-8"?>\n' + ET.tostring(state['urlset'])
path = os.path.abspath(os.path.join(settings.MEDIA_ROOT, 'sitemap%06d.xml.gz' % state['part']))
with open(path[:-3], 'wb') as f:
f.write(data)
with gzip.open(path, 'wb') as f:
f.write(data)
state['part'] += 1
state['count'] = 0
state['urlset'] = new_urlset()
def tick():
state['count'] += 1
if state['count'] > 40000:
save_urlset()
sitemap_index = ET.Element('sitemapindex')
sitemap_index.attrib['xmlns'] = "http://www.sitemaps.org/schemas/sitemap/0.9"
sitemap_index.attrib['xmlns:xsi'] = "http://www.w3.org/2001/XMLSchema-instance"
sitemap_index.attrib['xsi:schemaLocation'] = "http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"
state['urlset'] = new_urlset()
url = ET.SubElement(state['urlset'], "url")
loc = ET.SubElement(url, "loc") loc = ET.SubElement(url, "loc")
loc.text = absolute_url('') loc.text = absolute_url('')
# always, hourly, daily, weekly, monthly, yearly, never # always, hourly, daily, weekly, monthly, yearly, never
@ -213,10 +151,9 @@ def update_sitemap(base_url):
# priority of page on site values 0.1 - 1.0 # priority of page on site values 0.1 - 1.0
priority = ET.SubElement(url, "priority") priority = ET.SubElement(url, "priority")
priority.text = '1.0' priority.text = '1.0'
tick()
for page in [s['id'] for s in settings.CONFIG['sitePages']]: for page in [s['id'] for s in settings.CONFIG['sitePages']]:
url = ET.SubElement(state['urlset'], "url") url = ET.SubElement(urlset, "url")
loc = ET.SubElement(url, "loc") loc = ET.SubElement(url, "loc")
loc.text = absolute_url(page) loc.text = absolute_url(page)
# always, hourly, daily, weekly, monthly, yearly, never # always, hourly, daily, weekly, monthly, yearly, never
@ -225,12 +162,11 @@ def update_sitemap(base_url):
# priority of page on site values 0.1 - 1.0 # priority of page on site values 0.1 - 1.0
priority = ET.SubElement(url, "priority") priority = ET.SubElement(url, "priority")
priority.text = '1.0' priority.text = '1.0'
tick()
allowed_level = settings.CONFIG['capabilities']['canSeeItem']['guest'] allowed_level = settings.CONFIG['capabilities']['canSeeItem']['guest']
can_play = settings.CONFIG['capabilities']['canPlayVideo']['guest'] can_play = settings.CONFIG['capabilities']['canPlayVideo']['guest']
for i in models.Item.objects.filter(level__lte=allowed_level): for i in models.Item.objects.filter(level__lte=allowed_level):
url = ET.SubElement(state['urlset'], "url") url = ET.SubElement(urlset, "url")
# URL of the page. This URL must begin with the protocol (such as http) # URL of the page. This URL must begin with the protocol (such as http)
loc = ET.SubElement(url, "loc") loc = ET.SubElement(url, "loc")
loc.text = absolute_url("%s/info" % i.public_id) loc.text = absolute_url("%s/info" % i.public_id)
@ -266,12 +202,11 @@ def update_sitemap(base_url):
el.text = "%s" % int(duration) el.text = "%s" % int(duration)
el = ET.SubElement(video, "video:live") el = ET.SubElement(video, "video:live")
el.text = "no" el.text = "no"
tick()
# Featured Lists # Featured Lists
from itemlist.models import List from itemlist.models import List
for l in List.objects.filter(Q(status='featured') | Q(status='public')): for l in List.objects.filter(Q(status='featured') | Q(status='public')):
url = ET.SubElement(state['urlset'], "url") url = ET.SubElement(urlset, "url")
# URL of the page. This URL must begin with the protocol (such as http) # URL of the page. This URL must begin with the protocol (such as http)
loc = ET.SubElement(url, "loc") loc = ET.SubElement(url, "loc")
loc.text = absolute_url("list==%s" % quote(l.get_id())) loc.text = absolute_url("list==%s" % quote(l.get_id()))
@ -285,12 +220,10 @@ def update_sitemap(base_url):
# priority of page on site values 0.1 - 1.0 # priority of page on site values 0.1 - 1.0
priority = ET.SubElement(url, "priority") priority = ET.SubElement(url, "priority")
priority.text = '1.0' if l.status == 'featured' else '0.75' priority.text = '1.0' if l.status == 'featured' else '0.75'
tick()
# Featured Edits # Featured Edits
from edit.models import Edit from edit.models import Edit
for l in Edit.objects.filter(Q(status='featured') | Q(status='public')): for l in Edit.objects.filter(Q(status='featured') | Q(status='public')):
url = ET.SubElement(state['urlset'], "url") url = ET.SubElement(urlset, "url")
# URL of the page. This URL must begin with the protocol (such as http) # URL of the page. This URL must begin with the protocol (such as http)
loc = ET.SubElement(url, "loc") loc = ET.SubElement(url, "loc")
loc.text = absolute_url(l.get_absolute_url()[1:]) loc.text = absolute_url(l.get_absolute_url()[1:])
@ -304,12 +237,10 @@ def update_sitemap(base_url):
# priority of page on site values 0.1 - 1.0 # priority of page on site values 0.1 - 1.0
priority = ET.SubElement(url, "priority") priority = ET.SubElement(url, "priority")
priority.text = '1.0' if l.status == 'featured' else '0.75' priority.text = '1.0' if l.status == 'featured' else '0.75'
tick()
# Featured Collections # Featured Collections
from documentcollection.models import Collection from documentcollection.models import Collection
for l in Collection.objects.filter(Q(status='featured') | Q(status='public')): for l in Collection.objects.filter(Q(status='featured') | Q(status='public')):
url = ET.SubElement(state['urlset'], "url") url = ET.SubElement(urlset, "url")
# URL of the page. This URL must begin with the protocol (such as http) # URL of the page. This URL must begin with the protocol (such as http)
loc = ET.SubElement(url, "loc") loc = ET.SubElement(url, "loc")
loc.text = absolute_url("documents/collection==%s" % quote(l.get_id())) loc.text = absolute_url("documents/collection==%s" % quote(l.get_id()))
@ -323,11 +254,10 @@ def update_sitemap(base_url):
# priority of page on site values 0.1 - 1.0 # priority of page on site values 0.1 - 1.0
priority = ET.SubElement(url, "priority") priority = ET.SubElement(url, "priority")
priority.text = '1.0' if l.status == 'featured' else '0.75' priority.text = '1.0' if l.status == 'featured' else '0.75'
tick()
from document.models import Document from document.models import Document
for d in Document.objects.filter(rightslevel=0).filter(Q(extension='html') | Q(extension='pdf')): for d in Document.objects.filter(rightslevel=0).filter(Q(extension='html') | Q(extension='pdf')):
url = ET.SubElement(state['urlset'], "url") url = ET.SubElement(urlset, "url")
# URL of the page. This URL must begin with the protocol (such as http) # URL of the page. This URL must begin with the protocol (such as http)
loc = ET.SubElement(url, "loc") loc = ET.SubElement(url, "loc")
loc.text = absolute_url(d.get_id()) loc.text = absolute_url(d.get_id())
@ -343,10 +273,8 @@ def update_sitemap(base_url):
priority.text = '0.75' priority.text = '0.75'
if d.collections.filter(Q(status='featured') | Q(status='public')).count(): if d.collections.filter(Q(status='featured') | Q(status='public')).count():
priority.text = '1.0' priority.text = '1.0'
tick()
if state['count']: data = b'<?xml version="1.0" encoding="UTF-8"?>\n' + ET.tostring(urlset)
save_urlset()
data = b'<?xml version="1.0" encoding="UTF-8"?>\n' + ET.tostring(sitemap_index)
with open(sitemap[:-3], 'wb') as f: with open(sitemap[:-3], 'wb') as f:
f.write(data) f.write(data)
with gzip.open(sitemap, 'wb') as f: with gzip.open(sitemap, 'wb') as f:

View file

@ -16,7 +16,6 @@ urlpatterns = [
url(r'^(?P<id>[A-Z0-9].*)/download$', views.download), url(r'^(?P<id>[A-Z0-9].*)/download$', views.download),
url(r'^(?P<id>[A-Z0-9].*)/download/$', views.download), url(r'^(?P<id>[A-Z0-9].*)/download/$', views.download),
url(r'^(?P<id>[A-Z0-9].*)/download/source/(?P<part>\d+)?$', views.download_source), url(r'^(?P<id>[A-Z0-9].*)/download/source/(?P<part>\d+)?$', views.download_source),
url(r'^(?P<id>[A-Z0-9].*)/download/(?P<resolution>\d+)p(?P<part>\d+)\.(?P<format>webm|ogv|mp4)$', views.download),
url(r'^(?P<id>[A-Z0-9].*)/download/(?P<resolution>\d+)p\.(?P<format>webm|ogv|mp4)$', views.download), url(r'^(?P<id>[A-Z0-9].*)/download/(?P<resolution>\d+)p\.(?P<format>webm|ogv|mp4)$', views.download),
#video #video

View file

@ -103,7 +103,3 @@ def normalize_dict(encoding, data):
elif isinstance(data, list): elif isinstance(data, list):
return [normalize_dict(encoding, value) for value in data] return [normalize_dict(encoding, value) for value in data]
return data return data
def is_imdb_id(id):
return bool(len(id) >= 7 and str(id).isdigit())

View file

@ -638,32 +638,6 @@ def edit(request, data):
return render_to_json_response(response) return render_to_json_response(response)
actions.register(edit, cache=False) actions.register(edit, cache=False)
def extractClip(request, data):
'''
Extract and cache clip
takes {
item: string
resolution: int
format: string
in: float
out: float
}
returns {
taskId: string, // taskId
}
'''
item = get_object_or_404_json(models.Item, public_id=data['item'])
if not item.access(request.user):
return HttpResponseForbidden()
response = json_response()
t = tasks.extract_clip.delay(data['item'], data['in'], data['out'], data['resolution'], data['format'])
response['data']['taskId'] = t.task_id
return render_to_json_response(response)
actions.register(extractClip, cache=False)
@login_required_json @login_required_json
def remove(request, data): def remove(request, data):
''' '''
@ -992,8 +966,6 @@ def download_source(request, id, part=None):
raise Http404 raise Http404
parts = ['%s - %s ' % (item.get('title'), settings.SITENAME), item.public_id] parts = ['%s - %s ' % (item.get('title'), settings.SITENAME), item.public_id]
if len(streams) > 1:
parts.append('.Part %d' % (part + 1))
parts.append('.') parts.append('.')
parts.append(f.extension) parts.append(f.extension)
filename = ''.join(parts) filename = ''.join(parts)
@ -1004,7 +976,7 @@ def download_source(request, id, part=None):
response['Content-Disposition'] = "attachment; filename*=UTF-8''%s" % quote(filename.encode('utf-8')) response['Content-Disposition'] = "attachment; filename*=UTF-8''%s" % quote(filename.encode('utf-8'))
return response return response
def download(request, id, resolution=None, format='webm', part=None): def download(request, id, resolution=None, format='webm'):
item = get_object_or_404(models.Item, public_id=id) item = get_object_or_404(models.Item, public_id=id)
if not resolution or int(resolution) not in settings.CONFIG['video']['resolutions']: if not resolution or int(resolution) not in settings.CONFIG['video']['resolutions']:
resolution = max(settings.CONFIG['video']['resolutions']) resolution = max(settings.CONFIG['video']['resolutions'])
@ -1012,35 +984,22 @@ def download(request, id, resolution=None, format='webm', part=None):
resolution = int(resolution) resolution = int(resolution)
if not item.access(request.user) or not item.rendered: if not item.access(request.user) or not item.rendered:
return HttpResponseForbidden() return HttpResponseForbidden()
if part is not None:
part = int(part) - 1
streams = item.streams()
if part > len(streams):
raise Http404
ext = '.%s' % format ext = '.%s' % format
parts = ['%s - %s ' % (item.get('title'), settings.SITENAME), item.public_id] parts = ['%s - %s ' % (item.get('title'), settings.SITENAME), item.public_id]
if resolution != max(settings.CONFIG['video']['resolutions']): if resolution != max(settings.CONFIG['video']['resolutions']):
parts.append('.%dp' % resolution) parts.append('.%dp' % resolution)
if part is not None:
parts.append('.Part %d' % (part + 1))
parts.append(ext) parts.append(ext)
filename = ''.join(parts) filename = ''.join(parts)
video = NamedTemporaryFile(suffix=ext) video = NamedTemporaryFile(suffix=ext)
content_type = mimetypes.guess_type(video.name)[0] content_type = mimetypes.guess_type(video.name)[0]
if part is None: r = item.merge_streams(video.name, resolution, format)
r = item.merge_streams(video.name, resolution, format) if not r:
if not r: return HttpResponseForbidden()
return HttpResponseForbidden() elif r is True:
elif r is True: response = HttpResponse(FileWrapper(video), content_type=content_type)
response = HttpResponse(FileWrapper(video), content_type=content_type) response['Content-Length'] = os.path.getsize(video.name)
response['Content-Length'] = os.path.getsize(video.name)
else:
response = HttpFileResponse(r, content_type=content_type)
else: else:
stream = streams[part].get(resolution, format) response = HttpFileResponse(r, content_type=content_type)
path = stream.media.path
content_type = mimetypes.guess_type(path)[0]
response = HttpFileResponse(path, content_type=content_type)
response['Content-Disposition'] = "attachment; filename*=UTF-8''%s" % quote(filename.encode('utf-8')) response['Content-Disposition'] = "attachment; filename*=UTF-8''%s" % quote(filename.encode('utf-8'))
return response return response
@ -1097,23 +1056,6 @@ def video(request, id, resolution, format, index=None, track=None):
ext = '.%s' % format ext = '.%s' % format
duration = stream.info['duration'] duration = stream.info['duration']
filename = u"Clip of %s - %s-%s - %s %s%s" % (
item.get('title'),
ox.format_duration(t[0] * 1000).replace(':', '.')[:-4],
ox.format_duration(t[1] * 1000).replace(':', '.')[:-4],
settings.SITENAME.replace('/', '-'),
item.public_id,
ext
)
content_type = mimetypes.guess_type(path)[0]
cache_name = '%s_%sp_%s.%s' % (item.public_id, resolution, '%s,%s' % (t[0], t[1]), format)
cache_path = os.path.join(settings.MEDIA_ROOT, item.path('cache/%s' % cache_name))
if os.path.exists(cache_path):
response = HttpFileResponse(cache_path, content_type=content_type)
response['Content-Disposition'] = "attachment; filename*=UTF-8''%s" % quote(filename.encode('utf-8'))
return response
# multipart request beyond first part, merge parts and chop that # multipart request beyond first part, merge parts and chop that
if not index and streams.count() > 1 and stream.info['duration'] < t[1]: if not index and streams.count() > 1 and stream.info['duration'] < t[1]:
video = NamedTemporaryFile(suffix=ext) video = NamedTemporaryFile(suffix=ext)
@ -1123,6 +1065,7 @@ def video(request, id, resolution, format, index=None, track=None):
path = video.name path = video.name
duration = sum(item.cache['durations']) duration = sum(item.cache['durations'])
content_type = mimetypes.guess_type(path)[0]
if len(t) == 2 and t[1] > t[0] and duration >= t[1]: if len(t) == 2 and t[1] > t[0] and duration >= t[1]:
# FIXME: could be multilingual here # FIXME: could be multilingual here
subtitles = utils.get_by_key(settings.CONFIG['layers'], 'isSubtitles', True) subtitles = utils.get_by_key(settings.CONFIG['layers'], 'isSubtitles', True)
@ -1133,12 +1076,20 @@ def video(request, id, resolution, format, index=None, track=None):
else: else:
srt = None srt = None
response = HttpResponse(extract.chop(path, t[0], t[1], subtitles=srt), content_type=content_type) response = HttpResponse(extract.chop(path, t[0], t[1], subtitles=srt), content_type=content_type)
filename = u"Clip of %s - %s-%s - %s %s%s" % (
item.get('title'),
ox.format_duration(t[0] * 1000).replace(':', '.')[:-4],
ox.format_duration(t[1] * 1000).replace(':', '.')[:-4],
settings.SITENAME,
item.public_id,
ext
)
response['Content-Disposition'] = "attachment; filename*=UTF-8''%s" % quote(filename.encode('utf-8')) response['Content-Disposition'] = "attachment; filename*=UTF-8''%s" % quote(filename.encode('utf-8'))
return response return response
else: else:
filename = "%s - %s %s%s" % ( filename = "%s - %s %s%s" % (
item.get('title'), item.get('title'),
settings.SITENAME.replace('/', '-'), settings.SITENAME,
item.public_id, item.public_id,
ext ext
) )
@ -1375,15 +1326,6 @@ def sitemap_xml(request):
response['Content-Type'] = 'application/xml' response['Content-Type'] = 'application/xml'
return response return response
def sitemap_part_xml(request, part):
part = int(part)
sitemap = os.path.abspath(os.path.join(settings.MEDIA_ROOT, 'sitemap%06d.xml' % part))
if not os.path.exists(sitemap):
raise Http404
response = HttpFileResponse(sitemap)
response['Content-Type'] = 'application/xml'
return response
def item_json(request, id): def item_json(request, id):
level = settings.CONFIG['capabilities']['canSeeItem']['guest'] level = settings.CONFIG['capabilities']['canSeeItem']['guest']
if not request.user.is_anonymous(): if not request.user.is_anonymous():

View file

@ -271,13 +271,12 @@ class List(models.Model):
self.save() self.save()
for i in self.poster_frames: for i in self.poster_frames:
from item.models import Item from item.models import Item
if 'item' in i: qs = Item.objects.filter(public_id=i['item'])
qs = Item.objects.filter(public_id=i['item']) if qs.count() > 0:
if qs.count() > 0: if i.get('position'):
if i.get('position'): frame = qs[0].frame(i['position'])
frame = qs[0].frame(i['position']) if frame:
if frame: frames.append(frame)
frames.append(frame)
self.icon.name = self.path('icon.jpg') self.icon.name = self.path('icon.jpg')
icon = self.icon.path icon = self.icon.path
if frames: if frames:

View file

@ -1,4 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python
import os import os
import signal import signal
import sys import sys

View file

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.22 on 2019-07-23 14:46
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('person', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='person',
name='imdbId',
field=models.CharField(blank=True, max_length=16),
),
]

View file

@ -38,7 +38,7 @@ class Person(models.Model):
#FIXME: how to deal with aliases #FIXME: how to deal with aliases
aliases = fields.TupleField(default=[]) aliases = fields.TupleField(default=[])
imdbId = models.CharField(max_length=16, blank=True) imdbId = models.CharField(max_length=7, blank=True)
wikipediaId = models.CharField(max_length=1000, blank=True) wikipediaId = models.CharField(max_length=1000, blank=True)
objects = managers.PersonManager() objects = managers.PersonManager()

View file

@ -141,5 +141,4 @@ class PlaceManager(Manager):
user) user)
if conditions: if conditions:
qs = qs.filter(conditions) qs = qs.filter(conditions)
qs = qs.distinct()
return qs return qs

View file

@ -239,8 +239,8 @@ def findPlaces(request, data):
qs = order_query(query['qs'], query['sort']) qs = order_query(query['qs'], query['sort'])
qs = qs.distinct() qs = qs.distinct()
if 'keys' in data: if 'keys' in data:
qs = qs.select_related('user__profile')
qs = qs[query['range'][0]:query['range'][1]] qs = qs[query['range'][0]:query['range'][1]]
qs = qs.select_related()
response['data']['items'] = [p.json(data['keys'], request.user) for p in qs] response['data']['items'] = [p.json(data['keys'], request.user) for p in qs]
elif 'position' in query: elif 'position' in query:
ids = [i.get_id() for i in qs] ids = [i.get_id() for i in qs]

View file

@ -178,7 +178,6 @@ CACHES = {
AUTH_PROFILE_MODULE = 'user.UserProfile' AUTH_PROFILE_MODULE = 'user.UserProfile'
AUTH_CHECK_USERNAME = True AUTH_CHECK_USERNAME = True
FFMPEG = 'ffmpeg' FFMPEG = 'ffmpeg'
FFPROBE = 'ffprobe'
FFMPEG_SUPPORTS_VP9 = True FFMPEG_SUPPORTS_VP9 = True
FFMPEG_DEBUG = False FFMPEG_DEBUG = False
@ -205,9 +204,6 @@ CELERY_BROKER_URL = 'amqp://pandora:box@localhost:5672//pandora'
SEND_CELERY_ERROR_EMAILS = False SEND_CELERY_ERROR_EMAILS = False
# Elasticsearch
ELASTICSEARCH_HOST = None
#with apache x-sendfile or lighttpd set this to True #with apache x-sendfile or lighttpd set this to True
XSENDFILE = False XSENDFILE = False

View file

@ -183,17 +183,13 @@ class Task(models.Model):
def json(self): def json(self):
if self.status != 'canceled': if self.status != 'canceled':
self.update() self.update()
data = { return {
'started': self.started, 'started': self.started,
'ended': self.ended, 'ended': self.ended,
'status': self.status, 'status': self.status,
'id': self.public_id, 'title': self.item.get('title'),
'item': self.item.public_id,
'user': self.user and self.user.username or '', 'user': self.user and self.user.username or '',
'id': self.public_id,
} }
try:
data['title'] = self.item.get('title')
data['item'] = self.item.public_id
except:
pass
return data

View file

@ -1,4 +1,4 @@
<!DOCTYPE html> <!DOCTYPE html>
<!-- <!--
Copyright 2012 Mozilla Foundation Copyright 2012 Mozilla Foundation
@ -41,6 +41,9 @@ See https://github.com/adobe-type-tools/cmap-resources
<script src="/static/pdf.js/l10n.js"></script> <script src="/static/pdf.js/l10n.js"></script>
<script src="/static/pdf.js/pdf.js"></script> <script src="/static/pdf.js/pdf.js"></script>
<script src="/static/pdf.js/debugger.js"></script>
<script type="text/javascript"> <script type="text/javascript">
var DEFAULT_URL = '{{url}}', var DEFAULT_URL = '{{url}}',
embeds = {{embeds|safe}}, embeds = {{embeds|safe}},

View file

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.22 on 2019-07-23 14:46
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('title', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='title',
name='imdbId',
field=models.CharField(blank=True, max_length=16),
),
]

View file

@ -32,7 +32,7 @@ class Title(models.Model):
sortsorttitle = models.CharField(max_length=1000) sortsorttitle = models.CharField(max_length=1000)
edited = models.BooleanField(default=False) edited = models.BooleanField(default=False)
imdbId = models.CharField(max_length=16, blank=True) imdbId = models.CharField(max_length=7, blank=True)
objects = managers.TitleManager() objects = managers.TitleManager()

View file

@ -64,7 +64,6 @@ urlpatterns = [
url(r'^atom.xml$', item.views.atom_xml), url(r'^atom.xml$', item.views.atom_xml),
url(r'^robots.txt$', app.views.robots_txt), url(r'^robots.txt$', app.views.robots_txt),
url(r'^sitemap.xml$', item.views.sitemap_xml), url(r'^sitemap.xml$', item.views.sitemap_xml),
url(r'^sitemap(?P<part>\d+).xml$', item.views.sitemap_part_xml),
url(r'', include(item.urls)), url(r'', include(item.urls)),
] ]
#sould this not be enabled by default? nginx should handle those #sould this not be enabled by default? nginx should handle those

View file

@ -6,7 +6,7 @@ random.seed()
import re import re
import json import json
from django.contrib.auth import authenticate, login, logout, update_session_auth_hash from django.contrib.auth import authenticate, login, logout
from django.template import loader from django.template import loader
from django.conf import settings from django.conf import settings
from django.core.mail import send_mail, BadHeaderError, EmailMessage from django.core.mail import send_mail, BadHeaderError, EmailMessage
@ -719,9 +719,7 @@ def editPreferences(request, data):
profile.save() profile.save()
if 'password' in data: if 'password' in data:
change = True change = True
user = request.user request.user.set_password(data['password'])
user.set_password(data['password'])
update_session_auth_hash(request, user)
if 'script' in data: if 'script' in data:
profile = request.user.profile profile = request.user.profile
profile.preferences['script'] = data['script'] profile.preferences['script'] = data['script']

View file

@ -1,4 +1,4 @@
Django==1.11.28 Django==1.11.22
simplejson simplejson
chardet chardet
celery>4 celery>4
@ -11,4 +11,3 @@ tornado<5
geoip2==2.9.0 geoip2==2.9.0
youtube-dl>=2019.4.30 youtube-dl>=2019.4.30
python-memcached python-memcached
elasticsearch

View file

@ -8,11 +8,6 @@ pandora.ui.addFilesDialog = function(options) {
}).bindEvent({ }).bindEvent({
click: function() { click: function() {
$button.options({disabled: true}); $button.options({disabled: true});
that.disableCloseButton()
var $screen = Ox.LoadingScreen({
size: 16
});
that.options({content: $screen.start()});
(options.action == 'upload' ? uploadVideos : importVideos)(function() { (options.action == 'upload' ? uploadVideos : importVideos)(function() {
that.close(); that.close();
pandora.ui.tasksDialog({ pandora.ui.tasksDialog({
@ -122,15 +117,6 @@ pandora.ui.addFilesDialog = function(options) {
) )
}); });
} else { } else {
if (options.items.length > 1) {
selectItems.push({
id: 'multiple',
title: Ox._(
'Create multiple {0}',
[pandora.site.itemName.plural.toLowerCase()]
)
});
}
selectItems.push({ selectItems.push({
id: 'one', id: 'one',
title: Ox._( title: Ox._(
@ -139,6 +125,15 @@ pandora.ui.addFilesDialog = function(options) {
) )
}); });
} }
if (options.items.length > 1) {
selectItems.push({
id: 'multiple',
title: Ox._(
'Create multiple {0}',
[pandora.site.itemName.plural.toLowerCase()]
)
});
}
var $select = Ox.Select({ var $select = Ox.Select({
items: selectItems, items: selectItems,
width: 256 width: 256
@ -149,49 +144,6 @@ pandora.ui.addFilesDialog = function(options) {
$($select.find('.OxButton')[0]).css({margin: '-1px'}); $($select.find('.OxButton')[0]).css({margin: '-1px'});
$button.parent().parent().append($select); $button.parent().parent().append($select);
function getNewOrEmptyItem(data, callback) {
pandora.api.find({
query: {
conditions: [
{key: 'title', value: data.title, operator: '=='}
]
},
keys: ['id']
}, function(result) {
if (!result.data.items.length) {
pandora.api.add(data, callback)
} else {
var isNew = true
Ox.serialForEach(result.data.items, function(item, index, items, next) {
isNew && pandora.api.findMedia({
query: {
conditions: [
{key: 'id', value: item.id, operator: '=='}
]
},
keys: ['id']
}, function(result) {
if (!result.data.items.length) {
isNew = false
callback({
data: {
title: data.title,
id: item.id
}
})
}
next()
})
}, function() {
if (isNew) {
pandora.api.add(data, callback)
}
})
}
})
}
function importVideos(callback) { function importVideos(callback) {
var id, title; var id, title;
($select.value() == 'add' ? pandora.api.get : Ox.noop)({ ($select.value() == 'add' ? pandora.api.get : Ox.noop)({
@ -209,7 +161,7 @@ pandora.ui.addFilesDialog = function(options) {
} else { } else {
title = items[$select.value() == 'one' ? 0 : index].title; title = items[$select.value() == 'one' ? 0 : index].title;
} }
(isNewItem ? getNewOrEmptyItem : Ox.noop)({ (isNewItem ? pandora.api.add : Ox.noop)({
title: title title: title
}, function(result) { }, function(result) {
if (isNewItem) { if (isNewItem) {
@ -249,7 +201,7 @@ pandora.ui.addFilesDialog = function(options) {
} else { } else {
title = items[$select.value() == 'one' ? 0 : index].title; title = items[$select.value() == 'one' ? 0 : index].title;
} }
(isNewItem ? getNewOrEmptyItem : Ox.noop)({ (isNewItem ? pandora.api.add : Ox.noop)({
title: title title: title
}, function(result) { }, function(result) {
if (isNewItem) { if (isNewItem) {

View file

@ -69,15 +69,6 @@ pandora.ui.addItemDialog = function(options) {
title: Ox._('Add {0}', [pandora.site.itemName.singular]), title: Ox._('Add {0}', [pandora.site.itemName.singular]),
width: 544 width: 544
}); });
if (options.files) {
that.options({content: $screen.start()});
$button.options({disabled: true});
Ox.serialMap(options.files, function(file, index, files, callback) {
getFileInfo(file, function(info) {
callback(Ox.extend(info, {file: file}));
});
}, onInfo);
}
function createButton() { function createButton() {
$button = Ox[selected == 'upload' ? 'FileButton' : 'Button']({ $button = Ox[selected == 'upload' ? 'FileButton' : 'Button']({
@ -230,7 +221,6 @@ pandora.ui.addItemDialog = function(options) {
// FIXME: what about pending/aborted uploads // FIXME: what about pending/aborted uploads
pandora.api.findMedia({ pandora.api.findMedia({
keys: ['id', 'item', 'url'], keys: ['id', 'item', 'url'],
range: [0, items.length],
query: { query: {
conditions: selected == 'upload' ? items.map(function(item) { conditions: selected == 'upload' ? items.map(function(item) {
return {key: 'oshash', operator: '==', value: item.oshash}; return {key: 'oshash', operator: '==', value: item.oshash};

View file

@ -66,7 +66,7 @@ pandora.ui.document = function() {
? pandora.user.ui.documents[item.id].position ? pandora.user.ui.documents[item.id].position
: 1, : 1,
url: '/documents/' + item.id + '/' url: '/documents/' + item.id + '/'
+ pandora.safeDocumentName(item.title) + '.' + item.extension, + item.title.replace('?', '_') + '.' + item.extension,
width: that.width(), width: that.width(),
zoom: 'fit' zoom: 'fit'
}) })
@ -80,7 +80,7 @@ pandora.ui.document = function() {
imageHeight: item.dimensions[1], imageHeight: item.dimensions[1],
imagePreviewURL: pandora.getMediaURL('/documents/' + item.id + '/256p.jpg?' + item.modified), imagePreviewURL: pandora.getMediaURL('/documents/' + item.id + '/256p.jpg?' + item.modified),
imageURL: pandora.getMediaURL('/documents/' + item.id + '/' imageURL: pandora.getMediaURL('/documents/' + item.id + '/'
+ pandora.safeDocumentName(item.title) + '.' + item.extension + '?' + item.modified), + item.title + '.' + item.extension + '?' + item.modified),
imageWidth: item.dimensions[0], imageWidth: item.dimensions[0],
width: that.width() width: that.width()
}).css({ }).css({

View file

@ -196,12 +196,13 @@ pandora.ui.documentDialog = function(options) {
? pandora.user.ui.documents[item.id].position ? pandora.user.ui.documents[item.id].position
: 1, : 1,
url: '/documents/' + item.id + '/' url: '/documents/' + item.id + '/'
+ pandora.safeDocumentName(item.title) + '.' + item.extension, + item.title.replace('?', '_') + '.' + item.extension,
width: dialogWidth, width: dialogWidth,
zoom: 'fit' zoom: 'fit'
}) })
: item.extension == 'html' : item.extension == 'html'
? pandora.$ui.textPanel = pandora.ui.textPanel(item) ? pandora.ui.textPanel(item).css({
})
: Ox.ImageViewer({ : Ox.ImageViewer({
area: pandora.user.ui.documents[item.id] area: pandora.user.ui.documents[item.id]
? pandora.user.ui.documents[item.id].position ? pandora.user.ui.documents[item.id].position
@ -210,7 +211,7 @@ pandora.ui.documentDialog = function(options) {
imageHeight: item.dimensions[1], imageHeight: item.dimensions[1],
imagePreviewURL: pandora.getMediaURL('/documents/' + item.id + '/256p.jpg?' + item.modified), imagePreviewURL: pandora.getMediaURL('/documents/' + item.id + '/256p.jpg?' + item.modified),
imageURL: pandora.getMediaURL('/documents/' + item.id + '/' imageURL: pandora.getMediaURL('/documents/' + item.id + '/'
+ pandora.safeDocumentName(item.title) + '.' + item.extension + '?' + item.modified), + item.title + '.' + item.extension + '?' + item.modified),
imageWidth: item.dimensions[0], imageWidth: item.dimensions[0],
width: dialogWidth width: dialogWidth
}) })
@ -242,7 +243,7 @@ pandora.ui.documentDialog = function(options) {
} }
function setTitle() { function setTitle() {
that.options({title: item.title + (item.extension == 'html' ? '' : '.' + item.extension)}); that.options({title: item.title + '.' + item.extension});
} }
that.getItems = function() { that.getItems = function() {

View file

@ -4,7 +4,7 @@ pandora.ui.documentFilter = function(id) {
var i = Ox.getIndexById(pandora.user.ui.documentFilters, id), var i = Ox.getIndexById(pandora.user.ui.documentFilters, id),
filter = Ox.getObjectById(pandora.site.documentFilters, id), filter = Ox.getObjectById(pandora.site.documentFilters, id),
panelWidth = Ox.$document.width() - (pandora.user.ui.showSidebar * pandora.user.ui.sidebarSize) - 1, panelWidth = Ox.$document.width() - (pandora.user.ui.showSidebar * pandora.user.ui.sidebarSize) - 1,
title = Ox._(filter.title), title = Ox._(Ox.getObjectById(pandora.site.documentFilters, id).title),
//width = pandora.getFilterWidth(i, panelWidth), //width = pandora.getFilterWidth(i, panelWidth),
that = Ox.TableList({ that = Ox.TableList({
_selected: !pandora.user.ui.showFilters _selected: !pandora.user.ui.showFilters

View file

@ -27,7 +27,7 @@ pandora.ui.documentFilterForm = function(options) {
if (key.format && key.format.type == 'ColorPercent') { if (key.format && key.format.type == 'ColorPercent') {
key.format.type = 'percent'; key.format.type = 'percent';
} }
key.autocomplete = autocompleteFunction(key) Ox.print(key);
return key; return key;
}).concat([{ }).concat([{
id: 'collection', id: 'collection',
@ -37,7 +37,6 @@ pandora.ui.documentFilterForm = function(options) {
return item.id; return item.id;
}) })
}]), }]),
listName: Ox._('Collection'),
list: mode == 'find' ? { list: mode == 'find' ? {
sort: pandora.user.ui.collectionSort, sort: pandora.user.ui.collectionSort,
view: pandora.user.ui.collectionView view: pandora.user.ui.collectionView
@ -70,24 +69,6 @@ pandora.ui.documentFilterForm = function(options) {
that.getList = that.$filter.getList; that.getList = that.$filter.getList;
that.value = that.$filter.value; that.value = that.$filter.value;
}); });
function autocompleteFunction(key) {
return key.autocomplete ? function(value, callback) {
pandora.api.autocomplete({
key: key.id,
query: {
conditions: [],
operator: '&'
},
range: [0, 100],
sort: key.autocompleteSort,
value: value
}, function(result) {
callback(result.data.items.map(function(item) {
return Ox.decodeHTMLEntities(item);
}));
});
} : null;
}
that.updateResults = function() { that.updateResults = function() {
if (mode == 'collection') { if (mode == 'collection') {
Ox.Request.clearCache(collection.id); Ox.Request.clearCache(collection.id);
@ -100,9 +81,11 @@ pandora.ui.documentFilterForm = function(options) {
} }
}) })
.reloadList(); .reloadList();
pandora.$ui.documentFilters && pandora.$ui.documentFilters.forEach(function($filter) { /*
pandora.$ui.filters && pandora.$ui.filters.forEach(function($filter) {
$filter.reloadList(); $filter.reloadList();
}); });
*/
} else { } else {
pandora.UI.set({findDocuments: Ox.clone(that.$filter.options('value'), true)}); pandora.UI.set({findDocuments: Ox.clone(that.$filter.options('value'), true)});
pandora.$ui.findElement.updateElement(); pandora.$ui.findElement.updateElement();

View file

@ -28,13 +28,6 @@ pandora.ui.documentInfoView = function(data, isMixed) {
}).map(function(key){ }).map(function(key){
return key.id; return key.id;
}), }),
displayedKeys = [ // FIXME: can tis be a flag in the config?
'title', 'notes', 'name', 'description', 'id',
'user', 'rightslevel', 'timesaccessed',
'extension', 'dimensions', 'size', 'matches',
'created', 'modified', 'accessed',
'random', 'entity'
],
statisticsWidth = 128, statisticsWidth = 128,
$bar = Ox.Bar({size: 16}) $bar = Ox.Bar({size: 16})
@ -133,7 +126,7 @@ pandora.ui.documentInfoView = function(data, isMixed) {
height: iconHeight + 'px' height: iconHeight + 'px'
}) })
.bindEvent({ .bindEvent({
singleclick: toggleIconSize // singleclick: toggleIconSize
}) })
.appendTo($info), .appendTo($info),
@ -241,10 +234,6 @@ pandora.ui.documentInfoView = function(data, isMixed) {
Ox.getObjectById(pandora.site.documentKeys, 'keywords') && renderGroup(['keywords']) Ox.getObjectById(pandora.site.documentKeys, 'keywords') && renderGroup(['keywords'])
// Render any remaing keys defined in config
renderRemainingKeys();
// Description ------------------------------------------------------------- // Description -------------------------------------------------------------
@ -332,7 +321,6 @@ pandora.ui.documentInfoView = function(data, isMixed) {
} }
// Extension, Dimensions, Size --------------------------------------------- // Extension, Dimensions, Size ---------------------------------------------
['extension', 'dimensions', 'size'].forEach(function(key) { ['extension', 'dimensions', 'size'].forEach(function(key) {
@ -545,7 +533,6 @@ pandora.ui.documentInfoView = function(data, isMixed) {
function renderGroup(keys) { function renderGroup(keys) {
var $element; var $element;
keys.forEach(function(key) { displayedKeys.push(key) });
if (canEdit || keys.filter(function(key) { if (canEdit || keys.filter(function(key) {
return data[key]; return data[key];
}).length) { }).length) {
@ -578,17 +565,6 @@ pandora.ui.documentInfoView = function(data, isMixed) {
return $element; return $element;
} }
function renderRemainingKeys() {
var keys = pandora.site.documentKeys.filter(function(item) {
return item.id != '*' && !Ox.contains(displayedKeys, item.id);
}).map(function(item) {
return item.id;
});
if (keys.length) {
renderGroup(keys)
}
}
function renderRightsLevel() { function renderRightsLevel() {
var $rightsLevelElement = getRightsLevelElement(data.rightslevel), var $rightsLevelElement = getRightsLevelElement(data.rightslevel),
$rightsLevelSelect; $rightsLevelSelect;
@ -636,36 +612,6 @@ pandora.ui.documentInfoView = function(data, isMixed) {
//renderCapabilities(data.rightslevel); //renderCapabilities(data.rightslevel);
} }
function toggleIconSize() {
iconSize = iconSize == 256 ? 512 : 256;
iconWidth = iconRatio > 1 ? iconSize : Math.round(iconSize * iconRatio);
iconHeight = iconRatio < 1 ? iconSize : Math.round(iconSize / iconRatio);
iconLeft = iconSize == 256 ? Math.floor((iconSize - iconWidth) / 2) : 0,
$icon.animate({
left: margin + iconLeft + 'px',
width: iconWidth + 'px',
height: iconHeight + 'px',
}, 250);
$reflection.animate({
top: margin + iconHeight + 'px',
width: iconSize + 'px',
height: iconSize / 2 + 'px'
}, 250);
$reflectionIcon.animate({
left: iconLeft + 'px',
width: iconWidth + 'px',
height: iconHeight + 'px',
}, 250);
$reflectionGradient.animate({
width: iconSize + 'px',
height: iconSize / 2 + 'px'
}, 250);
$text.animate({
left: margin + (iconSize == 256 ? 256 : iconWidth) + margin + 'px'
}, 250);
pandora.UI.set({infoIconSize: iconSize});
}
that.reload = function() { that.reload = function() {
/* /*
var src = '/documents/' + data.id + '/512p.jpg?' + data.modified; var src = '/documents/' + data.id + '/512p.jpg?' + data.modified;

View file

@ -1,8 +1,107 @@
'use strict'; 'use strict';
pandora.documentColumns = [
{
id: 'title',
operator: '+',
title: Ox._('Title'),
find: true,
visible: true,
width: 256
},
{
id: 'id',
operator: '+',
title: Ox._('ID'),
visible: true,
width: 64
},
{
format: function(value) {
return value.toUpperCase();
},
id: 'extension',
operator: '+',
title: Ox._('Extension'),
find: true,
visible: true,
width: 64
},
{
align: 'right',
format: function(value, data) {
return Ox.isArray(value)
? Ox.formatDimensions(value, 'px')
: Ox.formatCount(value, (data && data.extension == 'html') ? 'word' : 'page');
},
id: 'dimensions',
operator: '-',
title: Ox._('Dimensions'),
visible: true,
width: 128
},
{
align: 'right',
format: function(value) {
return Ox.formatValue(value, 'B');
},
id: 'size',
operator: '-',
title: Ox._('Size'),
visible: true,
width: 64
},
{
id: 'description',
operator: '+',
title: Ox._('Description'),
find: true,
visible: true,
width: 256
},
{
align: 'right',
id: 'matches',
operator: '-',
title: Ox._('Matches'),
visible: true,
width: 64
},
{
id: 'user',
operator: '+',
title: Ox._('User'),
find: true,
visible: true,
width: 128
},
{
align: 'right',
format: function(value) {
return Ox.formatDate(value, '%F %T');
},
id: 'created',
operator: '-',
title: Ox._('Created'),
visible: true,
width: 144
},
{
align: 'right',
format: function(value) {
return Ox.formatDate(value, '%F %T');
},
id: 'modified',
operator: '-',
title: Ox._('Modified'),
visible: true,
width: 144
}
];
pandora.ui.documentSortSelect = function() { pandora.ui.documentSortSelect = function() {
var ui = pandora.user.ui, var ui = pandora.user.ui,
$orderButton = Ox.Button({ $orderButton = Ox.Button({
overlap: 'left', overlap: 'left',
title: getOrderButtonTitle(), title: getOrderButtonTitle(),
tooltip: getOrderButtonTooltip(), tooltip: getOrderButtonTooltip(),
@ -17,9 +116,7 @@ pandora.ui.documentSortSelect = function() {
} }
}), }),
$sortSelect = Ox.Select({ $sortSelect = Ox.Select({
items: pandora.site.documentKeys.filter(function(key) { items: pandora.documentColumns.map(function(column) {
return key.sort;
}).map(function(column) {
return { return {
id: column.id, id: column.id,
title: Ox._('Sort by {0}', [column.title]) title: Ox._('Sort by {0}', [column.title])
@ -33,7 +130,7 @@ pandora.ui.documentSortSelect = function() {
var key = data.value; var key = data.value;
pandora.UI.set({documentsSort: [{ pandora.UI.set({documentsSort: [{
key: key, key: key,
operator: Ox.getObjectById(pandora.site.documentKeys, key).operator operator: Ox.getObjectById(pandora.documentColumns, key).operator
}]}); }]});
} }
}), }),
@ -764,46 +861,21 @@ pandora.ui.documentsPanel = function(options) {
function renderList() { function renderList() {
var options = { var options = {
items: pandora.api.findDocuments, items: pandora.api.findDocuments,
keys: ['description', 'dimensions', 'extension', 'id', 'title', 'ratio', 'size', 'user', 'entities', 'modified'], keys: ['description', 'dimensions', 'extension', 'id', 'title', 'ratio', 'size', 'user', 'entities', 'modified'],
query: { query: {
conditions: isItemView ? [{ key: 'item', value: ui.item, operator: '==' }] : [], conditions: isItemView ? [{ key: 'item', value: ui.item, operator: '==' }] : [],
operator: '&' operator: '&'
}, },
selected: ui.documentsSelection[isItemView ? ui.item : ''] || [], selected: ui.documentsSelection[isItemView ? ui.item : ''] || [],
sort: ui.documentsSort.concat([ sort: ui.documentsSort.concat([
{key: 'extension', operator: '+'}, {key: 'extension', operator: '+'},
{key: 'title', operator: '+'} {key: 'title', operator: '+'}
]), ]),
unique: 'id' unique: 'id'
}; };
return (ui.documentsView == 'list' ? Ox.TableList(Ox.extend(options, { return (ui.documentsView == 'list' ? Ox.TableList(Ox.extend(options, {
columns: pandora.site.documentSortKeys.filter(function(key) { columns: pandora.documentColumns,
return (!key.capability
|| pandora.hasCapability(key.capability)) && key.columnWidth;
}).map(function(key) {
var position = ui.collectionColumns.indexOf(key.id);
return {
addable: key.id != 'random',
align: ['string', 'text'].indexOf(
Ox.isArray(key.type) ? key.type[0]: key.type
) > -1 ? 'left' : key.type == 'list' ? 'center' : 'right',
defaultWidth: key.columnWidth,
format: (function() {
return function(value, data) {
return pandora.formatDocumentKey(key, data);
}
})(),
id: key.id,
operator: pandora.getDocumentSortOperator(key.id),
position: position,
removable: !key.columnRequired,
title: Ox._(key.title),
type: key.type,
visible: position > -1,
width: ui.collectionColumnWidth[key.id] || key.columnWidth
};
}),
columnsVisible: true, columnsVisible: true,
scrollbarVisible: true, scrollbarVisible: true,
})) : Ox.IconList(Ox.extend(options, { })) : Ox.IconList(Ox.extend(options, {
@ -811,7 +883,7 @@ pandora.ui.documentsPanel = function(options) {
var sortKey = sort[0].key, var sortKey = sort[0].key,
infoKey = sortKey == 'title' ? 'extension' : sortKey, infoKey = sortKey == 'title' ? 'extension' : sortKey,
info = ( info = (
Ox.getObjectById(pandora.site.documentKeys, infoKey).format || Ox.identity Ox.getObjectById(pandora.documentColumns, infoKey).format || Ox.identity
)(data[infoKey]), )(data[infoKey]),
size = size || 128; size = size || 128;
return { return {

View file

@ -9,10 +9,6 @@ pandora.ui.downloadVideoDialog = function(options) {
'mp4': 'MP4', 'mp4': 'MP4',
}, },
parts = options.out ? null : Ox.max(options.video.map(function(video) {
return video.index
})),
$content = Ox.Element() $content = Ox.Element()
.css({margin: '16px'}), .css({margin: '16px'}),
@ -31,9 +27,6 @@ pandora.ui.downloadVideoDialog = function(options) {
.css({marginBottom: '16px'}) .css({marginBottom: '16px'})
.appendTo($content), .appendTo($content),
$format,
$resolution,
$form = window.$form = Ox.Form({ $form = window.$form = Ox.Form({
items: [ items: [
Ox.Select({ Ox.Select({
@ -43,10 +36,7 @@ pandora.ui.downloadVideoDialog = function(options) {
id: format, id: format,
title: formats[format] title: formats[format]
}; };
}).concat(!options.out && options.source ? [{ }),
id: 'source',
title: Ox._('Source')
}] : []),
label: Ox._('Format'), label: Ox._('Format'),
labelWidth: 120, labelWidth: 120,
value: pandora.site.video.downloadFormat, value: pandora.site.video.downloadFormat,
@ -54,14 +44,9 @@ pandora.ui.downloadVideoDialog = function(options) {
}) })
.bindEvent({ .bindEvent({
change: function(data) { change: function(data) {
if (data.value == 'source') {
$resolution.hide()
} else {
$resolution.show()
}
} }
}), }),
$resolution = Ox.Select({ Ox.Select({
id: 'resolution', id: 'resolution',
items: pandora.site.video.resolutions.map(function(resolution) { items: pandora.site.video.resolutions.map(function(resolution) {
return { return {
@ -78,29 +63,9 @@ pandora.ui.downloadVideoDialog = function(options) {
change: function(data) { change: function(data) {
} }
}) })
].concat(parts ? [ ]
Ox.Select({
id: 'part',
items: Ox.range(parts + 1).map(function(resolution, idx) {
return {
id: idx + 1,
title: 'Part ' + (idx+1)
};
}),
label: Ox._('Part'),
labelWidth: 120,
value: 1,
width: 240
})
.bindEvent({
change: function(data) {
}
})
] : [])
}).appendTo($content), }).appendTo($content),
failed = false,
that = Ox.Dialog({ that = Ox.Dialog({
buttons: [ buttons: [
Ox.Button({ Ox.Button({
@ -108,63 +73,20 @@ pandora.ui.downloadVideoDialog = function(options) {
title: Ox._('Download') title: Ox._('Download')
}).bindEvent({ }).bindEvent({
click: function() { click: function() {
if (failed) { that.close();
that.close();
return
}
var values = $form.values(), var values = $form.values(),
url url
if (options.out) { if (options.out) {
var $screen = Ox.LoadingScreen({ url = '/' + options.item
size: 16 + '/' + values.resolution
}) + 'p.' + values.format
that.options({content: $screen.start()}); + '?t=' + options['in'] + ',' + options.out;
pandora.api.extractClip({
item: options.item,
resolution: values.resolution,
format: values.format,
'in': options['in'],
out: options.out
}, function(result) {
if (result.data.taskId) {
pandora.wait(result.data.taskId, function(result) {
console.log('wait -> ', result)
if (result.data.result) {
url = '/' + options.item
+ '/' + values.resolution
+ 'p.' + values.format
+ '?t=' + options['in'] + ',' + options.out;
that.close();
document.location.href = url
} else {
}
}, 1000)
} else {
that.options({content: 'Failed to extract clip.'});
that.options('buttons')[0].options({
title: Ox._('Close')
});
failed = true;
}
})
} else { } else {
if (values.format == 'source') { url = '/' + options.item
url = '/' + options.item + '/download/' + values.resolution
+ '/download/source/' + 'p.' + values.format
+ (values.part ? values.part : '')
} else {
url = '/' + options.item
+ '/download/' + values.resolution
+ 'p'
+ (values.part ? values.part : '')
+ '.' + values.format
}
}
if (url) {
that.close();
document.location.href = url
} }
document.location.href = url
} }
}) })
], ],

View file

@ -28,7 +28,7 @@ pandora.ui.editor = function(data) {
enableSetPosterFrame: !pandora.site.media.importFrames && data.editable, enableSetPosterFrame: !pandora.site.media.importFrames && data.editable,
enableSubtitles: ui.videoSubtitles, enableSubtitles: ui.videoSubtitles,
find: ui.itemFind, find: ui.itemFind,
findLayer: pandora.getFindLayer(), findLayer: ui._findState.key,
getFrameURL: function(position) { getFrameURL: function(position) {
return pandora.getMediaURL('/' + ui.item + '/' + ui.videoResolution + 'p' + position + '.jpg?' + data.modified); return pandora.getMediaURL('/' + ui.item + '/' + ui.videoResolution + 'p' + position + '.jpg?' + data.modified);
}, },
@ -189,9 +189,7 @@ pandora.ui.editor = function(data) {
pandora.ui.downloadVideoDialog({ pandora.ui.downloadVideoDialog({
item: ui.item, item: ui.item,
rightsLevel: rightsLevel, rightsLevel: rightsLevel,
source: data.source && pandora.hasCapability('canDownloadSource'), title: data.title
title: data.title,
video: data.video
}).open(); }).open();
}, },
downloadselection: function(selection) { downloadselection: function(selection) {
@ -200,8 +198,7 @@ pandora.ui.editor = function(data) {
'in': selection['in'], 'in': selection['in'],
out: selection.out, out: selection.out,
rightsLevel: rightsLevel, rightsLevel: rightsLevel,
title: data.title, title: data.title
video: data.video
}).open(); }).open();
}, },
editannotation: function(data) { editannotation: function(data) {

View file

@ -12,9 +12,7 @@ pandora.ui.filterDialog = function() {
click: function() { click: function() {
var list = pandora.$ui.filterForm.getList(); var list = pandora.$ui.filterForm.getList();
if (list.save) { if (list.save) {
pandora.api[ pandora.api.addList({
pandora.user.ui.section == 'documents' ? 'addCollection' : 'addList'
]({
name: list.name, name: list.name,
query: list.query, query: list.query,
status: 'private', status: 'private',
@ -22,21 +20,12 @@ pandora.ui.filterDialog = function() {
}, function(result) { }, function(result) {
var $list = pandora.$ui.folderList.personal, var $list = pandora.$ui.folderList.personal,
id = result.data.id; id = result.data.id;
if (pandora.user.ui.section) { pandora.UI.set({
pandora.UI.set({ find: {
findDocuments: { conditions: [{key: 'list', value: id, operator: '=='}],
conditions: [{key: 'collection', value: id, operator: '=='}], operator: '&'
operator: '&' }
} });
});
} else {
pandora.UI.set({
find: {
conditions: [{key: 'list', value: id, operator: '=='}],
operator: '&'
}
});
}
Ox.Request.clearCache(); // fixme: remove Ox.Request.clearCache(); // fixme: remove
$list.bindEventOnce({ $list.bindEventOnce({
load: function(data) { load: function(data) {

View file

@ -33,7 +33,7 @@ pandora.ui.findDocumentsElement = function() {
} }
}), }),
] : [], [ ] : [], [
pandora.$ui.findDocumentsSelect = $findSelect = Ox.Select({ $findSelect = Ox.Select({
id: 'select', id: 'select',
items: [].concat( items: [].concat(
pandora.site.documentKeys.filter(function(key) { pandora.site.documentKeys.filter(function(key) {
@ -70,7 +70,7 @@ pandora.ui.findDocumentsElement = function() {
} }
} }
}), }),
pandora.$ui.findDocumentsInput = $findInput = Ox.Input({ $findInput = Ox.Input({
autocomplete: autocompleteFunction(), autocomplete: autocompleteFunction(),
autocompleteSelect: true, autocompleteSelect: true,
autocompleteSelectHighlight: true, autocompleteSelectHighlight: true,

View file

@ -287,9 +287,7 @@ pandora.ui.folderList = function(id, section) {
// works when switching back from browser, but won't work on load, since // works when switching back from browser, but won't work on load, since
// getListData relies on $folderList, so selectList is called in init handler // getListData relies on $folderList, so selectList is called in init handler
selected: pandora.getListData().folder == id selected: pandora.getListData().folder == id
? [ui[ui.section == 'items' ? '_list' ? [ui[ui.section == 'items' ? '_list' : ui.section.slice(0, -1)]]
: ui.section == 'documents' ? '_collection'
: ui.section.slice(0, -1)]]
: [], : [],
sort: [{key: 'position', operator: '+'}], sort: [{key: 'position', operator: '+'}],
sortable: id != 'featured' || canEditFeatured, sortable: id != 'featured' || canEditFeatured,
@ -391,7 +389,7 @@ pandora.ui.folderList = function(id, section) {
}, },
key_control_d: function() { key_control_d: function() {
if (that.options('selected').length) { if (that.options('selected').length) {
pandora.addFolderItem(ui.section, ui.section == 'documents' ? ui._collection : ui._list); pandora.addFolderItem(ui.section, ui._list);
} }
}, },
key_control_e: function() { key_control_e: function() {

View file

@ -52,13 +52,13 @@ pandora.ui.folders = function(section) {
[Ox._(folderItem)]), [Ox._(folderItem)]),
keyboard: 'shift control n', keyboard: 'shift control n',
disabled: ui.section == 'documents' disabled: ui.section == 'documents'
? ui.collectionSelection.length == 0 ? ui.collectionSelection == 0
: ui.listSelection.length == 0 : ui.listSelection.length == 0
}, },
{ id: 'newsmartlist', title: Ox._('New Smart {0}', [Ox._(folderItem)]), keyboard: 'alt control n' }, { id: 'newsmartlist', title: Ox._('New Smart {0}', [Ox._(folderItem)]), keyboard: 'alt control n' },
{ id: 'newsmartlistfromresults', title: Ox._('New Smart {0} from Results', [Ox._(folderItem)]), keyboard: 'shift alt control n' }, { id: 'newsmartlistfromresults', title: Ox._('New Smart {0} from Results', [Ox._(folderItem)]), keyboard: 'shift alt control n' },
{}, {},
{ id: 'duplicatelist', title: Ox._('Duplicate Selected {0}', [Ox._(folderItem)]), keyboard: 'control d', disabled: ui.section == 'documents' ? !ui._collection : !ui._list }, { id: 'duplicatelist', title: Ox._('Duplicate Selected {0}', [Ox._(folderItem)]), keyboard: 'control d', disabled: !ui._list },
{ id: 'editlist', title: Ox._('Edit Selected {0}...', [Ox._(folderItem)]), keyboard: 'control e', disabled: !editable }, { id: 'editlist', title: Ox._('Edit Selected {0}...', [Ox._(folderItem)]), keyboard: 'control e', disabled: !editable },
{ id: 'deletelist', title: Ox._('Delete Selected {0}...', [Ox._(folderItem)]), keyboard: 'delete', disabled: !editable } { id: 'deletelist', title: Ox._('Delete Selected {0}...', [Ox._(folderItem)]), keyboard: 'delete', disabled: !editable }
], ],
@ -75,18 +75,13 @@ pandora.ui.folders = function(section) {
], data.id)) { ], data.id)) {
pandora.addList(data.id.indexOf('smart') > -1, data.id.indexOf('from') > -1); pandora.addList(data.id.indexOf('smart') > -1, data.id.indexOf('from') > -1);
} else if (data.id == 'duplicatelist') { } else if (data.id == 'duplicatelist') {
pandora.addList(ui.section == 'documents' ? ui._collection : ui._list); pandora.addList(ui._list);
} else if (data.id == 'editlist') { } else if (data.id == 'editlist') {
pandora.ui.listDialog().open(); pandora.ui.listDialog().open();
} else if (data.id == 'deletelist') { } else if (data.id == 'deletelist') {
pandora.ui.deleteListDialog().open(); pandora.ui.deleteListDialog().open();
} }
}, },
pandora_collectionselection: function(data) {
pandora.$ui.personalListsMenu[
data.value.length ? 'enableItem' : 'disableItem'
]('newlistfromselection');
},
pandora_find: function() { pandora_find: function() {
// fixme: duplicated // fixme: duplicated
var action = ui._list var action = ui._list
@ -101,20 +96,6 @@ pandora.ui.folders = function(section) {
ui.listSelection.length ? 'enableItem' : 'disableItem' ui.listSelection.length ? 'enableItem' : 'disableItem'
]('newlistfromselection'); ]('newlistfromselection');
}, },
pandora_finddocuments: function() {
// fixme: duplicated
var action = ui._collection
&& pandora.getListData(ui._collection).user == pandora.user.username
? 'enableItem' : 'disableItem'
pandora.$ui.personalListsMenu[
ui._collection ? 'enableItem' : 'disableItem'
]('duplicatelist');
pandora.$ui.personalListsMenu[action]('editlist');
pandora.$ui.personalListsMenu[action]('deletelist');
pandora.$ui.personalListsMenu[
ui.collectionSelection.length ? 'enableItem' : 'disableItem'
]('newlistfromselection');
},
pandora_listselection: function(data) { pandora_listselection: function(data) {
pandora.$ui.personalListsMenu[ pandora.$ui.personalListsMenu[
data.value.length ? 'enableItem' : 'disableItem' data.value.length ? 'enableItem' : 'disableItem'

View file

@ -97,7 +97,7 @@ pandora.ui.idDialog = function(data) {
labelWidth: 128, labelWidth: 128,
value: Ox.decodeHTMLEntities(key == 'director' && data[key] value: Ox.decodeHTMLEntities(key == 'director' && data[key]
? data[key].join(', ') ? data[key].join(', ')
: ('' + (data[key] || ''))), : ('' + data[key])),
width: formWidth width: formWidth
}) })
.css({display: 'inline-block', margin: '3px'}) .css({display: 'inline-block', margin: '3px'})
@ -201,7 +201,7 @@ pandora.ui.idDialog = function(data) {
} }
return Ox.filter([ return Ox.filter([
item.id, item.id,
item.title + ((item.originalTitle && item.title != item.originalTitle) ? ' (' + item.originalTitle + ')' : ''), item.title + (item.originalTitle ? ' (' + item.originalTitle + ')' : ''),
item.director ? item.director.join(', ') : '', item.director ? item.director.join(', ') : '',
item.year item.year
]).join(' &mdash; '); ]).join(' &mdash; ');

View file

@ -68,15 +68,7 @@ pandora.ui.importAnnotationsDialog = function(options) {
.bindEvent({ .bindEvent({
change: function(data) { change: function(data) {
$status.empty(); $status.empty();
if (data.value.length) { data.value.length && $formatSelect.options({value: Ox.last(data.value[0].name.split('.'))});
var format = Ox.last(data.value[0].name.split('.'));
$formatSelect.options({value: format});
var subtitlesLayer = pandora.getSubtitlesLayer()
if (subtitlesLayer && format == 'srt' && Ox.getObjectById(layers, subtitlesLayer)) {
$layerSelect.options({value: subtitlesLayer})
}
updateLanguageSelect();
}
that[ that[
data.value.length ? 'enableButton' : 'disableButton' data.value.length ? 'enableButton' : 'disableButton'
]('import'); ]('import');

View file

@ -1,54 +0,0 @@
'use strict';
pandora.ui.importScreen = function() {
var that = Ox.Element()
.attr({id: 'importScreen'})
.css({
position: 'absolute',
left: 0,
top: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
zIndex: 1000
})
.on({
click: function() {
that.remove();
},
dragleave: function() {
that.remove();
}
});
Ox.Element()
.css({
position: 'absolute',
left: 0,
top: 0,
right: 0,
bottom: 0,
width: pandora.hasCapability('canAddItems') ? 192 : 256,
height: 16,
padding: '8px 0',
borderRadius: 8,
margin: 'auto',
background: 'rgba(255, 255, 255, 0.9)',
fontSize: 13,
color: 'rgb(0, 0, 0)',
textAlign: 'center'
})
.text(
Ox._(pandora.hasCapability('canAddItems') ? (
'Import {0}'
) : (
'You are not allowed to import {0}'
),
[pandora.user.ui.section == 'documents' ? 'Documents' : pandora.site.itemName.plural])
)
.appendTo(that);
return that;
};

View file

@ -919,130 +919,21 @@ pandora.ui.infoView = function(data, isMixed) {
}); });
} }
function renderFrames() {
pandora.api.get({
id: data.id,
keys: ['frames']
}, 0, function(result) {
var images = result.data.frames.map(function(image) {
return Ox.extend(image, {index: image.index.toString()});
}),
selectedImage = images.filter(function(image) {
return image.selected;
})[0],
modified = data.modified;
$list = Ox.IconList({
defaultRatio: !data.stream ? pandora.site.posters.ratio : data.stream.aspectratio,
fixedRatio: !data.stream ? false : data.stream.aspectratio,
item: function(data, sort, size) {
var ratio = data.width / data.height;
size = size || 128;
return {
height: ratio <= 1 ? size : size / ratio,
id: data.id,
info: data.width + ' × ' + data.height + ' px',
title: Ox.formatDuration(data.position),
url: data.url,
width: ratio >= 1 ? size : size * ratio
}
},
items: images,
keys: ['index', 'position', 'width', 'height', 'url'],
max: 1,
min: 1,
orientation: 'both',
// fixme: should never be undefined
selected: selectedImage ? [selectedImage['index']] : [],
size: 128,
sort: [{key: 'index', operator: '+'}],
unique: 'index'
})
.addClass('OxMedia')
.css({
display: 'block',
position: 'absolute',
left: 0,
top: 0,
width: listWidth + 'px',
height: getHeight() + 'px'
})
.bindEvent({
select: function(event) {
var index = event.ids[0];
selectedImage = images.filter(function(image) {
return image.index == index;
})[0];
var imageRatio = selectedImage.width / selectedImage.height,
src = selectedImage.url
if ($browserImages.length == 0) {
$browserImages = pandora.$ui.browser.find('img[src*="/' + data.id + '/"]');
}
pandora.api.setPosterFrame({
id: data.id,
// fixme: api slightly inconsistent, this shouldn't be "position"
position: selectedImage.index
}, function() {
var src;
Ox.Request.clearCache();
if (ui.icons == 'frames') {
src = pandora.getMediaURL('/' + data.id + '/icon512.jpg?' + Ox.uid());
$icon.attr({src: src});
$reflectionIcon.attr({src: src});
if (pandora.$ui.videoPreview) {
pandora.$ui.videoPreview.options({
position: $list.value(selectedImage.index, 'position')
});
}
} else if (ui.icons == 'posters' && ui.showSitePosters) {
src = pandora.getMediaURL('/' + data.id + '/siteposter512.jpg?' + Ox.uid());
$icon.attr({src: src});
$reflectionIcon.attr({src: src});
}
$browserImages.each(function() {
$(this).attr({src: pandora.getMediaURL('/' + data.id + '/' + (
ui.icons == 'posters'
? ui.showSitePosters ? 'siteposter' : 'poster'
: 'icon'
) + '128.jpg?' + Ox.uid())});
});
if (ui.listSort[0].key == 'modified') {
pandora.$ui.browser.reloadList();
}
});
}
})
.appendTo($info);
$list.size();
});
}
function renderList() { function renderList() {
if (ui.icons == 'posters' && !ui.showSitePosters) {
renderPosters()
} else {
renderFrames()
}
}
function renderPosters() {
pandora.api.get({ pandora.api.get({
id: data.id, id: data.id,
keys: ['posters'] keys: [ui.icons == 'posters' ? 'posters' : 'frames']
}, 0, function(result) { }, 0, function(result) {
var images = result.data.posters.map(function(image) { var images = result.data[ui.icons == 'posters' ? 'posters' : 'frames'].map(function(image) {
return Ox.extend(image, {index: image.index.toString()}); return Ox.extend(image, {index: image.index.toString()});
}), }),
selectedImage = images.filter(function(image) { selectedImage = images.filter(function(image) {
return image.selected; return image.selected;
})[0], })[0],
modified = data.modified; modified = data.modified;
if (images.length == 1) {
renderFrames()
return
}
$list = Ox.IconList({ $list = Ox.IconList({
defaultRatio: pandora.site.posters.ratio, defaultRatio: ui.icons == 'posters' || !data.stream ? pandora.site.posters.ratio : data.stream.aspectratio,
fixedRatio: false, fixedRatio: ui.icons == 'posters' || !data.stream ? false : data.stream.aspectratio,
item: function(data, sort, size) { item: function(data, sort, size) {
var ratio = data.width / data.height; var ratio = data.width / data.height;
size = size || 128; size = size || 128;
@ -1050,15 +941,17 @@ pandora.ui.infoView = function(data, isMixed) {
height: ratio <= 1 ? size : size / ratio, height: ratio <= 1 ? size : size / ratio,
id: data.id, id: data.id,
info: data.width + ' × ' + data.height + ' px', info: data.width + ' × ' + data.height + ' px',
title: data.source, title: ui.icons == 'posters' ? data.source : Ox.formatDuration(data.position),
url: data.url.replace('http://', '//') + ( url: data.url.replace('http://', '//') + (
data.source == pandora.site.site.url ? '?' + modified : '' ui.icons == 'posters' && data.source == pandora.site.site.url ? '?' + modified : ''
), ),
width: ratio >= 1 ? size : size * ratio width: ratio >= 1 ? size : size * ratio
} }
}, },
items: images, items: images,
keys: ['index', 'source', 'width', 'height', 'url'], keys: ui.icons == 'posters'
? ['index', 'source', 'width', 'height', 'url']
: ['index', 'position', 'width', 'height', 'url'],
max: 1, max: 1,
min: 1, min: 1,
orientation: 'both', orientation: 'both',
@ -1088,12 +981,12 @@ pandora.ui.infoView = function(data, isMixed) {
if ($browserImages.length == 0) { if ($browserImages.length == 0) {
$browserImages = pandora.$ui.browser.find('img[src*="/' + data.id + '/"]'); $browserImages = pandora.$ui.browser.find('img[src*="/' + data.id + '/"]');
} }
if (!ui.showSitePosters) { if (ui.icons == 'posters' && !ui.showSitePosters) {
$browserImages.each(function() { $browserImages.each(function() {
var $this = $(this), var $this = $(this),
size = Math.max($this.width(), $this.height()); size = Math.max($this.width(), $this.height());
$this.attr({src: src}); $this.attr({src: src});
$this.css(imageRatio < 1 ? { ui.icons == 'posters' && $this.css(imageRatio < 1 ? {
width: Math.round(size * imageRatio) + 'px', width: Math.round(size * imageRatio) + 'px',
height: size + 'px' height: size + 'px'
} : { } : {
@ -1107,17 +1000,31 @@ pandora.ui.infoView = function(data, isMixed) {
iconSize = iconSize == 256 ? 512 : 256; iconSize = iconSize == 256 ? 512 : 256;
toggleIconSize(); toggleIconSize();
} }
pandora.api.setPoster({ pandora.api[ui.icons == 'posters' ? 'setPoster' : 'setPosterFrame'](Ox.extend({
id: data.id, id: data.id
}, ui.icons == 'posters' ? {
source: selectedImage.source source: selectedImage.source
}, function() { } : {
// fixme: api slightly inconsistent, this shouldn't be "position"
position: selectedImage.index
}), function() {
var src; var src;
Ox.Request.clearCache(); Ox.Request.clearCache();
if (ui.icons == 'frames') {
src = pandora.getMediaURL('/' + data.id + '/icon512.jpg?' + Ox.uid());
$icon.attr({src: src});
$reflectionIcon.attr({src: src});
if (pandora.$ui.videoPreview) {
pandora.$ui.videoPreview.options({
position: $list.value(selectedImage.index, 'position')
});
}
}
if (!ui.showSitePosters) { if (!ui.showSitePosters) {
$browserImages.each(function() { $browserImages.each(function() {
$(this).attr({ $(this).attr({src: pandora.getMediaURL('/' + data.id + '/' + (
src: pandora.getMediaURL('/' + data.id + '/poster128.jpg?' + Ox.uid()) ui.icons == 'posters' ? 'poster' : 'icon'
}); ) + '128.jpg?' + Ox.uid())});
}); });
} }
if (ui.listSort[0].key == 'modified') { if (ui.listSort[0].key == 'modified') {
@ -1131,7 +1038,6 @@ pandora.ui.infoView = function(data, isMixed) {
}); });
} }
function renderRightsLevel() { function renderRightsLevel() {
var $rightsLevelElement = getRightsLevelElement(data.rightslevel), var $rightsLevelElement = getRightsLevelElement(data.rightslevel),
$rightsLevelSelect; $rightsLevelSelect;

View file

@ -621,6 +621,19 @@ pandora.ui.infoView = function(data, isMixed) {
$('<div>').css({height: '16px'}).appendTo($statistics); $('<div>').css({height: '16px'}).appendTo($statistics);
function cleanupDate(value) {
if (/\d{2}-\d{2}-\d{4}/.test(value)) {
value = Ox.reverse(value.split('-')).join('-')
}
if (/\d{4}i\/\d{2}\/\d{d}/.test(value)) {
value = value.split('/').join('-')
}
if (/\d{2}\/\d{2}\/\d{4}/.test(value)) {
value = Ox.reverse(value.split('/')).join('-')
}
return value
}
function editMetadata(key, value) { function editMetadata(key, value) {
if (value != data[key]) { if (value != data[key]) {
var itemKey = Ox.getObjectById(pandora.site.itemKeys, key); var itemKey = Ox.getObjectById(pandora.site.itemKeys, key);
@ -643,12 +656,12 @@ pandora.ui.infoView = function(data, isMixed) {
? Ox.decodeHTMLEntities(value).split('; ').map(Ox.encodeHTMLEntities) ? Ox.decodeHTMLEntities(value).split('; ').map(Ox.encodeHTMLEntities)
: []; : [];
} else if (key == 'imdbId') { } else if (key == 'imdbId') {
edit[key] = value ? value.match(/\d+/)[0] : value; edit[key] = value ? value.match(/\d{7}/)[0] : value;
} else { } else {
edit[key] = value; edit[key] = value;
} }
if (itemKey && itemKey.type && itemKey.type[0] == 'date') { if (itemKey && itemKey.type && itemKey.type[0] == 'date') {
edit[key] = edit[key].map(pandora.cleanupDate); edit[key] = edit[key].map(cleanupDate);
} }
pandora.api.edit(edit, function(result) { pandora.api.edit(edit, function(result) {
if (!isMultiple) { if (!isMultiple) {
@ -750,14 +763,14 @@ pandora.ui.infoView = function(data, isMixed) {
specialListKeys.indexOf(key) > -1 && itemKey && itemKey.type[0] == 'date' specialListKeys.indexOf(key) > -1 && itemKey && itemKey.type[0] == 'date'
) { ) {
ret = value.split('; ').map(function(date) { ret = value.split('; ').map(function(date) {
date = pandora.cleanupDate(date) date = cleanupDate(date)
return date ? formatLink(Ox.formatDate(date, return date ? formatLink(Ox.formatDate(date,
['', '%Y', '%B %Y', '%B %e, %Y'][date.split('-').length], ['', '%Y', '%B %Y', '%B %e, %Y'][date.split('-').length],
true true
), key, date) : ''; ), key, date) : '';
}).join('; '); }).join('; ');
} else if (['releasedate'].indexOf(key) > -1) { } else if (['releasedate'].indexOf(key) > -1) {
value = pandora.cleanupDate(value); value = cleanupDate(value);
ret = value ? Ox.formatDate(value, ret = value ? Ox.formatDate(value,
['', '%Y', '%B %Y', '%B %e, %Y'][value.split('-').length], ['', '%Y', '%B %Y', '%B %e, %Y'][value.split('-').length],
true true

View file

@ -38,15 +38,6 @@ pandora.ui.infoView = function(data, isMixed) {
}) })
), ),
posterKeys = nameKeys.concat(['title', 'year']), posterKeys = nameKeys.concat(['title', 'year']),
displayedKeys = [ // FIXME: can tis be a flag in the config?
'title', 'notes', 'name', 'summary', 'id',
'hue', 'saturation', 'lightness', 'cutsperminute', 'volume',
'user', 'rightslevel', 'bitrate', 'timesaccessed',
'numberoffiles', 'numberofannotations', 'numberofcuts', 'words', 'wordsperminute',
'duration', 'aspectratio', 'pixels', 'size', 'resolution',
'created', 'modified', 'accessed',
'random'
],
statisticsWidth = 128, statisticsWidth = 128,
$bar = Ox.Bar({size: 16}) $bar = Ox.Bar({size: 16})
@ -245,17 +236,13 @@ pandora.ui.infoView = function(data, isMixed) {
) )
.appendTo($text); .appendTo($text);
// Director, Year and Country, Language -------------------------------- // Director, Year and Country ----------------------------------------------
renderGroup(['director', 'year', 'country', 'language']); renderGroup(['director', 'year', 'country']);
// Featuring ---------------------------------------------- // Featuring ----------------------------------------------
Ox.getObjectById(pandora.site.itemKeys, 'featuring') && renderGroup(['featuring']); renderGroup(['featuring']);
// Render any remaing keys defined in config
renderRemainingKeys();
// Summary ----------------------------------------------------------------- // Summary -----------------------------------------------------------------
@ -291,7 +278,6 @@ pandora.ui.infoView = function(data, isMixed) {
.appendTo($text); .appendTo($text);
} }
// Duration, Aspect Ratio -------------------------------------------------- // Duration, Aspect Ratio --------------------------------------------------
if (!isMultiple) { if (!isMultiple) {
['duration', 'aspectratio'].forEach(function(key) { ['duration', 'aspectratio'].forEach(function(key) {
@ -368,7 +354,7 @@ pandora.ui.infoView = function(data, isMixed) {
.append( .append(
Ox.EditableContent({ Ox.EditableContent({
height: 128, height: 128,
placeholder: formatLight(Ox._(isMixed.notes ? 'Mixed notes' : 'No notes')), placeholder: formatLight(Ox._(isMixed ? 'Mixed notes' : 'No notes')),
tooltip: pandora.getEditTooltip(), tooltip: pandora.getEditTooltip(),
type: 'textarea', type: 'textarea',
value: data.notes || '', value: data.notes || '',
@ -385,6 +371,19 @@ pandora.ui.infoView = function(data, isMixed) {
$('<div>').css({height: '16px'}).appendTo($statistics); $('<div>').css({height: '16px'}).appendTo($statistics);
function cleanupDate(value) {
if (/\d{2}-\d{2}-\d{4}/.test(value)) {
value = Ox.reverse(value.split('-')).join('-')
}
if (/\d{4}i\/\d{2}\/\d{d}/.test(value)) {
value = value.split('/').join('-')
}
if (/\d{2}\/\d{2}\/\d{4}/.test(value)) {
value = Ox.reverse(value.split('/')).join('-')
}
return value
}
function editMetadata(key, value) { function editMetadata(key, value) {
if (value != data[key]) { if (value != data[key]) {
var itemKey = Ox.getObjectById(pandora.site.itemKeys, key); var itemKey = Ox.getObjectById(pandora.site.itemKeys, key);
@ -401,7 +400,7 @@ pandora.ui.infoView = function(data, isMixed) {
edit[key] = value ? value : null; edit[key] = value ? value : null;
} }
if (itemKey && itemKey.type && itemKey.type[0] == 'date') { if (itemKey && itemKey.type && itemKey.type[0] == 'date') {
edit[key] = edit[key].map(pandora.cleanupDate); edit[key] = edit[key].map(cleanupDate);
} }
pandora.api.edit(edit, function(result) { pandora.api.edit(edit, function(result) {
if (!isMultiple) { if (!isMultiple) {
@ -475,7 +474,7 @@ pandora.ui.infoView = function(data, isMixed) {
listKeys.indexOf(key) > -1 && Ox.getObjectById(pandora.site.itemKeys, key).type[0] == 'date' listKeys.indexOf(key) > -1 && Ox.getObjectById(pandora.site.itemKeys, key).type[0] == 'date'
) { ) {
ret = value.split('; ').map(function(date) { ret = value.split('; ').map(function(date) {
date = pandora.cleanupDate(date) date = cleanupDate(date)
return date ? formatLink(Ox.formatDate(date, return date ? formatLink(Ox.formatDate(date,
['', '%Y', '%B %Y', '%B %e, %Y'][date.split('-').length], ['', '%Y', '%B %Y', '%B %e, %Y'][date.split('-').length],
true true
@ -589,7 +588,6 @@ pandora.ui.infoView = function(data, isMixed) {
function renderGroup(keys) { function renderGroup(keys) {
var $element; var $element;
keys.forEach(function(key) { displayedKeys.push(key) });
if (canEdit || keys.filter(function(key) { if (canEdit || keys.filter(function(key) {
return data[key]; return data[key];
}).length) { }).length) {
@ -621,17 +619,6 @@ pandora.ui.infoView = function(data, isMixed) {
} }
} }
function renderRemainingKeys() {
var keys = pandora.site.itemKeys.filter(function(item) {
return item.id != '*' && item.type != 'layer' && !Ox.contains(displayedKeys, item.id);
}).map(function(item) {
return item.id;
});
if (keys.length) {
renderGroup(keys)
}
}
function renderRightsLevel() { function renderRightsLevel() {
var $rightsLevelElement = getRightsLevelElement(data.rightslevel), var $rightsLevelElement = getRightsLevelElement(data.rightslevel),
$rightsLevelSelect; $rightsLevelSelect;

View file

@ -1,16 +0,0 @@
'use strict';
pandora.cleanupDate = function(value) {
if (/\d{2}-\d{2}-\d{4}/.test(value)) {
value = Ox.reverse(value.split('-')).join('-')
}
if (/\d{4}i\/\d{2}\/\d{d}/.test(value)) {
value = value.split('/').join('-')
}
if (/\d{2}\/\d{2}\/\d{4}/.test(value)) {
value = Ox.reverse(value.split('/')).join('-')
}
return value
};

View file

@ -41,28 +41,11 @@ pandora.ui.item = function() {
pandora.user.ui.itemView.slice(0, 1) pandora.user.ui.itemView.slice(0, 1)
) > -1 ? 'an': 'a') + ' ' ) > -1 ? 'an': 'a') + ' '
+'{1} view.', [result.data.title, Ox._(pandora.user.ui.itemView)]); +'{1} view.', [result.data.title, Ox._(pandora.user.ui.itemView)]);
pandora.$ui.contentPanel.replaceElement(1,
var note = Ox.Element() Ox.Element()
.css({marginTop: '32px', fontSize: '12px', textAlign: 'center'}) .css({marginTop: '32px', fontSize: '12px', textAlign: 'center'})
pandora.$ui.contentPanel.replaceElement(1, note); .html(html)
if (pandora.user.username == result.data.user || pandora.hasCapability('canSeeAllTasks')) { );
pandora.api.getTasks({
user: pandora.hasCapability('canSeeAllTasks') ? '' : pandora.user.username
}, function(result_) {
var tasks = result_.data.items.filter(function(task) { return task.item == item})
if (tasks.length > 0) {
html = Ox._(
'<i>{0}</i> is currently processed. '
+ '{1} view will be available in a moment.',
[result.data.title, Ox._(pandora.user.ui.itemView)]
)
}
note.html(html)
})
} else {
note.html(html)
}
pandora.site.itemViews.filter(function(view) { pandora.site.itemViews.filter(function(view) {
return view.id == 'documents'; return view.id == 'documents';
}).length && pandora.api.get({ }).length && pandora.api.get({

View file

@ -52,7 +52,170 @@ pandora.ui.mainMenu = function() {
] }, ] },
getListMenu(), getListMenu(),
getItemMenu(), getItemMenu(),
getViewMenu(), { id: 'viewMenu', title: Ox._('View'), items: [
{ id: 'section', title: Ox._('Section'), items: [
{ group: 'viewsection', min: 1, max: 1, items: Object.keys(pandora.site.sectionFolders).map(function(section) {
return {
id: section,
title: section == 'items' ? Ox._(pandora.site.itemName.plural) : Ox._(Ox.toTitleCase(section)),
checked: ui.section == section
};
}) }
] },
{},
{ id: 'movies', title: Ox._('View {0}', [Ox._(pandora.site.itemName.plural)]), items: [
{ group: 'listview', min: 1, max: 1, items: pandora.site.listViews.map(function(view) {
return Ox.extend({
checked: ui.listView == view.id
}, view, {
keyboard: listViewKey <= 10
? 'shift ' + (listViewKey++%10)
: void 0,
title: Ox._(view.title)
});
}) },
]},
{ id: 'icons', title: Ox._('Icons'), items: [].concat([
{ group: 'viewicons', min: 1, max: 1, items: ['posters', 'frames'].map(function(icons) {
return {id: icons, title: Ox._(Ox.toTitleCase(icons)), checked: ui.icons == icons};
}) },
{},
], pandora.site.media.importPosters ? [
{ id: 'showsiteposters', title: Ox._('Always Show {0} Poster', [pandora.site.site.name]), checked: ui.showSitePosters },
{}
] : [], [
{ id: 'showreflections', title: Ox._('Show Reflections'), checked: true, disabled: true }
]
) },
{ id: 'timelines', title: Ox._('Timelines'), items: [
{ group: 'viewtimelines', min: 1, max: 1, items: pandora.site.timelines.map(function(mode) {
return {id: mode.id, title: Ox._(mode.title), checked: ui.videoTimeline == mode.id};
}) }
]},
{ id: 'columns', title: Ox._('Columns'), items: [
{ id: 'loadcolumns', title: Ox._('Load Layout...'), disabled: true },
{ id: 'savecolumns', title: Ox._('Save Layout...'), disabled: true },
{},
{ id: 'resetcolumns', title: Ox._('Reset Layout'), disabled: true }
] },
{ id: 'filters', title: Ox._('Filters'), disabled: ui.section != 'items', items: [
{ group: 'filters', min: 5, max: 5, items: pandora.site.filters.map(function(filter) {
return Ox.extend({
checked: Ox.getIndexById(ui.filters, filter.id) > -1
}, filter, {
title: Ox._(filter.title)
});
}) },
{},
{ id: 'resetfilters', title: Ox._('Reset Filters') }
] },
{},
{ id: 'item', title: [
Ox._('Open {0}', [Ox._(pandora.site.itemName.singular)]),
Ox._('Open {0}', [Ox._(pandora.site.itemName.plural)])
], items: [
{ group: 'itemview', min: 1, max: 1, items: pandora.site.itemViews.filter(function(view) {
return view.id != 'data' && view.id != 'media' ||
pandora.hasCapability('canSeeExtraItemViews');
}).map(function(view) {
return Ox.extend({
checked: ui.itemView == view.id
}, view, {
keyboard: itemViewKey <= 10
? 'shift ' + (itemViewKey++%10)
: void 0,
title: Ox._(view.title)
});
}) },
] },
{ id: 'clips', title: Ox._('Open Clips'), items: [
{ group: 'videoview', min: 1, max: 1, items: ['player', 'editor', 'timeline'].map(function(view) {
return {id: view, title: Ox._(Ox.toTitleCase(view)), checked: ui.videoView == view};
}) }
] },
{ id: 'documents', title: Ox._('Open Documents'), items: [
{ group: 'documentview', min: 1, max: 1, items: ['info', 'view'].map(function(id) {
return {
id: id,
checked: ui.documentView == id,
keyboard: documentViewKey <= 10
? 'shift ' + (documentViewKey++%10)
: void 0,
title: Ox._(Ox.toTitleCase(id))
}
}) }
] },
{},
{
id: 'showsidebar',
title: Ox._((ui.showSidebar ? 'Hide' : 'Show') + ' Sidebar'),
keyboard: 'shift s'
},
{
id: 'showinfo',
title: Ox._((ui.showInfo ? 'Hide' : 'Show') + ' Info'),
disabled: !ui.showSidebar, keyboard: 'shift i'
},
{
id: 'showfilters',
title: Ox._((ui.showFilters ? 'Hide' : 'Show') + ' Filters'),
disabled: ui.section != 'items' || !!ui.item, keyboard: 'shift f'
},
{
id: 'showbrowser',
title: Ox._((ui.showBrowser ? 'Hide': 'Show') + ' {0} Browser', [Ox._(pandora.site.itemName.singular)]),
disabled: !ui.item, keyboard: 'shift b'
},
{
id: 'showdocument',
title: Ox._((ui.showDocument ? 'Hide' : 'Show') + ' Document'),
disabled: !hasDocument(), keyboard: 'shift d'
},
{
id: 'showtimeline',
title: Ox._((ui.showTimeline ? 'Hide' : 'Show') + ' Timeline'),
disabled: !hasTimeline(), keyboard: 'shift t'
},
{
id: 'showannotations',
title: Ox._((ui.showAnnotations ? 'Hide' : 'Show') + ' Annotations'),
disabled: !hasAnnotations(), keyboard: 'shift a'
},
{
id: 'showclips',
title: Ox._((ui.showClips ? 'Hide' : 'Show') + ' Clips'),
disabled: !hasClips(), keyboard: 'shift c'
},
{},
{
id: 'togglefullscreen',
title: Ox._((fullscreenState ? 'Exit' : 'Enter') + ' Fullscreen'),
disabled: fullscreenState === void 0,
keyboard: /^Mac/.test(window.navigator.platform)
? 'shift alt f'
: 'F11'
},
{
id: 'entervideofullscreen',
title: Ox._('Enter Video Fullscreen'),
disabled: !ui.item || ui.itemView != 'player'
},
{},
{ id: 'theme', title: Ox._('Theme'), items: [
{ group: 'settheme', min: 1, max: 1, items: pandora.site.themes.map(function(theme) {
return {id: theme, title: Ox.Theme.getThemeData(theme).themeName, checked: ui.theme == theme}
}) }
] },
{ id: 'locale',
title: Ox._('Language'), items: [
{ group: 'setlocale', min: 1, max: 1, items: pandora.site.languages.map(function(locale) {
return {id: locale, title: Ox.LOCALE_NAMES[locale], checked: ui.locale == locale}
}) }
] },
{},
{ id: 'embed', title: Ox._('Embed...') }
]},
getSortMenu(), getSortMenu(),
getFindMenu(), getFindMenu(),
{ id: 'dataMenu', title: Ox._('Data'), items: [ { id: 'dataMenu', title: Ox._('Data'), items: [
@ -66,10 +229,8 @@ pandora.ui.mainMenu = function() {
{ id: 'names', title: Ox._('Manage Names...'), disabled: !pandora.hasCapability('canManageTitlesAndNames') }, { id: 'names', title: Ox._('Manage Names...'), disabled: !pandora.hasCapability('canManageTitlesAndNames') },
{ id: 'translations', title: Ox._('Manage Translations...'), disabled: !pandora.hasCapability('canManageTranslations') }, { id: 'translations', title: Ox._('Manage Translations...'), disabled: !pandora.hasCapability('canManageTranslations') },
{}, {},
pandora.hasView('map') { id: 'places', title: Ox._('Manage Places...'), disabled: !pandora.hasCapability('canManagePlacesAndEvents') },
? [{ id: 'places', title: Ox._('Manage Places...'), disabled: !pandora.hasCapability('canManagePlacesAndEvents') }] : [], { id: 'events', title: Ox._('Manage Events...'), disabled: !pandora.hasCapability('canManagePlacesAndEvents') },
pandora.hasView('calendar')
? [{ id: 'events', title: Ox._('Manage Events...'), disabled: !pandora.hasCapability('canManagePlacesAndEvents') }] : [],
{}, {},
{ id: 'users', title: Ox._('Manage Users...'), disabled: !pandora.hasCapability('canManageUsers') }, { id: 'users', title: Ox._('Manage Users...'), disabled: !pandora.hasCapability('canManageUsers') },
{ id: 'statistics', title: Ox._('Statistics...'), disabled: !pandora.hasCapability('canManageUsers') }, { id: 'statistics', title: Ox._('Statistics...'), disabled: !pandora.hasCapability('canManageUsers') },
@ -151,9 +312,6 @@ pandora.ui.mainMenu = function() {
pandora.UI.set({listSort: [{key: value, operator: pandora.getSortOperator(value)}]}); pandora.UI.set({listSort: [{key: value, operator: pandora.getSortOperator(value)}]});
} else if (data.id == 'itemview') { } else if (data.id == 'itemview') {
pandora.UI.set({itemView: value}); pandora.UI.set({itemView: value});
} else if (data.id == 'collectionview') {
var set = {collectionView: value};
pandora.UI.set(set);
} else if (data.id == 'listview') { } else if (data.id == 'listview') {
var set = {listView: value}; var set = {listView: value};
if ( if (
@ -402,47 +560,6 @@ pandora.ui.mainMenu = function() {
}); });
} }
} else if (ui.section == 'documents') { } else if (ui.section == 'documents') {
var files, ids = [];
if (ui.document) {
files = [pandora.$ui.document.info()];
ids = [files[0].id];
} else {
files = pandora.$ui.list.options('selected').map(function(id) {
ids.push(id)
return pandora.$ui.list.value(id);
});
}
if (ui._collection) {
//fixme use history
//pandora.doHistory('delete', files, ui._collection, function() {
pandora.api.removeCollectionItems({
collection: ui._collection,
items: ids
}, function() {
pandora.UI.set({collectionSelection: []});
pandora.reloadList();
});
} else {
pandora.ui.deleteDocumentDialog(
files,
function() {
Ox.Request.clearCache();
if (ui.document) {
pandora.UI.set({document: ''});
} else {
pandora.$ui.list.reloadList()
}
}
).open();
}
} else if (ui.section == 'edits') {
var clips = pandora.$ui.editPanel.getSelectedClips();
pandora.doHistory('delete', clips, ui.edit, function(result) {
pandora.$ui.editPanel.updatePanel(function() {});
});
}
} else if (data.id == 'deletefromarchive') {
if (ui.section == 'documents') {
var files; var files;
if (ui.document) { if (ui.document) {
files = [pandora.$ui.document.info()]; files = [pandora.$ui.document.info()];
@ -462,6 +579,11 @@ pandora.ui.mainMenu = function() {
} }
} }
).open(); ).open();
} else if (ui.section == 'edits') {
var clips = pandora.$ui.editPanel.getSelectedClips();
pandora.doHistory('delete', clips, ui.edit, function(result) {
pandora.$ui.editPanel.updatePanel(function() {});
});
} }
} else if (data.id == 'undo') { } else if (data.id == 'undo') {
fromMenu = true; fromMenu = true;
@ -473,17 +595,10 @@ pandora.ui.mainMenu = function() {
fromMenu = true; fromMenu = true;
pandora.history.clear(); pandora.history.clear();
} else if (data.id == 'resetfilters') { } else if (data.id == 'resetfilters') {
if (ui.section == 'documents') { pandora.UI.set({
pandora.UI.set({ filters: pandora.site.user.ui.filters
documentFilters: pandora.site.user.ui.documentFilters });
}); pandora.$ui.contentPanel.replaceElement(0, pandora.$ui.browser = pandora.ui.browser());
pandora.$ui.documentContentPanel.replaceElement(0, pandora.$ui.documentBrowser = pandora.ui.documentBrowser());
} else {
pandora.UI.set({
filters: pandora.site.user.ui.filters
});
pandora.$ui.contentPanel.replaceElement(0, pandora.$ui.browser = pandora.ui.browser());
}
} else if (data.id == 'showsidebar') { } else if (data.id == 'showsidebar') {
pandora.UI.set({showSidebar: !ui.showSidebar}); pandora.UI.set({showSidebar: !ui.showSidebar});
} else if (data.id == 'showinfo') { } else if (data.id == 'showinfo') {
@ -723,9 +838,6 @@ pandora.ui.mainMenu = function() {
); );
} }
}, },
pandora_collectionview: function(data) {
that.checkItem('viewMenu_documents_' + data.value);
},
pandora_listview: function(data) { pandora_listview: function(data) {
that.checkItem('viewMenu_movies_' + data.value); that.checkItem('viewMenu_movies_' + data.value);
if ( if (
@ -743,7 +855,6 @@ pandora.ui.mainMenu = function() {
}, },
pandora_section: function(data) { pandora_section: function(data) {
lists = {}; lists = {};
that.replaceMenu('viewMenu', getViewMenu());
that.checkItem('viewMenu_section_' + data.value); that.checkItem('viewMenu_section_' + data.value);
that.replaceMenu('listMenu', getListMenu()); that.replaceMenu('listMenu', getListMenu());
that.replaceMenu('itemMenu', getItemMenu()); that.replaceMenu('itemMenu', getItemMenu());
@ -1081,11 +1192,7 @@ pandora.ui.mainMenu = function() {
{ id: 'paste', title: clipboardItems == 0 ? Ox._('Paste') : Ox._('Paste {0}', [clipboardItemName]), disabled: !canPaste, keyboard: 'control v' }, { id: 'paste', title: clipboardItems == 0 ? Ox._('Paste') : Ox._('Paste {0}', [clipboardItemName]), disabled: !canPaste, keyboard: 'control v' },
{ id: 'clearclipboard', title: Ox._('Clear Clipboard'), disabled: !clipboardItems}, { id: 'clearclipboard', title: Ox._('Clear Clipboard'), disabled: !clipboardItems},
{}, {},
[ { id: 'delete', title: Ox._('{0} {1} {2}', [deleteVerb, selectionItemName, listName]), disabled: !canDelete, keyboard: 'delete' },
{ id: 'delete', title: Ox._('{0} {1} {2}', [deleteVerb, selectionItemName, listName]), disabled: !canDelete, keyboard: 'delete' }
].concat(ui._collection ? [
{ id: 'deletefromarchive', title: Ox._('{0} {1} {2}', [Ox._('Delete'), selectionItemName, Ox._('from Archive')]), disabled: !canDelete }
] : []),
{}, {},
{ id: 'undo', title: undoText ? Ox._('Undo {0}', [undoText]) : Ox._('Undo'), disabled: !undoText, keyboard: 'control z' }, { id: 'undo', title: undoText ? Ox._('Undo {0}', [undoText]) : Ox._('Undo'), disabled: !undoText, keyboard: 'control z' },
{ id: 'redo', title: redoText ? Ox._('Redo {0}', [redoText]) : Ox._('Redo'), disabled: !redoText, keyboard: 'shift control z' }, { id: 'redo', title: redoText ? Ox._('Redo {0}', [redoText]) : Ox._('Redo'), disabled: !redoText, keyboard: 'shift control z' },
@ -1448,123 +1555,6 @@ pandora.ui.mainMenu = function() {
] }; ] };
} }
function getSectionViews() {
if (ui.section == 'documents') {
return [
{ id: 'documents', title: Ox._('View Documents'), items: [
{ group: 'collectionview', min: 1, max: 1, items: pandora.site.listViews.filter(function(view) {
return Ox.contains(['list', 'grid'], view.id)
}).map(function(view) {
return Ox.extend({
checked: ui.collectionView == view.id
}, view, {
keyboard: listViewKey <= 10
? 'shift ' + (listViewKey++%10)
: void 0,
title: Ox._(view.title)
});
}) },
]},
{ id: 'filters', title: Ox._('Filters'), items: [
{ group: 'filters', min: 5, max: 5, items: pandora.site.documentFilters.map(function(filter) {
return Ox.extend({
checked: Ox.getIndexById(ui.documentFilters, filter.id) > -1
}, filter, {
title: Ox._(filter.title)
});
}) },
{},
{ id: 'resetfilters', title: Ox._('Reset Filters') }
] },
]
} else {
return [
{ id: 'movies', title: Ox._('View {0}', [Ox._(pandora.site.itemName.plural)]), items: [
{ group: 'listview', min: 1, max: 1, items: pandora.site.listViews.map(function(view) {
return Ox.extend({
checked: ui.listView == view.id
}, view, {
keyboard: listViewKey <= 10
? 'shift ' + (listViewKey++%10)
: void 0,
title: Ox._(view.title)
});
}) },
]},
{ id: 'icons', title: Ox._('Icons'), items: [].concat([
{ group: 'viewicons', min: 1, max: 1, items: ['posters', 'frames'].map(function(icons) {
return {id: icons, title: Ox._(Ox.toTitleCase(icons)), checked: ui.icons == icons};
}) },
{},
], pandora.site.media.importPosters ? [
{ id: 'showsiteposters', title: Ox._('Always Show {0} Poster', [pandora.site.site.name]), checked: ui.showSitePosters },
{}
] : [], [
{ id: 'showreflections', title: Ox._('Show Reflections'), checked: true, disabled: true }
]
) },
{ id: 'timelines', title: Ox._('Timelines'), items: [
{ group: 'viewtimelines', min: 1, max: 1, items: pandora.site.timelines.map(function(mode) {
return {id: mode.id, title: Ox._(mode.title), checked: ui.videoTimeline == mode.id};
}) }
]},
{ id: 'columns', title: Ox._('Columns'), items: [
{ id: 'loadcolumns', title: Ox._('Load Layout...'), disabled: true },
{ id: 'savecolumns', title: Ox._('Save Layout...'), disabled: true },
{},
{ id: 'resetcolumns', title: Ox._('Reset Layout'), disabled: true }
] },
{ id: 'filters', title: Ox._('Filters'), disabled: ui.section != 'items', items: [
{ group: 'filters', min: 5, max: 5, items: pandora.site.filters.map(function(filter) {
return Ox.extend({
checked: Ox.getIndexById(ui.filters, filter.id) > -1
}, filter, {
title: Ox._(filter.title)
});
}) },
{},
{ id: 'resetfilters', title: Ox._('Reset Filters') }
] },
{},
{ id: 'item', title: [
Ox._('Open {0}', [Ox._(pandora.site.itemName.singular)]),
Ox._('Open {0}', [Ox._(pandora.site.itemName.plural)])
], items: [
{ group: 'itemview', min: 1, max: 1, items: pandora.site.itemViews.filter(function(view) {
return view.id != 'data' && view.id != 'media' ||
pandora.hasCapability('canSeeExtraItemViews');
}).map(function(view) {
return Ox.extend({
checked: ui.itemView == view.id
}, view, {
keyboard: itemViewKey <= 10
? 'shift ' + (itemViewKey++%10)
: void 0,
title: Ox._(view.title)
});
}) },
] },
{ id: 'clips', title: Ox._('Open Clips'), items: [
{ group: 'videoview', min: 1, max: 1, items: ['player', 'editor', 'timeline'].map(function(view) {
return {id: view, title: Ox._(Ox.toTitleCase(view)), checked: ui.videoView == view};
}) }
] },
{ id: 'documents', title: Ox._('Open Documents'), items: [
{ group: 'documentview', min: 1, max: 1, items: ['info', 'view'].map(function(id) {
return {
id: id,
checked: ui.documentView == id,
keyboard: documentViewKey <= 10
? 'shift ' + (documentViewKey++%10)
: void 0,
title: Ox._(Ox.toTitleCase(id))
}
}) }
] }
]
}
}
function getSortMenu() { function getSortMenu() {
if (ui.section == 'documents') { if (ui.section == 'documents') {
@ -1644,94 +1634,6 @@ pandora.ui.mainMenu = function() {
] }; ] };
} }
function getViewMenu() {
return { id: 'viewMenu', title: Ox._('View'), items: [
{ id: 'section', title: Ox._('Section'), items: [
{ group: 'viewsection', min: 1, max: 1, items: pandora.site.sections.map(function(section) {
section = Ox.extend({}, section)
section.checked = section.id == ui.section;
return section;
}) }
] },
{},
getSectionViews(),
{},
{
id: 'showsidebar',
title: Ox._((ui.showSidebar ? 'Hide' : 'Show') + ' Sidebar'),
keyboard: 'shift s'
},
{
id: 'showinfo',
title: Ox._((ui.showInfo ? 'Hide' : 'Show') + ' Info'),
disabled: !ui.showSidebar, keyboard: 'shift i'
},
{
id: 'showfilters',
title: Ox._((ui.showFilters ? 'Hide' : 'Show') + ' Filters'),
disabled: (
!Ox.contains(['items', 'documents'], ui.section) ||
(ui.section == 'items' && !!ui.item) ||
(ui.section == 'documents' && !!ui.document)
), keyboard: 'shift f'
},
{
id: 'showbrowser',
title: Ox._((ui.showBrowser ? 'Hide': 'Show') + ' {0} Browser', [Ox._(pandora.site.itemName.singular)]),
disabled: !ui.item, keyboard: 'shift b'
},
{
id: 'showdocument',
title: Ox._((ui.showDocument ? 'Hide' : 'Show') + ' Document'),
disabled: !hasDocument(), keyboard: 'shift d'
},
{
id: 'showtimeline',
title: Ox._((ui.showTimeline ? 'Hide' : 'Show') + ' Timeline'),
disabled: !hasTimeline(), keyboard: 'shift t'
},
{
id: 'showannotations',
title: Ox._((ui.showAnnotations ? 'Hide' : 'Show') + ' Annotations'),
disabled: !hasAnnotations(), keyboard: 'shift a'
},
{
id: 'showclips',
title: Ox._((ui.showClips ? 'Hide' : 'Show') + ' Clips'),
disabled: !hasClips(), keyboard: 'shift c'
},
{},
{
id: 'togglefullscreen',
title: Ox._((fullscreenState ? 'Exit' : 'Enter') + ' Fullscreen'),
disabled: fullscreenState === void 0,
keyboard: /^Mac/.test(window.navigator.platform)
? 'shift alt f'
: 'F11'
},
{
id: 'entervideofullscreen',
title: Ox._('Enter Video Fullscreen'),
disabled: !ui.item || ui.itemView != 'player'
},
{},
{ id: 'theme', title: Ox._('Theme'), items: [
{ group: 'settheme', min: 1, max: 1, items: pandora.site.themes.map(function(theme) {
return {id: theme, title: Ox.Theme.getThemeData(theme).themeName, checked: ui.theme == theme}
}) }
] },
{ id: 'locale',
title: Ox._('Language'), items: [
{ group: 'setlocale', min: 1, max: 1, items: pandora.site.languages.map(function(locale) {
return {id: locale, title: Ox.LOCALE_NAMES[locale], checked: ui.locale == locale}
}) }
] },
{},
{ id: 'embed', title: Ox._('Embed...') }
]}
}
function hasAnnotations() { function hasAnnotations() {
return ui.section == 'items' && ui.item && pandora.isVideoView(); return ui.section == 'items' && ui.item && pandora.isVideoView();
} }

View file

@ -23,39 +23,8 @@ pandora.ui.mainPanel = function() {
.bindEvent({ .bindEvent({
pandora_finddocuments: function() { pandora_finddocuments: function() {
var previousUI = pandora.UI.getPrevious(); var previousUI = pandora.UI.getPrevious();
Ox.Log('FIND', 'finddocuments handled in mainPanel', previousUI.document, previousUI._collection) if (!previousUI.document && ui._list == previousUI._list) {
if (!previousUI.document && ui._collection == previousUI._collection) { that.replaceElement(1, pandora.$ui.documentPanel = pandora.ui.documentPanel());
pandora.$ui.list.reloadList();
// FIXME: why is this being handled _here_?
ui._documentFilterState.forEach(function(data, i) {
if (!Ox.isEqual(data.selected, previousUI._documentFilterState[i].selected)) {
pandora.$ui.documentFilters[i].options(
ui.showFilters ? {
selected: data.selected
} : {
_selected: data.selected,
selected: []
}
);
}
if (!Ox.isEqual(data.find, previousUI._documentFilterState[i].find)) {
if (!ui.showFilters) {
pandora.$ui.documentFilters[i].options({
_selected: data.selected
});
}
// we can call reloadList here, since the items function
// handles the hidden filters case without making requests
pandora.$ui.documentFilters[i].reloadList();
}
});
} else {
if (pandora.stayInItemView) {
pandora.stayInItemView = false;
} else {
that.replaceElement(1, pandora.$ui.documentPanel = pandora.ui.documentPanel());
}
} }
}, },
pandora_document: function(data) { pandora_document: function(data) {
@ -68,7 +37,7 @@ pandora.ui.mainPanel = function() {
}, },
pandora_find: function() { pandora_find: function() {
var previousUI = pandora.UI.getPrevious(); var previousUI = pandora.UI.getPrevious();
Ox.Log('FIND', 'find handled in mainPanel', previousUI.item, previousUI._list) Ox.Log('FIND', 'handled in mainPanel', previousUI.item, previousUI._list)
if (!previousUI.item && ui._list == previousUI._list) { if (!previousUI.item && ui._list == previousUI._list) {
if (['map', 'calendar'].indexOf(ui.listView) > -1) { if (['map', 'calendar'].indexOf(ui.listView) > -1) {
pandora.$ui.contentPanel.replaceElement(1, pandora.$ui.contentPanel.replaceElement(1,

View file

@ -406,7 +406,7 @@ pandora.ui.mediaView = function(options) {
var conditions, matches; var conditions, matches;
if (key == 'id' && data.value.substr(0, 2) != '0x') { if (key == 'id' && data.value.substr(0, 2) != '0x') {
if (pandora.site.site.id == '0xdb') { if (pandora.site.site.id == '0xdb') {
matches = data.value.match(/\d+/); matches = data.value.match(/\d{7}/);
} else { } else {
matches = data.value.match(/[A-Z]+/); matches = data.value.match(/[A-Z]+/);
} }

View file

@ -181,9 +181,6 @@ pandora.ui.metadataDialog = function(data) {
} }
function getKey(key) { function getKey(key) {
if (Ox.getObjectById(pandora.site.itemKeys, key) && mapKeys[key]) {
return key
}
return mapKeys[key] || key; return mapKeys[key] || key;
} }
@ -225,7 +222,7 @@ pandora.ui.metadataDialog = function(data) {
if (result.data) { if (result.data) {
imdb = Ox.clone(result.data, true); imdb = Ox.clone(result.data, true);
if (!Ox.contains(keys, 'originalTitle')) { if (!Ox.contains(keys, 'originalTitle')) {
if (imdb.originalTitle && imdb.originalTitle != imdb.title) { if (imdb.originalTitle) {
imdb.alternativeTitles = [[imdb.title, []]].concat(imdb.alternativeTitles || []); imdb.alternativeTitles = [[imdb.title, []]].concat(imdb.alternativeTitles || []);
imdb.title = imdb.originalTitle; imdb.title = imdb.originalTitle;
} }

View file

@ -281,37 +281,6 @@ appPanel
resize: pandora.resizeWindow, resize: pandora.resizeWindow,
unload: pandora.unloadWindow unload: pandora.unloadWindow
}) })
Ox.$document.on({
dragenter: function(event) {
if (Ox.contains(event.originalEvent.dataTransfer.types, 'Files')) {
event.originalEvent.preventDefault();
event.originalEvent.stopPropagation();
if (!$('#importScreen').length) {
pandora.ui.importScreen().appendTo(Ox.$body);
}
} else {
console.log(event.originalEvent.dataTransfer);
}
},
dragover: function(event) {
event.originalEvent.preventDefault();
event.originalEvent.stopPropagation();
},
dragstart: function(event) {
event.originalEvent.preventDefault();
event.originalEvent.stopPropagation();
},
drop: function(event) {
$('#importScreen').remove();
if (pandora.hasCapability('canAddItems')) {
if (event.originalEvent.dataTransfer.files.length) {
event.originalEvent.preventDefault();
event.originalEvent.stopPropagation();
pandora.uploadDroppedFiles(event.originalEvent.dataTransfer.files)
}
}
}
});
Ox.extend(pandora, { Ox.extend(pandora, {
$ui: {}, $ui: {},
site: data.site, site: data.site,
@ -395,11 +364,6 @@ appPanel
}) ? 'manual' : data.site.layers.some(function(layer) { }) ? 'manual' : data.site.layers.some(function(layer) {
return layer.hasPlaces; return layer.hasPlaces;
}) ? 'auto' : 'none', }) ? 'auto' : 'none',
sections: [
{id: 'items', title: Ox._(pandora.site.itemName.plural)},
{id: 'edits', title: Ox._('Edits')},
{id: 'documents', title: Ox._('Documents')}
],
sectionFolders: { sectionFolders: {
items: [ items: [
{id: 'personal', title: 'Personal Lists'}, {id: 'personal', title: 'Personal Lists'},

View file

@ -43,19 +43,11 @@ pandora.ui.preferencesDialog = function() {
id: 'password', id: 'password',
label: Ox._('New Password'), label: Ox._('New Password'),
labelWidth: 120, labelWidth: 120,
type: 'text', type: 'password',
validate: pandora.validateNewPassword, validate: pandora.validateNewPassword,
width: 320 width: 320
}) })
.attr({
autocomplete: 'new-password'
})
.bindEvent({ .bindEvent({
focus: function(data) {
this.options({
type: 'password'
})
},
validate: function(data) { validate: function(data) {
data.valid && pandora.api.editPreferences({password: data.value}); data.valid && pandora.api.editPreferences({password: data.value});
} }

View file

@ -2,7 +2,11 @@
pandora.ui.sectionButtons = function(section) { pandora.ui.sectionButtons = function(section) {
var that = Ox.ButtonGroup({ var that = Ox.ButtonGroup({
buttons: pandora.site.sections, buttons: [
{id: 'items', title: Ox._(pandora.site.itemName.plural)},
{id: 'edits', title: Ox._('Edits')},
{id: 'documents', title: Ox._('Documents')}
],
id: 'sectionButtons', id: 'sectionButtons',
selectable: true, selectable: true,
value: section || pandora.user.ui.section value: section || pandora.user.ui.section

View file

@ -4,7 +4,11 @@ pandora.ui.sectionSelect = function(section) {
// fixme: duplicated // fixme: duplicated
var that = Ox.Select({ var that = Ox.Select({
id: 'sectionSelect', id: 'sectionSelect',
items: pandora.site.sections, items: [
{id: 'items', title: Ox._(pandora.site.itemName.plural)},
{id: 'edits', title: Ox._('Edits')},
{id: 'documents', title: Ox._('Documents')}
],
value: section || pandora.user.ui.section value: section || pandora.user.ui.section
}).css({ }).css({
float: 'left', float: 'left',

View file

@ -1,30 +1,16 @@
'use strict'; 'use strict';
pandora.ui.textPanel = function(text, $toolbar) { pandora.ui.textPanel = function(text, $toolbar) {
if (Ox.isUndefined(text.text)) {
var that = Ox.Element().append(Ox.LoadingScreen().start())
pandora.api.getDocument({
id: text.id,
keys: ['text']
}, function(result) {
text.text = result.data.text
if (text.text) {
pandora.$ui.textPanel.replaceWith(pandora.$ui.textPanel = pandora.ui.textPanel(text, $toolbar))
}
})
return that;
}
var textElement, var textElement,
textEmbed, textEmbed,
embedURLs = getEmbedURLs(text.text), embedURLs = getEmbedURLs(text.text),
that = Ox.SplitPanel({ that = Ox.SplitPanel({
elements: [ elements: [
{ {
element: textElement = pandora.ui.textHTML(text) element: textElement = pandora.$ui.textElement = pandora.ui.textHTML(text)
}, },
{ {
element: textEmbed = pandora.ui.textEmbed(), element: textEmbed = pandora.ui.textEmbed(textElement),
collapsed: !embedURLs.length, collapsed: !embedURLs.length,
size: pandora.user.ui.embedSize, size: pandora.user.ui.embedSize,
resizable: true, resizable: true,
@ -138,6 +124,7 @@ pandora.ui.textPanel = function(text, $toolbar) {
0), 0),
position = 100 * scrollTop / Math.max(1, textElement[0].scrollHeight); position = 100 * scrollTop / Math.max(1, textElement[0].scrollHeight);
textElement.scrollTo(position); textElement.scrollTo(position);
window.text = textElement;
} }
that.selectEmbed = function(index, scroll) { that.selectEmbed = function(index, scroll) {
@ -444,7 +431,7 @@ pandora.ui.textHTML = function(text) {
}; };
pandora.ui.textEmbed = function(textEmbed) { pandora.ui.textEmbed = function(textElement) {
var that = Ox.Element() var that = Ox.Element()
.bindEvent({ .bindEvent({

View file

@ -5,8 +5,6 @@ pandora.ui.uploadDocumentDialog = function(options, callback) {
extensions = files.map(function(file) { extensions = files.map(function(file) {
return file.name.split('.').pop().toLowerCase() return file.name.split('.').pop().toLowerCase()
}), }),
existingFiles = [],
uploadFiles = [],
supportedExtensions = ['gif', 'jpg', 'jpeg', 'pdf', 'png'], supportedExtensions = ['gif', 'jpg', 'jpeg', 'pdf', 'png'],
@ -58,9 +56,9 @@ pandora.ui.uploadDocumentDialog = function(options, callback) {
height: 112, height: 112,
keys: {escape: 'close'}, keys: {escape: 'close'},
width: 288, width: 288,
title: uploadFiles.length == 1 title: files.length == 1
? Ox._('Upload Document') ? Ox._('Upload Document')
: Ox._('Upload {0} Documents', [uploadFiles.length]) : Ox._('Upload {0} Documents', [files.length])
}) })
.bindEvent({ .bindEvent({
open: function() { open: function() {
@ -75,63 +73,46 @@ pandora.ui.uploadDocumentDialog = function(options, callback) {
Ox._('Supported file types are GIF, JPG, PNG and PDF.') Ox._('Supported file types are GIF, JPG, PNG and PDF.')
); );
} else { } else {
var oshashes = []; var valid = true;
Ox.parallelForEach(files, function(file, index, array, callback) { Ox.parallelForEach(files, function(file, index, array, callback) {
var extension = file.name.split('.').pop().toLowerCase(), var extension = file.name.split('.').pop().toLowerCase(),
filename = file.name.split('.').slice(0, -1).join('.') + '.' filename = file.name.split('.').slice(0, -1).join('.') + '.'
+ (extension == 'jpeg' ? 'jpg' : extension); + (extension == 'jpeg' ? 'jpg' : extension);
Ox.oshash(file, function(oshash) { valid && Ox.oshash(file, function(oshash) {
// skip duplicate files pandora.api.findDocuments({
if (Ox.contains(oshashes, oshash)) { keys: ['id', 'user', 'title', 'extension'],
query: {
conditions: [{
key: 'oshash',
operator: '==',
value: oshash
}],
operator: '&'
},
range: [0, 1],
sort: [{key: 'title', operator: '+'}]
}, function(result) {
if (result.data.items.length) {
var id = result.data.items[0].title + '.'
+ result.data.items[0].extension;
valid && errorDialog(
filename == id
? Ox._(
'The file "{0}" already exists.',
[filename]
)
: Ox._(
'The file "{0}" already exists as "{1}".',
[filename, id]
)
).open();
valid = false;
}
callback(); callback();
} else { })
oshashes.push(oshash)
pandora.api.findDocuments({
keys: ['id', 'user', 'title', 'extension'],
query: {
conditions: [{
key: 'oshash',
operator: '==',
value: oshash
}],
operator: '&'
},
range: [0, 1],
sort: [{key: 'title', operator: '+'}]
}, function(result) {
if (result.data.items.length) {
var id = result.data.items[0].title + '.'
+ result.data.items[0].extension;
existingFiles.push({
id: id,
filename: filename
})
} else {
uploadFiles.push(file)
}
callback();
})
}
}); });
} ,function() { } ,function() {
if (uploadFiles.length) { valid && $uploadDialog.open();
$uploadDialog.open();
} else if (existingFiles.length) {
var filename = existingFiles[0].filename
var id = existingFiles[0].id
errorDialog(
filename == id
? Ox._(
'The file "{0}" already exists.',
[filename]
)
: Ox._(
'The file "{0}" already exists as "{1}".',
[filename, id]
)
).open();
}
}); });
return {open: Ox.noop}; return {open: Ox.noop};
} }
@ -157,7 +138,7 @@ pandora.ui.uploadDocumentDialog = function(options, callback) {
function uploadFile(part) { function uploadFile(part) {
var data = { var data = {
}, },
file = uploadFiles[part], file = files[part],
extension = file.name.split('.').pop().toLowerCase(), extension = file.name.split('.').pop().toLowerCase(),
filename = file.name.split('.').slice(0, -1).join('.') + '.' filename = file.name.split('.').slice(0, -1).join('.') + '.'
+ (extension == 'jpeg' ? 'jpg' : extension); + (extension == 'jpeg' ? 'jpg' : extension);
@ -177,7 +158,7 @@ pandora.ui.uploadDocumentDialog = function(options, callback) {
if (data.progress == 1) { if (data.progress == 1) {
part++; part++;
ids.push(data.response.id); ids.push(data.response.id);
if (part == uploadFiles.length) { if (part == files.length) {
$progress.options({progress: data.progress}); $progress.options({progress: data.progress});
callback && callback({ids: ids}); callback && callback({ids: ids});
$uploadDialog.close(); $uploadDialog.close();
@ -193,7 +174,7 @@ pandora.ui.uploadDocumentDialog = function(options, callback) {
}, },
progress: function(data) { progress: function(data) {
var progress = data.progress || 0; var progress = data.progress || 0;
progress = part / uploadFiles.length + 1 / uploadFiles.length * progress; progress = part / files.length + 1 / files.length * progress;
$progress.options({progress: progress}); $progress.options({progress: progress});
} }
}); });

View file

@ -0,0 +1,599 @@
'use strict';
pandora.ui.uploadVideoDialog = function(data) {
var cancelled = false,
file,
hasFirefogg = !(Ox.isUndefined(window.Firefogg)) && (
$.browser.version < '35' || Firefogg().version >= 334
),
infoContent = Ox._('Please select the video file that you want to upload.'),
itemView = pandora.hasCapability('canSeeExtraItemViews') ? 'media' : 'info',
selectFile,
$actionButton,
$closeButton,
$content = Ox.Element().css({margin: '16px'}),
$info = $('<div>')
.css({padding: '4px'})
.html(infoContent),
$progress,
$status = $('<div>').css({padding: '4px', paddingTop: '8px'}),
that = Ox.Dialog({
buttons: [
$closeButton = Ox.Button({
id: 'close',
title: Ox._('Close')
}).css({
float: 'left'
}).bindEvent({
click: function() {
if ($closeButton.options('title') == Ox._('Cancel')) {
cancelled = true;
$info.html(infoContent);
$status.html('');
pandora.firefogg && pandora.firefogg.cancel();
pandora.$ui.upload && pandora.$ui.upload.abort();
$closeButton.options('title', Ox._('Close'));
if ($actionButton.options('title') == Ox._('Upload')) {
$closeButton.options('title', Ox._('Close'));
$actionButton.replaceWith($actionButton = hasFirefogg
? getFirefoggButton()
: getSelectVideoButton()
);
}
$actionButton.show();
} else {
that.triggerEvent('close');
}
}
}),
$actionButton = hasFirefogg ? getFirefoggButton() : getSelectVideoButton()
],
content: $content,
height: 128,
removeOnClose: true,
width: 368,
title: Ox._('Upload Video'),
})
.bindEvent({
close: function(data) {
if (pandora.firefogg) {
pandora.firefogg.cancel();
delete pandora.firefogg;
}
that.close();
}
});
if (!pandora.site.itemRequiresVideo && !pandora.user.ui.item) {
$info.html(Ox._(
'You can only upload a video to an existing {0}.'
+ ' Please check if an entry for the {0}'
+ ' you want to upload exists, and create one otherwise.',
[pandora.site.itemName.singular.toLowerCase()]
));
$actionButton.hide();
}
$content.append($info);
$content.append($status);
function aspectratio(ratio) {
var denominator, numerator;
ratio = ratio.split(':');
numerator = ratio[0];
if (ratio.length == 2) {
denominator = ratio[1];
}
if (Math.abs(numerator / denominator - 4/3) < 0.03) {
numerator = 4;
denominator = 3;
} else if (Math.abs(numerator / denominator - 16/9) < 0.02) {
numerator = 16;
denominator = 9;
}
return {
denominator: denominator,
'float': numerator / denominator,
numerator: numerator,
ratio: numerator + ':' + denominator
};
}
function resetProgress(status) {
$progress = Ox.Progressbar({
progress: 0,
showPercent: true,
showTime: true,
width: 304
});
$status.html(status || '').append($progress);
}
function directUpload(file, info) {
resetProgress();
pandora.api.addMedia({
filename: info.name,
id: info.oshash,
item: pandora.site.itemRequiresVideo
? undefined
: pandora.user.ui.item
}, function(result) {
uploadStream(result.data.item, info, file);
});
}
function encode() {
var filename = pandora.firefogg.sourceFilename,
info = JSON.parse(pandora.firefogg.sourceInfo),
item,
oshash = info.oshash;
$info.html('<b>' + filename + '</b><br>' + Ox._('encoding...'));
resetProgress();
pandora.api.addMedia({
filename: filename,
id: oshash,
info: info,
item: pandora.site.itemRequiresVideo
? undefined
: pandora.user.ui.item
}, function(result) {
item = result.data.item;
pandora.firefogg.encode(
getEncodingOptions(info),
function(result, file) {
result = JSON.parse(result);
if (result.progress != 1) {
$status.html(
cancelled
? Ox._('Encoding cancelled.')
: Ox._('Encoding failed.')
);
delete pandora.firefogg;
return;
}
setTimeout(function() {
$info.html(
'<b>' + filename + '</b><br>'
+ Ox._('uploading...')
);
uploadStream(item, info, file);
});
},
Ox.throttle(function(progress) {
progress = JSON.parse(progress).progress || 0;
$progress.options({progress: progress});
}, 1000)
);
});
}
function getInfo(file, callback) {
Ox.oshash(file, function(oshash) {
var $video = $('<video>'),
url = URL.createObjectURL(file),
info = {
audio: [],
direct: false,
oshash: oshash,
name: file.name,
size: file.size,
video: []
};
$video.one('error', function(event) {
callback(info);
});
$video.one('loadedmetadata', function(event) {
info.duration = $video[0].duration;
if ($video[0].videoHeight > 0) {
info.video.push({
height: $video[0].videoHeight,
width: $video[0].videoWidth
});
}
if (info.duration) {
info.bitrate = info.size * 8 / info.duration / 1000;
}
var format = pandora.site.video.formats[0],
resolution = getResolution(info);
info.direct = Ox.endsWith(info.name, format)
&& info.video.length > 0
&& info.video[0].height <= resolution;
callback(info);
});
$video[0].src = url;
});
}
function getResolution(info) {
var height = info.video && info.video.length
? info.video[0].height
: Ox.max(pandora.site.video.resolutions),
resolution = Ox.sort(pandora.site.video.resolutions)
.filter(function(resolution) {
return height <= resolution;
})[0] || Ox.max(pandora.site.video.resolutions);
return resolution;
}
function getFirefoggButton() {
return Ox.Button({
id: 'action',
title: Ox._('Select Video')
}).bindEvent({
click: function() {
if ($actionButton.options('title') == Ox._('Select Video')) {
if (selectVideo()) {
$closeButton.options('title', Ox._('Cancel'));
$actionButton.options('title', Ox._('Upload'));
}
} else if ($actionButton.options('title') == Ox._('Cancel')) {
cancelled = true;
pandora.firefogg && pandora.firefogg.cancel();
pandora.$ui.upload && pandora.$ui.upload.abort();
$actionButton.options('title', Ox._('Select Video'));
$closeButton.show();
} else {
$closeButton.options('title', Ox._('Cancel'));
$actionButton.hide().options('title', Ox._('Select Video'));
encode();
}
}
})
}
function getSelectVideoButton() {
return Ox.FileButton({
id: 'action',
title: Ox._('Select Video'),
maxFiles: 1,
width: 96
}).css({
float: 'left'
}).bindEvent({
click: function(data) {
if (data.files.length) {
cancelled = false;
$actionButton.replaceWith($actionButton = Ox.Button({
id: 'action',
title: 'Upload',
disabled: true
}).css({
float: 'left'
}));
getInfo(data.files[0], function(info) {
$actionButton.options({
disabled: false
}).bindEvent({
click: function() {
$actionButton.replaceWith($actionButton = getSelectVideoButton().hide());
info.direct
? directUpload(data.files[0], info)
: upload(data.files[0]);
}
});
$info.html(formatVideoInfo(info));
$status.html(
info.direct
? Ox._(
'Your video will be used directly.'
)
: Ox._(
'Your video will be transcoded on the server.'
)
);
});
$closeButton.options('title', Ox._('Cancel'));
}
}
});
}
function uploadStream(item, info, file) {
var oshash = info.oshash,
format = pandora.site.video.formats[0],
resolution = getResolution(info);
pandora.$ui.upload = pandora.chunkupload({
file: file,
url: '/api/upload/?profile=' + resolution + 'p.'
+ format + '&id=' + oshash,
data: {}
}).bindEvent({
done: function(data) {
if (data.progress == 1) {
Ox.Request.clearCache();
if (
pandora.user.ui.item == item
&& pandora.user.ui.itemView == itemView
) {
pandora.$ui.item.reload();
} else {
pandora.UI.set({
item: item,
itemView: itemView
});
}
delete pandora.firefogg;
that.close();
} else {
$status.html(Ox._('Upload failed.'));
pandora.api.logError({
text: data.responseText,
url: '/' + item,
line: 1
});
}
},
progress: function(data) {
$progress.options({progress: data.progress || 0});
},
});
}
function upload(file) {
resetProgress();
$info.html(Ox._('Uploading {0}', [file.name]));
Ox.oshash(file, function(oshash) {
pandora.api.findMedia({
query: {
conditions: [{key: 'oshash', value: oshash}]
},
keys: ['id', 'item', 'available']
}, function(result) {
if (
result.data.items.length === 0
|| !result.data.items[0].available
) {
pandora.api.addMedia({
filename: file.name,
id: oshash,
item: pandora.site.itemRequiresVideo
? undefined
: pandora.user.ui.item
}, function(result) {
var item = result.data.item;
pandora.$ui.upload = pandora.chunkupload({
file: file,
url: '/api/upload/direct/',
data: {
id: oshash
}
}).bindEvent({
done: function(data) {
if (data.progress == 1) {
Ox.Request.clearCache();
if (
pandora.user.ui.item == item
&& pandora.user.ui.itemView == itemView
) {
pandora.$ui.item.reload();
} else {
pandora.UI.set({
item: item,
itemView: itemView
});
}
that.close();
} else {
$status.html(
cancelled
? Ox._('Upload cancelled.')
: Ox._('Upload failed.')
);
!cancelled && pandora.api.logError({
text: data.responseText,
url: '/' + item,
line: 1
});
}
},
progress: function(data) {
$progress.options({
progress: data.progress || 0
});
}
});
});
} else {
pandora.UI.set({
item: result.data.items[0].item,
itemView: itemView
});
that.close();
}
});
});
}
function getEncodingOptions(info) {
var bpp = 0.17,
dar,
format = pandora.site.video.formats[0],
fps,
options = {},
resolution = getResolution(info);
if (format == 'webm') {
options.videoCodec = 'vp8';
options.audioCodec = 'vorbis';
} else if (format == 'ogv') {
options.videoCodec = 'theora';
options.audioCodec = 'vorbis';
}
if (resolution == 720) {
options.height = 720;
options.samplerate = 48000;
options.audioQuality = 5;
} else if (resolution == 480) {
options.height = 480;
options.samplerate = 44100;
options.audioQuality = 3;
options.channels = 2;
} else if (resolution == 432) {
options.height = 432;
options.samplerate = 44100;
options.audioQuality = 3;
options.channels = 2;
} else if (resolution == 360) {
options.height = 320;
options.samplerate = 44100;
options.audioQuality = 1;
options.channels = 1;
} else if (resolution == 288) {
options.height = 288;
options.samplerate = 44100;
options.audioQuality = 0;
options.channels = 1;
} else if (resolution == 240) {
options.height = 240;
options.samplerate = 44100;
options.audioQuality = 0;
options.channels = 1;
} else if (resolution == 144) {
options.height = 144;
options.samplerate = 22050;
options.audioQuality = -1;
options.audioBitrate = 22;
options.channels = 1;
} else if (resolution == 96) {
options.height = 96;
options.samplerate = 22050;
options.audioQuality = -1;
options.audioBitrate = 22;
options.channels = 1;
}
if (info.video && info.video.length) {
info.video.forEach(function(video) {
if (!video.display_aspect_ratio) {
video.display_aspect_ratio = video.width + ':' + video.height;
video.pixel_aspect_ratio = '1:1';
}
});
dar = aspectratio(info.video[0].display_aspect_ratio);
fps = aspectratio(info.video[0].framerate).float;
options.width = parseInt(dar.float * options.height, 10);
options.width += options.width % 2;
// interlaced hdv material is detected with double framerates
if (fps == 50) {
fps = options.framerate = 25;
} else if (fps == 60) {
fps = options.framerate = 30;
}
if (Math.abs(options.width/options.height - dar.float) < 0.02) {
options.aspect = options.width + ':' + options.height;
} else {
options.aspect = dar.ratio;
}
options.videoBitrate = Math.round(
options.height * options.width * fps * bpp / 1000
);
options.denoise = true;
options.deinterlace = true;
} else {
options.noVideo = true;
}
if (info.audio) {
if (options.cannels && info.audio[0].channels < options.channels) {
delete options.channels;
}
} else {
options.noAudio = true;
delete options.samplerate;
delete options.audioQuality;
delete options.channels;
}
options.noUpscaling = true;
if (
(!info.video.length || (
info.video[0].codec == options.videoCodec
&& info.video[0].height <= options.height
))
&& (!info.audio.length || info.audio[0].codec == options.audioCodec)
) {
options = {passthrough: true};
}
return JSON.stringify(options);
}
function formatInfo(info) {
var html = '<b>' + info.path + '</b><br>';
if (info.video && info.video.length > 0) {
var video = info.video[0];
html += video.width + '×' + video.height + ' (' + video.codec + ')';
}
if (
info.video && info.video.length > 0
&& info.audio && info.audio.length > 0
) {
html += ' / ';
}
if (info.audio && info.audio.length > 0) {
var audio = info.audio[0];
html += {1: 'mono', 2: 'stereo', 6: '5.1'}[audio.channels]
+ ' ' + audio.samplerate / 1000 + ' kHz (' + audio.codec + ')';
}
html += '<br>' + Ox.formatValue(info.size, 'B')
+ ' / ' + Ox.formatDuration(info.duration);
return html;
}
function formatVideoInfo(info) {
var html = '<b>' + info.name + '</b><br>';
if (info.video && info.video.length > 0) {
html += info.video[0].width + '×' + info.video[0].height;
}
html += '<br>' + Ox.formatValue(info.size, 'B');
if(info.duration) {
html += ' / ' + Ox.formatDuration(info.duration);
}
return html;
}
function selectVideo() {
cancelled = false;
pandora.firefogg = new Firefogg();
pandora.firefogg.setFormat(pandora.site.video.formats[0]);
if (pandora.firefogg.selectVideo()) {
var info = JSON.parse(pandora.firefogg.sourceInfo),
options = JSON.parse(getEncodingOptions(info)),
oshash = info.oshash,
filename = pandora.firefogg.sourceFilename,
item;
pandora.api.findMedia({
query: {
conditions: [{key: 'oshash', value: oshash}]
},
keys: ['id', 'available']
}, function(result) {
if (
result.data.items.length === 0
|| !result.data.items[0].available
) {
$info.html(formatInfo(info));
$status.html(
options.passthrough
? Ox._('Your video will be uploaded directly.')
: Ox._('Your video will be transcoded before upload.')
);
} else {
pandora.api.find({
query: {
conditions: [{key: 'oshash', value: oshash}]
},
keys: ['id']
}, function(result) {
pandora.UI.set({
item: result.data.items[0].id,
itemView: itemView
});
delete pandora.firefogg;
that.close();
});
}
});
return true;
}
return false;
}
return that;
};

View file

@ -39,6 +39,7 @@ pandora.addFolderItem = function(section) {
if (isItems) { if (isItems) {
data.items = ui.listSelection; data.items = ui.listSelection;
} else if (section == 'documents') { } else if (section == 'documents') {
//fixme
data.items = ui.collectionSelection; data.items = ui.collectionSelection;
} else { } else {
data.clips = pandora.getClipData( data.clips = pandora.getClipData(
@ -48,7 +49,7 @@ pandora.addFolderItem = function(section) {
); );
} }
} else { } else {
data.query = section == 'documents' ? ui.findDocuments : ui.find; data.query = ui.find;
} }
} }
if (ui.section == 'items' && section == 'edits') { if (ui.section == 'items' && section == 'edits') {
@ -81,28 +82,18 @@ pandora.addFolderItem = function(section) {
data.description = result.data.items[0].description; data.description = result.data.items[0].description;
if (data.type == 'static') { if (data.type == 'static') {
var query; var query;
if (Ox.contains(['items', 'documents'], section)) { if (isItems) {
query = { query = {
conditions: [{ conditions: [{
key: { key: 'list',
items: 'list',
documents: 'collection'
}[section],
operator: '==', operator: '==',
value: list value: list
}], }],
operator: '&' operator: '&'
}; };
pandora.api.find({query: query}, function(result) {
pandora.api[{
items: 'find',
documents: 'findDocuments'
}[section]]({query: query}, function(result) {
if (result.data.items) { if (result.data.items) {
pandora.api[{ pandora.api.find({
items: 'find',
documents: 'findDocuments'
}[section]]({
query: query, query: query,
keys: ['id'], keys: ['id'],
sort: [{key: 'id', operator: ''}], sort: [{key: 'id', operator: ''}],
@ -117,6 +108,9 @@ pandora.addFolderItem = function(section) {
addList(); addList();
} }
}); });
} else if(section == 'documents') {
//fixme
addList();
} else { } else {
pandora.api.getEdit({ pandora.api.getEdit({
id: list, id: list,
@ -151,7 +145,7 @@ pandora.addFolderItem = function(section) {
} }
function getPosterFrames(newList) { function getPosterFrames(newList) {
var query, var query,
sortKey = section == 'items' && Ox.getObjectById(pandora.site.itemKeys, 'votes') sortKey = Ox.getObjectById(pandora.site.itemKeys, 'votes')
? 'votes' : 'timesaccessed'; ? 'votes' : 'timesaccessed';
if (!isDuplicate) { if (!isDuplicate) {
({ ({
@ -410,34 +404,6 @@ pandora.createLinks = function($element) {
}); });
}; };
pandora.uploadDroppedFiles = function(files) {
var documentExtensions = ['pdf', /* 'epub', 'txt', */ 'png', 'gif', 'jpg', 'jpeg'];
files = Ox.map(files, function(file) { return file });
if (files.every(function(file) {
var extension = file.name.split('.').pop().toLowerCase()
return Ox.contains(documentExtensions, extension)
})) {
pandora.ui.uploadDocumentDialog({
files: files
}, function(files) {
if (files) {
Ox.Request.clearCache('findDocuments');
if (pandora.user.ui.document || pandora.user.ui.section != 'documents') {
pandora.UI.set({section: 'documents', document: ''});
} else {
pandora.$ui.list && pandora.$ui.list.reloadList();
}
}
}).open();
} else {
pandora.ui.addItemDialog({
files: files,
selected: 'upload'
}).open()
}
};
(function() { (function() {
pandora.doHistory = function(action, items, targets, index, callback) { pandora.doHistory = function(action, items, targets, index, callback) {
@ -1449,14 +1415,6 @@ pandora.getFoldersWidth = function(section) {
return width; return width;
}; };
pandora.getFindLayer = function() {
var key = pandora.user.ui._findState.key
if (!Ox.getObjectById(pandora.site.layers, key)) {
key = '*'
}
return key
};
pandora.getHash = function(state, callback) { pandora.getHash = function(state, callback) {
// FIXME: remove this // FIXME: remove this
var embedKeys = [ var embedKeys = [
@ -2377,7 +2335,6 @@ pandora.VIDEO_OPTIONS_KEYS = [
'rendered', 'rendered',
'rightslevel', 'rightslevel',
'size', 'size',
'source',
'streams', 'streams',
'title', 'title',
'videoRatio' 'videoRatio'
@ -2499,9 +2456,6 @@ pandora.hasPlacesLayer = function() {
}); });
}; };
pandora.hasView = function(id) {
return !!(Ox.getObjectById(pandora.site.itemViews, id) || Ox.getObjectById(pandora.site.listViews, id))
};
pandora.isClipView = function(view, item) { pandora.isClipView = function(view, item) {
if (pandora.user.ui.section == 'items') { if (pandora.user.ui.section == 'items') {
@ -2647,13 +2601,6 @@ pandora.openURL = function(url) {
} }
}; };
pandora.safeDocumentName = function(name) {
['?', '#', '%'].forEach(function(c) {
name = name.replace(c, '_');
})
return name;
};
pandora.saveURL = function(url, name) { pandora.saveURL = function(url, name) {
var link = document.createElement('a'); var link = document.createElement('a');
if (typeof link.download === 'string') { if (typeof link.download === 'string') {
@ -2713,11 +2660,7 @@ pandora.reloadList = function() {
Ox.Log('', 'reloadList') Ox.Log('', 'reloadList')
var listData = pandora.getListData(); var listData = pandora.getListData();
Ox.Request.clearCache(); // fixme: remove Ox.Request.clearCache(); // fixme: remove
if (pandora.user.ui.section == 'documents' && pandora.$ui.documentFilters) { if (pandora.$ui.filters) {
pandora.$ui.documentFilters.forEach(function($filter) {
$filter.reloadList();
});
} else if (pandora.$ui.filters) {
pandora.$ui.filters.forEach(function($filter) { pandora.$ui.filters.forEach(function($filter) {
$filter.reloadList(); $filter.reloadList();
}); });
@ -3033,7 +2976,6 @@ pandora.resizeWindow = function() {
} }
} }
} else if (pandora.user.ui.section == 'documents') { } else if (pandora.user.ui.section == 'documents') {
pandora.resizeFilters(pandora.$ui.documentPanel.width());
if (pandora.user.ui.document) { if (pandora.user.ui.document) {
pandora.$ui.document && pandora.$ui.document.update(); pandora.$ui.document && pandora.$ui.document.update();
} else { } else {
@ -3251,8 +3193,9 @@ pandora.updateStatus = function(item) {
return ui.item == item && [ return ui.item == item && [
'info', 'player', 'editor', 'timeline' 'info', 'player', 'editor', 'timeline'
].indexOf(ui.itemView) > -1 && !( ].indexOf(ui.itemView) > -1 && !(
pandora.$ui.addItemDialog // fixme: still wrong
&& pandora.$ui.addItemDialog.is('::visible') pandora.$ui.uploadVideoDialog
&& pandora.$ui.uploadVideoDialog.is('::visible')
); );
} }
}; };

View file

@ -1,4 +1,4 @@
<!DOCTYPE html> <!DOCTYPE html>
<!-- <!--
Copyright 2012 Mozilla Foundation Copyright 2012 Mozilla Foundation
@ -43,6 +43,8 @@ See https://github.com/adobe-type-tools/cmap-resources
<script src="/static/pdf.js/debugger.js"></script>
<script src="/static/pdf.js/embeds.js"></script> <script src="/static/pdf.js/embeds.js"></script>
<script src="/static/pdf.js/viewer.js"></script> <script src="/static/pdf.js/viewer.js"></script>
<link rel="stylesheet" type="text/css" href="/static/pdf.js/css/videopdf.css"/> <link rel="stylesheet" type="text/css" href="/static/pdf.js/css/videopdf.css"/>

View file

@ -1,4 +1,4 @@
#!/usr/bin/env python3 #!/usr/bin/env python
from __future__ import print_function from __future__ import print_function
import json import json
import os import os
@ -127,9 +127,6 @@ def get_branch(path=None):
if __name__ == "__main__": if __name__ == "__main__":
if os.stat(__file__).st_uid != os.getuid() or os.getuid() == 0:
print('you must run update.py as the pandora user')
sys.exit(1)
base = os.path.normpath(os.path.abspath(os.path.dirname(__file__))) base = os.path.normpath(os.path.abspath(os.path.dirname(__file__)))
os.chdir(base) os.chdir(base)
activate_venv(base) activate_venv(base)
@ -335,7 +332,6 @@ if __name__ == "__main__":
'-- Model missing for table: djcelery_intervalschedule\n', '-- Model missing for table: djcelery_intervalschedule\n',
'-- Model missing for table: djcelery_workerstate\n', '-- Model missing for table: djcelery_workerstate\n',
'-- Model missing for table: djcelery_taskstate\n', '-- Model missing for table: djcelery_taskstate\n',
'-- Model missing for table: south_migrationhistory\n',
'-- Model missing for table: cache\n', '-- Model missing for table: cache\n',
]: ]:
if row in diff: if row in diff:

View file

@ -4,7 +4,7 @@
# Installing pan.do/ra inside LXC # Installing pan.do/ra inside LXC
1) Install lxc on the host (Ubuntu 18.04 or later): 1) Install lxc on the host (Ubuntu 16.04 or later):
sudo apt-get install lxc sudo apt-get install lxc
@ -15,7 +15,7 @@
2) Create a new container, use different names if installing multiple instances: 2) Create a new container, use different names if installing multiple instances:
sudo lxc-create -n pandora -t ubuntu-cloud -- -r bionic sudo lxc-create -n pandora -t ubuntu -- -r xenial
or or
@ -28,12 +28,12 @@
4) Attach to container and install pan.do/ra 4) Attach to container and install pan.do/ra
sudo lxc-attach -n pandora --clear-env sudo lxc-attach -n pandora --clear-env
apt-get update -qq && apt-get upgrade -y
apt-get -y install curl ca-certificates
sed -i s/ubuntu/pandora/g /etc/passwd /etc/shadow /etc/group sed -i s/ubuntu/pandora/g /etc/passwd /etc/shadow /etc/group
mv /home/ubuntu /home/pandora mv /home/ubuntu /home/pandora
echo "pandora:pandora" | chpasswd echo "pandora:pandora" | chpasswd
echo PasswordAuthentication no >> /etc/ssh/sshd_config echo PasswordAuthentication no >> /etc/ssh/sshd_config
apt-get update -qq && apt-get upgrade -y
apt-get -y install curl ca-certificates
locale-gen en_US.UTF-8 locale-gen en_US.UTF-8
update-locale LANG=en_US.UTF-8 update-locale LANG=en_US.UTF-8
export LANG=en_US.UTF-8 export LANG=en_US.UTF-8

View file

@ -5,11 +5,11 @@ BASE=`pwd`
VERSION=`cd ..;git rev-list HEAD --count` VERSION=`cd ..;git rev-list HEAD --count`
TARGET=${BASE}/pandora-r${VERSION}.vdi TARGET=${BASE}/pandora-r${VERSION}.vdi
img=bionic-server-cloudimg-amd64.img img=xenial-server-cloudimg-amd64-disk1.img
if [ ! -e $img ]; then if [ ! -e $img ]; then
echo downloading $img echo downloading $img
curl -s -O https://cloud-images.ubuntu.com/bionic/current/$img curl -s -O https://cloud-images.ubuntu.com/xenial/current/$img
fi fi
echo preparing ${TARGET}.img echo preparing ${TARGET}.img
cp -a $img ${TARGET}.img cp -a $img ${TARGET}.img
@ -19,7 +19,7 @@ qemu-img resize ${TARGET}.img +998G
echo boot image and install pandora echo boot image and install pandora
kvm -m 1024 \ kvm -m 1024 \
-smp 2 \ -smp 4 \
-cdrom seed.img \ -cdrom seed.img \
-device e1000,netdev=user.0 \ -device e1000,netdev=user.0 \
-netdev user,id=user.0,hostfwd=tcp::5555-:22,hostfwd=tcp::2620-:80 \ -netdev user,id=user.0,hostfwd=tcp::5555-:22,hostfwd=tcp::2620-:80 \

View file

@ -1,8 +0,0 @@
#!/bin/sh
curl -sL https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" > /etc/apt/sources.list.d/elasticsearch.list
apt-get update -qq
apt-get -y install elasticsearch
systemctl enable elasticsearch.service
systemctl start elasticsearch.service
#curl -X GET "http://localhost:9200/?pretty"

View file

@ -91,7 +91,6 @@ apt-get install -y \
python3-lxml \ python3-lxml \
python3-html5lib \ python3-html5lib \
python3-ox \ python3-ox \
python3-elasticsearch \
oxframe \ oxframe \
ffmpeg \ ffmpeg \
mkvtoolnix \ mkvtoolnix \
@ -99,8 +98,6 @@ apt-get install -y \
imagemagick \ imagemagick \
poppler-utils \ poppler-utils \
ipython3 \ ipython3 \
tesseract-ocr \
tesseract-ocr-eng \
postfix \ postfix \
postgresql-client $EXTRA postgresql-client $EXTRA
@ -129,7 +126,6 @@ fi
git clone https://git.0x2620.org/pandora.git /srv/pandora git clone https://git.0x2620.org/pandora.git /srv/pandora
cd /srv/pandora cd /srv/pandora
git checkout $BRANCH git checkout $BRANCH
chown -R $PANDORA:$PANDORA /srv/pandora
./ctl init ./ctl init
# create config.jsonc from templates in git # create config.jsonc from templates in git