Compare commits
No commits in common. "2d25c60606004a02297e521211f3d08e823cd8df" and "0b44b3b66b5d52c6d506b00b650520b21860c1f4" have entirely different histories.
2d25c60606
...
0b44b3b66b
84 changed files with 1237 additions and 1944 deletions
24
ctl
24
ctl
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 /;
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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()
|
|
||||||
match = re.compile('href=[\'"](http.*.mmdb.gz)').findall(index)
|
|
||||||
if match:
|
|
||||||
url = match[0]
|
|
||||||
print('download', url)
|
print('download', url)
|
||||||
ox.net.save_url(url, "%s.gz" % path)
|
ox.net.save_url(url, "%s.gz"%path)
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
os.unlink(path)
|
os.unlink(path)
|
||||||
os.system('gunzip "%s.gz"' % 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:
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
ext = os.path.splitext(video)[1]
|
|
||||||
if dest is None:
|
|
||||||
tmp = tempfile.mkdtemp()
|
tmp = tempfile.mkdtemp()
|
||||||
|
ext = os.path.splitext(video)[1]
|
||||||
choped_video = '%s/tmp%s' % (tmp, ext)
|
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()
|
||||||
if subtitles_f and os.path.exists(subtitles_f):
|
|
||||||
os.unlink(subtitles_f)
|
|
||||||
if dest is None:
|
|
||||||
f = open(choped_video, 'rb')
|
f = open(choped_video, 'rb')
|
||||||
os.unlink(choped_video)
|
os.unlink(choped_video)
|
||||||
|
if subtitles_f and os.path.exists(subtitles_f):
|
||||||
|
os.unlink(subtitles_f)
|
||||||
os.rmdir(tmp)
|
os.rmdir(tmp)
|
||||||
return f
|
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())
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
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)
|
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
|
||||||
|
|
|
||||||
|
|
@ -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']):
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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 = []
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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,13 +518,7 @@ 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:
|
|
||||||
crop = list(map(int, page.split(',')))
|
|
||||||
page = crop[0]
|
|
||||||
crop = crop[1:]
|
|
||||||
else:
|
|
||||||
page = int(page)
|
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)
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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()
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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()
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -165,9 +165,6 @@ 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 = [~x for x in q]
|
|
||||||
else:
|
|
||||||
q = ~q
|
q = ~q
|
||||||
else:
|
else:
|
||||||
q = Q(id=0)
|
q = Q(id=0)
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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 = {}
|
|
||||||
state['part'] = 1
|
|
||||||
state['count'] = 0
|
|
||||||
|
|
||||||
def new_urlset():
|
|
||||||
urlset = ET.Element('urlset')
|
urlset = ET.Element('urlset')
|
||||||
urlset.attrib['xmlns'] = "http://www.sitemaps.org/schemas/sitemap/0.9"
|
urlset.attrib['xmlns'] = "http://www.sitemaps.org/schemas/sitemap/0.9"
|
||||||
urlset.attrib['xmlns:xsi'] = "http://www.w3.org/2001/XMLSchema-instance"
|
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['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"
|
urlset.attrib['xmlns:video'] = "http://www.google.com/schemas/sitemap-video/1.1"
|
||||||
return urlset
|
|
||||||
|
|
||||||
def save_urlset():
|
url = ET.SubElement(urlset, "url")
|
||||||
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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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())
|
|
||||||
|
|
|
||||||
|
|
@ -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,22 +984,14 @@ 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()
|
||||||
|
|
@ -1036,11 +1000,6 @@ def download(request, id, resolution=None, format='webm', part=None):
|
||||||
response['Content-Length'] = os.path.getsize(video.name)
|
response['Content-Length'] = os.path.getsize(video.name)
|
||||||
else:
|
else:
|
||||||
response = HttpFileResponse(r, content_type=content_type)
|
response = HttpFileResponse(r, content_type=content_type)
|
||||||
else:
|
|
||||||
stream = streams[part].get(resolution, format)
|
|
||||||
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():
|
||||||
|
|
|
||||||
|
|
@ -271,7 +271,6 @@ 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'):
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python
|
||||||
import os
|
import os
|
||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
|
|
|
||||||
|
|
@ -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),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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}},
|
||||||
|
|
|
||||||
|
|
@ -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),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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']
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
|
||||||
|
|
@ -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,6 +117,14 @@ pandora.ui.addFilesDialog = function(options) {
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
selectItems.push({
|
||||||
|
id: 'one',
|
||||||
|
title: Ox._(
|
||||||
|
'Create one {0} with multiple parts',
|
||||||
|
[pandora.site.itemName.singular.toLowerCase()]
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
if (options.items.length > 1) {
|
if (options.items.length > 1) {
|
||||||
selectItems.push({
|
selectItems.push({
|
||||||
id: 'multiple',
|
id: 'multiple',
|
||||||
|
|
@ -131,14 +134,6 @@ pandora.ui.addFilesDialog = function(options) {
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
selectItems.push({
|
|
||||||
id: 'one',
|
|
||||||
title: Ox._(
|
|
||||||
'Create one {0} with multiple parts',
|
|
||||||
[pandora.site.itemName.singular.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) {
|
||||||
|
|
|
||||||
|
|
@ -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};
|
||||||
|
|
|
||||||
|
|
@ -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({
|
||||||
|
|
|
||||||
|
|
@ -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() {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,104 @@
|
||||||
'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({
|
||||||
|
|
@ -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
|
||||||
}]});
|
}]});
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
@ -778,32 +875,7 @@ pandora.ui.documentsPanel = function(options) {
|
||||||
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 {
|
||||||
|
|
|
||||||
|
|
@ -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,64 +73,21 @@ 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({
|
|
||||||
size: 16
|
|
||||||
})
|
|
||||||
that.options({content: $screen.start()});
|
|
||||||
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
|
url = '/' + options.item
|
||||||
+ '/' + values.resolution
|
+ '/' + values.resolution
|
||||||
+ 'p.' + values.format
|
+ 'p.' + values.format
|
||||||
+ '?t=' + options['in'] + ',' + options.out;
|
+ '?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 {
|
|
||||||
if (values.format == 'source') {
|
|
||||||
url = '/' + options.item
|
|
||||||
+ '/download/source/'
|
|
||||||
+ (values.part ? values.part : '')
|
|
||||||
} else {
|
} else {
|
||||||
url = '/' + options.item
|
url = '/' + options.item
|
||||||
+ '/download/' + values.resolution
|
+ '/download/' + values.resolution
|
||||||
+ 'p'
|
+ 'p.' + values.format
|
||||||
+ (values.part ? values.part : '')
|
|
||||||
+ '.' + values.format
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (url) {
|
|
||||||
that.close();
|
|
||||||
document.location.href = url
|
document.location.href = url
|
||||||
}
|
}
|
||||||
}
|
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
closeButton: true,
|
closeButton: true,
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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({
|
|
||||||
findDocuments: {
|
|
||||||
conditions: [{key: 'collection', value: id, operator: '=='}],
|
|
||||||
operator: '&'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
pandora.UI.set({
|
pandora.UI.set({
|
||||||
find: {
|
find: {
|
||||||
conditions: [{key: 'list', value: id, operator: '=='}],
|
conditions: [{key: 'list', value: id, operator: '=='}],
|
||||||
operator: '&'
|
operator: '&'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
Ox.Request.clearCache(); // fixme: remove
|
Ox.Request.clearCache(); // fixme: remove
|
||||||
$list.bindEventOnce({
|
$list.bindEventOnce({
|
||||||
load: function(data) {
|
load: function(data) {
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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() {
|
||||||
|
|
|
||||||
|
|
@ -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'
|
||||||
|
|
|
||||||
|
|
@ -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(' — ');
|
]).join(' — ');
|
||||||
|
|
|
||||||
|
|
@ -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');
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
@ -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({
|
||||||
|
|
|
||||||
|
|
@ -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({
|
|
||||||
documentFilters: pandora.site.user.ui.documentFilters
|
|
||||||
});
|
|
||||||
pandora.$ui.documentContentPanel.replaceElement(0, pandora.$ui.documentBrowser = pandora.ui.documentBrowser());
|
|
||||||
} else {
|
|
||||||
pandora.UI.set({
|
pandora.UI.set({
|
||||||
filters: pandora.site.user.ui.filters
|
filters: pandora.site.user.ui.filters
|
||||||
});
|
});
|
||||||
pandora.$ui.contentPanel.replaceElement(0, pandora.$ui.browser = pandora.ui.browser());
|
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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,40 +23,9 @@ 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) {
|
|
||||||
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());
|
that.replaceElement(1, pandora.$ui.documentPanel = pandora.ui.documentPanel());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
pandora_document: function(data) {
|
pandora_document: function(data) {
|
||||||
if (!data.value || !data.previousValue) {
|
if (!data.value || !data.previousValue) {
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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]+/);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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'},
|
||||||
|
|
|
||||||
|
|
@ -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});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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',
|
||||||
|
|
|
||||||
|
|
@ -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({
|
||||||
|
|
|
||||||
|
|
@ -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,17 +73,12 @@ 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
|
|
||||||
if (Ox.contains(oshashes, oshash)) {
|
|
||||||
callback();
|
|
||||||
} else {
|
|
||||||
oshashes.push(oshash)
|
|
||||||
pandora.api.findDocuments({
|
pandora.api.findDocuments({
|
||||||
keys: ['id', 'user', 'title', 'extension'],
|
keys: ['id', 'user', 'title', 'extension'],
|
||||||
query: {
|
query: {
|
||||||
|
|
@ -102,25 +95,7 @@ pandora.ui.uploadDocumentDialog = function(options, callback) {
|
||||||
if (result.data.items.length) {
|
if (result.data.items.length) {
|
||||||
var id = result.data.items[0].title + '.'
|
var id = result.data.items[0].title + '.'
|
||||||
+ result.data.items[0].extension;
|
+ result.data.items[0].extension;
|
||||||
existingFiles.push({
|
valid && errorDialog(
|
||||||
id: id,
|
|
||||||
filename: filename
|
|
||||||
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
uploadFiles.push(file)
|
|
||||||
}
|
|
||||||
callback();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} ,function() {
|
|
||||||
if (uploadFiles.length) {
|
|
||||||
$uploadDialog.open();
|
|
||||||
} else if (existingFiles.length) {
|
|
||||||
var filename = existingFiles[0].filename
|
|
||||||
var id = existingFiles[0].id
|
|
||||||
errorDialog(
|
|
||||||
filename == id
|
filename == id
|
||||||
? Ox._(
|
? Ox._(
|
||||||
'The file "{0}" already exists.',
|
'The file "{0}" already exists.',
|
||||||
|
|
@ -131,7 +106,13 @@ pandora.ui.uploadDocumentDialog = function(options, callback) {
|
||||||
[filename, id]
|
[filename, id]
|
||||||
)
|
)
|
||||||
).open();
|
).open();
|
||||||
|
valid = false;
|
||||||
}
|
}
|
||||||
|
callback();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
} ,function() {
|
||||||
|
valid && $uploadDialog.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});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
599
static/js/uploadVideoDialog.js
Normal file
599
static/js/uploadVideoDialog.js
Normal 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;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
@ -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')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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"/>
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 \
|
||||||
|
|
|
||||||
|
|
@ -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"
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue