add some backend/selectfile ui and api calls
This commit is contained in:
parent
5de2461eb6
commit
74eed215b5
9 changed files with 623 additions and 10 deletions
|
@ -5,7 +5,6 @@
|
|||
|
||||
import os
|
||||
import sys
|
||||
from glob import glob
|
||||
from optparse import OptionParser
|
||||
|
||||
root = os.path.join(os.path.abspath(os.path.dirname(__file__)), '..')
|
||||
|
@ -16,10 +15,10 @@ import pandoralocal
|
|||
|
||||
if __name__ == '__main__':
|
||||
parser = OptionParser()
|
||||
parser.add_option('-c', '--config', dest='config', help='config file', default='config.json')
|
||||
parser.add_option('-d', '--db', dest='db', help='settings db', default=pandoralocal.db_path())
|
||||
(opts, args) = parser.parse_args()
|
||||
|
||||
if None in (opts.config, ):
|
||||
if None in (opts.db, ):
|
||||
parser.print_help()
|
||||
sys.exit()
|
||||
pandoralocal.main(opts.config)
|
||||
pandoralocal.main(opts.db)
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
# encoding: utf-8
|
||||
# vi:si:et:sw=4:sts=4:ts=4
|
||||
import os
|
||||
import sys
|
||||
|
||||
import gobject
|
||||
gobject.threads_init()
|
||||
|
||||
from twisted.internet import glib2reactor
|
||||
glib2reactor.install()
|
||||
|
||||
from twisted.web.server import Site
|
||||
from twisted.internet import reactor
|
||||
|
@ -11,6 +18,15 @@ import api
|
|||
|
||||
from version import __version__
|
||||
|
||||
def db_path():
|
||||
#use something like http://pypi.python.org/pypi/appdirs/?
|
||||
if sys.platform == 'darwin':
|
||||
path = os.path.expanduser('~/Library/Application Support/pandora/local.db')
|
||||
elif sys.platform == 'win32':
|
||||
path = os.path.expanduser('~\\AppData\\pandora\\local.db')
|
||||
else:
|
||||
path = os.path.expanduser('~/.local/share/pandora/local.db')
|
||||
return path
|
||||
|
||||
def main(config):
|
||||
base = os.path.abspath(os.path.dirname(__file__))
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
# encoding: utf-8
|
||||
# vi:si:et:sw=4:sts=4:ts=4
|
||||
import os
|
||||
|
||||
from server import actions, json_response
|
||||
import ui
|
||||
|
||||
def init(backend, site, data):
|
||||
response = {}
|
||||
|
@ -14,3 +17,83 @@ actions.register(echo, cache=False)
|
|||
def site(backend, site, data):
|
||||
return json_response({'site': site})
|
||||
actions.register(site, cache=False)
|
||||
|
||||
def addVolume(backend, site, data):
|
||||
print data
|
||||
path = ui.selectFolder(data)
|
||||
if path:
|
||||
name = os.path.basename(path)
|
||||
volume = backend.add_volume(site, name, path)
|
||||
return json_response(volume)
|
||||
return json_response({})
|
||||
actions.register(addVolume, cache=False)
|
||||
|
||||
def removeVolume(backend, site, data):
|
||||
print data
|
||||
return json_response({})
|
||||
actions.register(removeVolume, cache=False)
|
||||
|
||||
def renameVolume(backend, site, data):
|
||||
volume = backend.rename_volume(site, data['name'], data['newName'])
|
||||
return json_response(volume)
|
||||
actions.register(renameVolume, cache=False)
|
||||
|
||||
def findVolumes(backend, site, data):
|
||||
print site, data
|
||||
response = {}
|
||||
response['items'] = backend.volumes(site)
|
||||
return json_response(response)
|
||||
actions.register(findVolumes, cache=False)
|
||||
|
||||
def findFiles(backend, site, data):
|
||||
'''
|
||||
implements Ox.List api for files
|
||||
keys: path, id, size, volume, item, uploaded
|
||||
'''
|
||||
print site, data
|
||||
response = {}
|
||||
if not 'keys' in data:
|
||||
response['items'] = backend.files(site, keys=['count(*)'])[0]['count(*)']
|
||||
print response
|
||||
else:
|
||||
response['items'] = backend.files(site, keys=data['keys'], limit=data['range'])
|
||||
|
||||
return json_response(response)
|
||||
actions.register(findFiles, cache=False)
|
||||
|
||||
def uploadFile(backend, site, data):
|
||||
'''
|
||||
uploadFile {
|
||||
id:
|
||||
}
|
||||
upload file with provides id
|
||||
'''
|
||||
print data
|
||||
return json_response({})
|
||||
actions.register(uploadFile, cache=False)
|
||||
|
||||
def encodeFile(backend, site, data):
|
||||
path = ui.selectFile(data)
|
||||
oshash = backend.add_file(site, path)
|
||||
url = '/%s/480p.webm' % oshash
|
||||
return json_response({
|
||||
'path': path,
|
||||
'oshash': oshash,
|
||||
'url': url
|
||||
})
|
||||
actions.register(encodeFile, cache=False)
|
||||
|
||||
def cacheVideo(backend, site, data):
|
||||
print data
|
||||
return json_response({})
|
||||
actions.register(encodeFile, cache=False)
|
||||
|
||||
def selectFile(backend, site, data):
|
||||
path = ui.selectFile(data)
|
||||
oshash = backend.add_file(site, path)
|
||||
return json_response({
|
||||
'path': path,
|
||||
'oshash': oshash
|
||||
})
|
||||
actions.register(selectFile, cache=False)
|
||||
|
||||
|
|
|
@ -1,15 +1,209 @@
|
|||
# encoding: utf-8
|
||||
# vi:si:et:sw=4:sts=4:ts=4
|
||||
import json
|
||||
import sqlite3
|
||||
import os
|
||||
|
||||
import ox
|
||||
|
||||
import utils
|
||||
|
||||
|
||||
class Backend:
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
def __init__(self, db):
|
||||
self.db = db
|
||||
|
||||
def get_file(self, site, itemId, filename):
|
||||
conn, c = self._conn()
|
||||
|
||||
c.execute('''CREATE TABLE IF NOT EXISTS setting (key varchar(1024) unique, value text)''')
|
||||
|
||||
if int(self.get('version', 0)) < 1:
|
||||
self.set('version', 1)
|
||||
db = [
|
||||
'''CREATE TABLE IF NOT EXISTS file (
|
||||
path varchar(1024) unique,
|
||||
oshash varchar(16),
|
||||
atime FLOAT,
|
||||
ctime FLOAT,
|
||||
mtime FLOAT,
|
||||
size INT,
|
||||
info TEXT,
|
||||
created INT,
|
||||
modified INT,
|
||||
deleted INT)''',
|
||||
'''CREATE INDEX IF NOT EXISTS path_idx ON file (path)''',
|
||||
'''CREATE INDEX IF NOT EXISTS oshash_idx ON file (oshash)''',
|
||||
]
|
||||
for i in db:
|
||||
c.execute(i)
|
||||
conn.commit()
|
||||
if int(self.get('version', 0)) < 2:
|
||||
self.set('version', 2)
|
||||
db = [
|
||||
'''CREATE TABLE IF NOT EXISTS encode (
|
||||
oshash varchar(16),
|
||||
site varchar(255))''',
|
||||
'''CREATE INDEX IF NOT EXISTS upload_site_idx ON encode (site)''',
|
||||
]
|
||||
for i in db:
|
||||
c.execute(i)
|
||||
conn.commit()
|
||||
if int(self.get('version', 0)) < 3:
|
||||
self.set('version', 3)
|
||||
db = [
|
||||
'''CREATE TABLE IF NOT EXISTS volume (
|
||||
name varchar(1024) unique,
|
||||
path text,
|
||||
site varchar(255))''',
|
||||
'''CREATE TABLE IF NOT EXISTS part (
|
||||
id varchar(1024),
|
||||
part int,
|
||||
oshash varchar(16),
|
||||
site varchar(255))''',
|
||||
]
|
||||
for i in db:
|
||||
c.execute(i)
|
||||
conn.commit()
|
||||
|
||||
self.media_cache = self.get('media_cache')
|
||||
if not self.media_cache:
|
||||
self.media_cache = os.path.join(os.path.dirname(self.db), 'media')
|
||||
self.set('media_cache', self.media_cache)
|
||||
|
||||
def _conn(self):
|
||||
if not os.path.exists(os.path.dirname(self.db)):
|
||||
os.makedirs(os.path.dirname(self.db))
|
||||
conn = sqlite3.connect(self.db, timeout=10)
|
||||
conn.text_factory = sqlite3.OptimizedUnicode
|
||||
return conn, conn.cursor()
|
||||
|
||||
def get(self, key, default=None):
|
||||
conn, c = self._conn()
|
||||
c.execute('SELECT value FROM setting WHERE key = ?', (key, ))
|
||||
for row in c:
|
||||
return row[0]
|
||||
return default
|
||||
|
||||
def set(self, key, value):
|
||||
conn, c = self._conn()
|
||||
c.execute(u'INSERT OR REPLACE INTO setting VALUES (?, ?)', (key, str(value)))
|
||||
conn.commit()
|
||||
|
||||
def info(self, oshash):
|
||||
conn, c = self._conn()
|
||||
c.execute('SELECT info FROM file WHERE oshash = ?', (oshash, ))
|
||||
for row in c:
|
||||
return json.loads(row[0])
|
||||
return None
|
||||
|
||||
def path(self, oshash):
|
||||
conn, c = self._conn()
|
||||
c.execute('SELECT path FROM file WHERE oshash = ?', (oshash, ))
|
||||
paths = []
|
||||
for row in c:
|
||||
paths.append(row[0])
|
||||
return paths
|
||||
|
||||
def cache_path(self, oshash, profile):
|
||||
return os.path.join(self.media_cache, os.path.join(*utils.hash_prefix(oshash)), profile)
|
||||
|
||||
def volumes(self, site):
|
||||
conn, c = self._conn()
|
||||
c.execute('SELECT name, path FROM volume WHERE site= ?', (site, ))
|
||||
volumes = []
|
||||
for r in c:
|
||||
volumes.append({'name': r[0], 'path': r[1]})
|
||||
return volumes
|
||||
|
||||
def add_volume(self, site, name, path):
|
||||
volumes = self.volumes(site)
|
||||
exists = filter(lambda v: v['path'] == path, volumes)
|
||||
if exists:
|
||||
return exists[0]
|
||||
_name = name
|
||||
n = 2
|
||||
while filter(lambda v: v['name'] == _name, volumes):
|
||||
_name = "%s %d" % (name, n)
|
||||
name = _name
|
||||
conn, c = self._conn()
|
||||
c.execute('INSERT INTO volume (site, name, path) VALUES (?, ?, ?)', (site, name, path))
|
||||
conn.commit()
|
||||
return {
|
||||
'name': name, 'path': path
|
||||
}
|
||||
|
||||
def rename_volume(self, site, name, new_name):
|
||||
volumes = self.volumes(site)
|
||||
_name = new_name
|
||||
n = 2
|
||||
while filter(lambda v: v['name'] == _name, volumes):
|
||||
_name = "%s %d" % (new_name, n)
|
||||
new_name = _name
|
||||
conn, c = self._conn()
|
||||
c.execute('UPDATE volume SET name = ? WHERE name = ? AND site = ?', (new_name, name, site))
|
||||
conn.commit()
|
||||
return {
|
||||
'name': new_name,
|
||||
}
|
||||
|
||||
def files(self, site, keys=[], order='path', limit=None):
|
||||
conn, c = self._conn()
|
||||
files = []
|
||||
sql = 'SELECT %s FROM file ORDER BY %s '% (','.join(keys), order)
|
||||
if limit:
|
||||
sql += ' LIMIT %d, %d' %(limit[0], limit[1]-limit[0])
|
||||
print sql
|
||||
c.execute(sql)
|
||||
for r in c:
|
||||
f = {}
|
||||
for i in range(len(r)):
|
||||
f[keys[i]] = r[i]
|
||||
files.append(f)
|
||||
return files
|
||||
|
||||
def add_file(self, site, filename):
|
||||
info = utils.avinfo(filename)
|
||||
return info['oshash']
|
||||
|
||||
def cache_file(self, site, url, itemId, filename):
|
||||
conn, c = self._conn()
|
||||
filename, ext = filename.split('.')
|
||||
resolution, part = filename.split('p')
|
||||
print site, itemId, resolution, part, ext
|
||||
c.execute('SELECT oshash FROM part WHERE site = ? AND id = ? AND part =?',
|
||||
(site, itemId, part))
|
||||
path = ''
|
||||
if resolution == '480' and ext == 'webm':
|
||||
path = '/home/j/.ox/media/44/c4/b1/11a888e96a/480p.webm'
|
||||
for r in c:
|
||||
oshash = r[0]
|
||||
path = self.cache_path(oshash, '480p.webm')
|
||||
break
|
||||
|
||||
if path and not os.path.exists(path):
|
||||
path = ''
|
||||
if not path:
|
||||
#FIXME: oshash get oshash for part
|
||||
path = self.cache_path(oshash, '480p.webm')
|
||||
#FIXME: need to add cookies
|
||||
ox.net.saveUrl(url, path)
|
||||
t = (oshash, part, itemId, site)
|
||||
c.execute('INSERT INTO part (oshash, part, id, site) VALUES (?, ?, ?, ?)', t)
|
||||
return path
|
||||
|
||||
def get_file(self, site, itemId, filename):
|
||||
conn, c = self._conn()
|
||||
filename, ext = filename.split('.')
|
||||
resolution, part = filename.split('p')
|
||||
print site, itemId, resolution, part, ext
|
||||
if len(itemId) == 16 and itemId.islower() and itemId.isalnum():
|
||||
path = self.cache_path(itemId, '480p.webm')
|
||||
else:
|
||||
c.execute('SELECT oshash FROM part WHERE site = ? AND id = ? AND part =?',
|
||||
(site, itemId, part))
|
||||
path = ''
|
||||
for r in c:
|
||||
path = self.cache_path(r[0], '480p.webm')
|
||||
break
|
||||
|
||||
if path and not os.path.exists(path):
|
||||
path = ''
|
||||
return path
|
||||
|
|
|
@ -110,7 +110,7 @@ class ApiActions(dict):
|
|||
result = self[action](backend, site, data)
|
||||
else:
|
||||
result = json_response(status=404, text='not found')
|
||||
print result
|
||||
#print result
|
||||
return json.dumps(result)
|
||||
|
||||
actions = ApiActions()
|
||||
|
|
12
pandoralocal/static/files.html
Normal file
12
pandoralocal/static/files.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>pandoralocal</title>
|
||||
<link rel="shortcut icon" type="image/png" href="/png/icon16.png"/>
|
||||
<script type="text/javascript" src="/oxjs/dev/Ox.js"></script>
|
||||
<script type="text/javascript" src="/js/files.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
189
pandoralocal/static/js/files.js
Executable file
189
pandoralocal/static/js/files.js
Executable file
|
@ -0,0 +1,189 @@
|
|||
/***
|
||||
PandoraLocal
|
||||
***/
|
||||
Ox.load('UI', {
|
||||
hideScreen: false,
|
||||
showScreen: true,
|
||||
theme: 'classic'
|
||||
}, function() {
|
||||
|
||||
window.pandora = new Ox.App({
|
||||
apiURL: '/api/',
|
||||
init: 'init',
|
||||
}).bindEvent('load', function(data) {
|
||||
pandora.site = {
|
||||
};
|
||||
Ox.UI.hideLoadingScreen();
|
||||
|
||||
pandora.$ui = {
|
||||
body: $('body'),
|
||||
document: $(document),
|
||||
window: $(window)
|
||||
.bind({
|
||||
resize: function() {
|
||||
//pandora.resizeWindow();
|
||||
},
|
||||
unload: function() {
|
||||
//pandora.nloadWindow();
|
||||
}
|
||||
})
|
||||
};
|
||||
pandora.$ui.volumes = constructList();
|
||||
pandora.$ui.files = constructFiles();
|
||||
|
||||
var $left = new Ox.SplitPanel({
|
||||
elements: [
|
||||
{
|
||||
element: new Ox.Element().append(new Ox.Element()
|
||||
.html('Pandoralocal').css({
|
||||
'padding': '4px',
|
||||
})).css({
|
||||
'background-color': '#ddd',
|
||||
'font-weight': 'bold',
|
||||
}),
|
||||
size: 24
|
||||
},
|
||||
{
|
||||
element: pandora.$ui.volumes
|
||||
}
|
||||
],
|
||||
orientation: 'vertical'
|
||||
});
|
||||
var $main = new Ox.SplitPanel({
|
||||
elements: [
|
||||
{
|
||||
element: $left,
|
||||
size: 160
|
||||
},
|
||||
{
|
||||
element: pandora.$ui.files,
|
||||
}
|
||||
],
|
||||
orientation: 'horizontal'
|
||||
});
|
||||
|
||||
$main.appendTo(pandora.$ui.body);
|
||||
});
|
||||
|
||||
function constructFiles() {
|
||||
return new Ox.TextList({
|
||||
columns: [
|
||||
{
|
||||
align: "left",
|
||||
id: "path",
|
||||
operator: "+",
|
||||
title: "Name",
|
||||
unique: true,
|
||||
visible: true,
|
||||
width: 860
|
||||
},
|
||||
{
|
||||
align: "left",
|
||||
id: "size",
|
||||
operator: "-",
|
||||
title: "Size",
|
||||
visible: true,
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
align: "left",
|
||||
id: "oshash",
|
||||
operator: "-",
|
||||
title: "ID",
|
||||
visible: true,
|
||||
width: 120
|
||||
},
|
||||
],
|
||||
columnsMovable: true,
|
||||
columnsRemovable: true,
|
||||
items: pandora.api.findFiles,
|
||||
scrollbarVisible: true,
|
||||
sort: [
|
||||
{
|
||||
key: "path",
|
||||
operator: "+"
|
||||
}
|
||||
]
|
||||
}).bindEvent({
|
||||
select: function(data) {
|
||||
var info = $('<div>').addClass('OxSelectable'),
|
||||
hash = '#';
|
||||
if(data.ids.length) {
|
||||
data.ids.forEach(function(id) {
|
||||
info.append($("<h2>").html(id));
|
||||
var $doc =$('<pre>')
|
||||
.html(pandora.actions[id].doc.replace('/\n/<br>\n/g'))
|
||||
.appendTo(info);
|
||||
var $code = $('<code class="python">')
|
||||
.html(pandora.actions[id].code[1].replace('/\n/<br>\n/g'))
|
||||
.hide();
|
||||
var f = pandora.actions[id].code[0];
|
||||
$('<span>')
|
||||
.html(' View Source ('+f+')')
|
||||
.click(function() { $code.toggle();})
|
||||
.appendTo(info);
|
||||
$('<pre>').append($code).appendTo(info);
|
||||
hljs.highlightBlock($code[0], ' ');
|
||||
|
||||
hash += id + ',';
|
||||
});
|
||||
} else {
|
||||
info.html(pandora.site.default_info);
|
||||
}
|
||||
|
||||
document.location.hash = hash.substring(0, hash.length-1);
|
||||
pandora.$ui.actionInfo.html(info);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function constructList() {
|
||||
return new Ox.TextList({
|
||||
columns: [
|
||||
{
|
||||
align: "left",
|
||||
id: "name",
|
||||
operator: "+",
|
||||
title: "Name",
|
||||
unique: true,
|
||||
visible: true,
|
||||
width: 140
|
||||
},
|
||||
],
|
||||
columnsMovable: false,
|
||||
columnsRemovable: false,
|
||||
id: 'actionList',
|
||||
items: function(data, callback) {
|
||||
function _sort(a, b) {
|
||||
if(a.name > b.name)
|
||||
return 1;
|
||||
else if(a.name == b.name)
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
pandora.api.findVolumes(function(result) {
|
||||
var items = result.data.items;
|
||||
items.sort(_sort);
|
||||
callback({'data': {
|
||||
'items': data.keys ? items : items.length
|
||||
}});
|
||||
});
|
||||
},
|
||||
scrollbarVisible: true,
|
||||
sort: [
|
||||
{
|
||||
key: "name",
|
||||
operator: "+"
|
||||
}
|
||||
]
|
||||
}).bindEvent({
|
||||
select: function(data) {
|
||||
if(data.ids.length) {
|
||||
var volume = data.ids[0];
|
||||
Ox.print("FIXME", volume);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
43
pandoralocal/ui.py
Normal file
43
pandoralocal/ui.py
Normal file
|
@ -0,0 +1,43 @@
|
|||
# encoding: utf-8
|
||||
# vi:si:et:sw=4:sts=4:ts=4
|
||||
import pygtk
|
||||
pygtk.require('2.0')
|
||||
import gtk
|
||||
|
||||
def selectFolder(data):
|
||||
dialog = gtk.FileChooserDialog("Select Folder..",
|
||||
None,
|
||||
gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
|
||||
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
|
||||
gtk.STOCK_OPEN, gtk.RESPONSE_OK))
|
||||
dialog.set_default_response(gtk.RESPONSE_OK)
|
||||
|
||||
response = dialog.run()
|
||||
if response == gtk.RESPONSE_OK:
|
||||
filename = dialog.get_filename()
|
||||
print filename, 'selected'
|
||||
elif response == gtk.RESPONSE_CANCEL:
|
||||
print 'Closed, no files selected'
|
||||
filename = None
|
||||
dialog.destroy()
|
||||
print "done"
|
||||
return filename
|
||||
|
||||
def selectFile(data):
|
||||
dialog = gtk.FileChooserDialog("Select File..",
|
||||
None,
|
||||
gtk.FILE_CHOOSER_ACTION_OPEN,
|
||||
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
|
||||
gtk.STOCK_OPEN, gtk.RESPONSE_OK))
|
||||
dialog.set_default_response(gtk.RESPONSE_OK)
|
||||
|
||||
response = dialog.run()
|
||||
if response == gtk.RESPONSE_OK:
|
||||
filename = dialog.get_filename()
|
||||
print filename, 'selected'
|
||||
elif response == gtk.RESPONSE_CANCEL:
|
||||
print 'Closed, no files selected'
|
||||
filename = None
|
||||
dialog.destroy()
|
||||
print "done"
|
||||
return filename
|
77
pandoralocal/utils.py
Normal file
77
pandoralocal/utils.py
Normal file
|
@ -0,0 +1,77 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# vi:si:et:sw=4:sts=4:ts=4
|
||||
# GPL 2010
|
||||
from __future__ import division, with_statement
|
||||
|
||||
import fractions
|
||||
from glob import glob
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sqlite3
|
||||
import subprocess
|
||||
import sys
|
||||
import shutil
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
import ox
|
||||
|
||||
|
||||
class AspectRatio(fractions.Fraction):
|
||||
def __new__(cls, numerator, denominator=None):
|
||||
if not denominator:
|
||||
ratio = map(int, numerator.split(':'))
|
||||
if len(ratio) == 1: ratio.append(1)
|
||||
numerator = ratio[0]
|
||||
denominator = ratio[1]
|
||||
#if its close enough to the common aspect ratios rather use that
|
||||
if abs(numerator/denominator - 4/3) < 0.03:
|
||||
numerator = 4
|
||||
denominator = 3
|
||||
elif abs(numerator/denominator - 16/9) < 0.02:
|
||||
numerator = 16
|
||||
denominator = 9
|
||||
return super(AspectRatio, cls).__new__(cls, numerator, denominator)
|
||||
|
||||
@property
|
||||
def ratio(self):
|
||||
return "%d:%d" % (self.numerator, self.denominator)
|
||||
|
||||
def avinfo(filename):
|
||||
if os.path.getsize(filename):
|
||||
info = ox.avinfo(filename)
|
||||
if 'video' in info and info['video'] and 'width' in info['video'][0]:
|
||||
if not 'display_aspect_ratio' in info['video'][0]:
|
||||
dar = AspectRatio(info['video'][0]['width'], info['video'][0]['height'])
|
||||
info['video'][0]['display_aspect_ratio'] = dar.ratio
|
||||
del info['path']
|
||||
if os.path.splitext(filename)[-1] in ('.srt', '.sub', '.idx', '.rar') and 'error' in info:
|
||||
del info['error']
|
||||
if 'code' in info and info['code'] == 'badfile':
|
||||
del info['code']
|
||||
return info
|
||||
return {'path': filename, 'size': 0}
|
||||
|
||||
def hash_prefix(h):
|
||||
return [h[:2], h[2:4], h[4:6], h[6:]]
|
||||
|
||||
def run_command(cmd, timeout=25):
|
||||
#print cmd
|
||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
|
||||
while timeout > 0:
|
||||
time.sleep(0.2)
|
||||
timeout -= 0.2
|
||||
if p.poll() != None:
|
||||
return p.returncode
|
||||
if p.poll() == None:
|
||||
os.kill(p.pid, 9)
|
||||
killedpid, stat = os.waitpid(p.pid, os.WNOHANG)
|
||||
return p.returncode
|
||||
|
||||
def video_frame_positions(duration):
|
||||
pos = duration / 2
|
||||
#return [pos/4, pos/2, pos/2+pos/4, pos, pos+pos/2, pos+pos/2+pos/4]
|
||||
return map(int, [pos/2, pos, pos+pos/2])
|
||||
|
Loading…
Reference in a new issue