Compare commits
5 commits
70e66314a3
...
34fa9e9262
| Author | SHA1 | Date | |
|---|---|---|---|
| 34fa9e9262 | |||
| b19ba24dba | |||
| f99b48b746 | |||
| df4410517a | |||
| 781e2b06ec |
6 changed files with 115 additions and 6 deletions
|
|
@ -1185,7 +1185,7 @@ examples (config.SITENAME.jsonc) that are part of this pan.do/ra distribution.
|
|||
"edits": [],
|
||||
"lists": []
|
||||
},
|
||||
"icons": "posters",
|
||||
"icons": "frames",
|
||||
"infoIconSize": 256,
|
||||
"item": "",
|
||||
"itemFind": "",
|
||||
|
|
|
|||
|
|
@ -105,6 +105,13 @@ if os.path.exists('__init__.py'):
|
|||
new_apps = apps.strip() + ',\n"%s"\n' % name
|
||||
local_settings = local_settings.replace(apps, new_apps)
|
||||
local_settings_changed = True
|
||||
if 'LOCAL_URLPATTERNS' not in local_settings:
|
||||
local_settings += '''
|
||||
LOCAL_URLPATTERNS = [
|
||||
[r'^(?P<id>[A-Z0-9].*)/source(?P<position>[\d\.]+)\.(?P<format>.*)$', 'p_for_power.views.source_frame'],
|
||||
]
|
||||
'''
|
||||
local_settings_changed = True
|
||||
if local_settings_changed:
|
||||
with open(local_settings_py, 'w') as fd:
|
||||
fd.write(local_settings)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ class Command(BaseCommand):
|
|||
parser.add_argument('--duration', action='store', dest='duration', default="3600", help='target duration of all fragments in seconds')
|
||||
parser.add_argument('--offset', action='store', dest='offset', default="1024", help='inital offset in pi')
|
||||
parser.add_argument('--no-video', action='store_true', dest='no_video', default=False, help='don\'t render video')
|
||||
parser.add_argument('--chapter', action='store', dest='chapter', default=None, help='chapter')
|
||||
parser.add_argument('--single-file', action='store_true', dest='single_file', default=False, help='render to single video')
|
||||
parser.add_argument('--keep-audio', action='store_true', dest='keep_audio', default=False, help='keep independent audio tracks')
|
||||
parser.add_argument('--stereo-downmix', action='store_true', dest='stereo_downmix', default=False, help='stereo downmix')
|
||||
|
|
|
|||
13
render.py
13
render.py
|
|
@ -147,16 +147,17 @@ def compose(clips, target=150, base=1024, voice_over=None, options=None):
|
|||
clips = all_clips.copy()
|
||||
if length + clip['duration'] > target and length >= vo_min:
|
||||
break
|
||||
print('%06.3f %06.3f' % (length, clip['duration']), os.path.basename(clip['original']))
|
||||
length += int(clip['duration'] * fps) / fps
|
||||
|
||||
# 50/50 original or ai
|
||||
src = clip['original']
|
||||
audio = clip['original']
|
||||
# select ai...
|
||||
# select ai if we have one
|
||||
if 'ai' in clip:
|
||||
if chance(seq, 0.5):
|
||||
src = random_choice(seq, clip['ai'].values(), False)
|
||||
if True or chance(seq, 0.5):
|
||||
src = random_choice(seq, list(clip['ai'].values()), False)
|
||||
|
||||
print('%07.3f %07.3f' % (length, clip['duration']), src.split('/')[-2], os.path.basename(clip['original']))
|
||||
|
||||
scene['front']['V2'].append({
|
||||
'duration': clip['duration'],
|
||||
|
|
@ -348,6 +349,8 @@ def render_all(options):
|
|||
for fragment in fragments:
|
||||
fragment_base += 1
|
||||
fragment_id = int(fragment['name'].split(' ')[0])
|
||||
if options["chapter"] and int(options["chapter"]) != fragment_id:
|
||||
continue
|
||||
name = fragment['name'].replace(' ', '_')
|
||||
if fragment_id < 10:
|
||||
name = '0' + name
|
||||
|
|
@ -404,7 +407,7 @@ def render_all(options):
|
|||
cmd += ['vn=1']
|
||||
else:
|
||||
cmd += ['an=1']
|
||||
cmd += ['vcodec=libx264', 'x264opts=keyint=1', 'crf=15']
|
||||
#cmd += ['vcodec=libx264', 'x264opts=keyint=1', 'crf=15']
|
||||
subprocess.call(cmd)
|
||||
if ext == '.wav' and timeline.endswith('audio.kdenlive'):
|
||||
cmd = [
|
||||
|
|
|
|||
|
|
@ -289,6 +289,17 @@ pandora.ui.infoView = function(data, isMixed) {
|
|||
.appendTo($text);
|
||||
}
|
||||
|
||||
if (data.type?.join('').includes('ai:')) {
|
||||
$('<div>').addClass('ai-preview').appendTo($text);
|
||||
}
|
||||
if (data.type?.includes('original')) {
|
||||
|
||||
$('<a>').attr({
|
||||
href: 'https://power-video.rmozone.com/#ox/' + data.id,
|
||||
target: '_blank'
|
||||
}).html('Open in AI Power Video').appendTo($text);
|
||||
}
|
||||
|
||||
|
||||
// Duration, Aspect Ratio --------------------------------------------------
|
||||
if (!isMultiple) {
|
||||
|
|
@ -648,6 +659,7 @@ pandora.ui.infoView = function(data, isMixed) {
|
|||
};
|
||||
$element.appendTo($text);
|
||||
pandora.api.find(request, function(response) {
|
||||
let original;
|
||||
response.data.items.forEach(item => {
|
||||
if (item.id != data.id) {
|
||||
var type = item.type ? item.type[0] : 'Unknown'
|
||||
|
|
@ -657,10 +669,44 @@ pandora.ui.infoView = function(data, isMixed) {
|
|||
$element.append(
|
||||
` <a href="/${item.id}/info">${type}</a>`
|
||||
)
|
||||
if (type == 'original') {
|
||||
original = item.id
|
||||
}
|
||||
}
|
||||
})
|
||||
$element.append(`[<a href="/grid/title/title=${pandora.escapeQueryValue(title)}">all</a>]`)
|
||||
pandora.createLinks($element)
|
||||
if (data.type?.join('').includes('ai:') && original) {
|
||||
const preview = $text[0].querySelector('.ai-preview')
|
||||
const src_ai = '480p.mp4'
|
||||
const src = `/${original}/480p.mp4`
|
||||
preview.innerHTML = `
|
||||
<video src="${src}" controls loop></video>
|
||||
<video src="${src_ai}" loop></video>
|
||||
<style>
|
||||
.ai-preview video {
|
||||
width: 33%;
|
||||
}
|
||||
</style>
|
||||
`
|
||||
preview.querySelectorAll('video').forEach(video => {
|
||||
video.addEventListener('play', event => {
|
||||
preview.querySelectorAll('video').forEach(v => {
|
||||
if (v != video) {
|
||||
v.currentTime = video.currentTime
|
||||
v.play()
|
||||
}
|
||||
})
|
||||
})
|
||||
video.addEventListener('pause', event => {
|
||||
preview.querySelectorAll('video').forEach(v => {
|
||||
if (v != video) {
|
||||
v.pause()
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
52
views.py
Normal file
52
views.py
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import os
|
||||
import ox
|
||||
|
||||
from django.conf import settings
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
import requests
|
||||
|
||||
from oxdjango.http import HttpFileResponse
|
||||
from item import models
|
||||
from archive import extract
|
||||
|
||||
def extract_source_frame(self, position, format):
|
||||
offset = 0
|
||||
streams = self.streams()
|
||||
for stream in streams:
|
||||
if stream.duration + offset < position:
|
||||
offset += stream.duration
|
||||
else:
|
||||
if not stream.file.is_video or not stream.file.info.get('video'):
|
||||
return None
|
||||
position = position - offset
|
||||
path = None
|
||||
if stream.file.data:
|
||||
height = stream.file.info['video'][0]['height']
|
||||
video_path = stream.file.data.path
|
||||
path = os.path.join(settings.MEDIA_ROOT, stream.path(),
|
||||
'source-frames', "%dp" % height, "%s.%s" % (position, format))
|
||||
if not os.path.exists(path) and stream.media:
|
||||
extract.frame(video_path, path, position, height, info=stream.file.info)
|
||||
if not os.path.exists(path):
|
||||
return None
|
||||
return path
|
||||
|
||||
def source_frame(request, id, position=None, format="jpg"):
|
||||
if format not in ("jpg", "png"):
|
||||
format = "jpg"
|
||||
content_type = "image/jpeg" if format == "jpg" else "image/png"
|
||||
item = get_object_or_404(models.Item, public_id=id)
|
||||
if not item.access(request.user):
|
||||
return HttpResponseForbidden()
|
||||
frame = None
|
||||
if not position:
|
||||
position = item.poster_frame
|
||||
else:
|
||||
position = float(position.replace(',', '.'))
|
||||
|
||||
frame = extract_source_frame(item, position, format)
|
||||
response = HttpFileResponse(frame, content_type=content_type)
|
||||
if request.method == 'OPTIONS':
|
||||
response.allow_access()
|
||||
return response
|
||||
Loading…
Add table
Add a link
Reference in a new issue