Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F4261
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
36 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/CHANGELOG b/CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,100 +1,102 @@
@%@~LICENSE:OK~@%@
-saw_060122_5 - [(crickets)] ...double that (player rack ack chaos).
+saw_060122_6 - [(crickets)] ...include replay capture (PRAC).
+
+saw_060122_5 - [(crickets)] ...double that (Player Rack Ack Chaos).
saw_060122_4 - [(crickets)] Include the test drawings as included samples (ack).
saw_060122_3 - [(crickets)] Randomly use ack saves in player rack chaos panel.
saw_060122_2 - [opeth] Bug fix.
saw_060122_1 - [opeth] Ack tool cursors.
saw_053122_2 - [opeth] Ack: all tools basically working (no text or group yet).
saw_053122_1 - [opeth] Fixing ack tooling: color, rect, and ellipse so far.
saw_053022_1 - [kaczynski] Color palette for ack.
saw_052822_1 - [ledzeppelin] More ack work.
saw_052722_3 - [ledzeppelin] Ack copy/paste (requires xclip system utility).
saw_052722_2 - [gojira] Ack save.
saw_052722_1 - [bach] A little bit of cleanup in ack classes.
saw_052622_5 - [bach] Implement ack Rect and Ellipse tools and Delete function.
saw_052622_4 - [bach] Add some color choices to ack.
saw_052622_3 - [bach] Begin to implement tools and objects in ack.
saw_052622_2 - [beethoven] Now have working captions filter in player rack.
saw_052622_1 - [beethoven] Implement keywords and labels in player rack. Add
ability to hide labels globally.
saw_052522_3 - [labyrinth] Improved confirmation/error dialogs.
saw_052522_2 - [labyrinth] Implemented a "list all" function for HotKeys.
saw_052522_1 - [labyrinth] Finished HotKeys panel for player.
saw_052422_1 - [labyrinth] Starting to implement HotKeys panel.
saw_052322_2 - [polog] More work on player rack. Starting to implement ack
(blackboard). Minor UI tweaks such as new status modes and
cursors. Improved escape key handling.
saw_052322_1 - [polog] Starting to implement player rack.
saw_052122_2 - [yelawolf] Remove the UNIQUE constraint on caption start time
because some files are actually marked with multiple captions
occurring at the same time. Add some more deferred downloads.
saw_052122_1 - [yelawolf] Implemented deferred downloads and seeded the database
with some videos. Beginning of a label system for tagging videos.
Channels, videos, and labels added by the user now start at
id=1000 to leave room for seeded entries. Database now enforces
foreign key constraints and patches use transactions. We now
perform a VACUUM operation on the database after patching or
application exit. Player explicitly disables key repeat on key
press. Debug DIP switch has been changed to the plus sign (+).
saw_051622_2 - [nin] Partial implementation of full-screen mode without meta.
saw_051622_1 - [nin] Improve database performance. Moved audio extraction step
to the main video download routine so we no longer have to wait
for audio extraction upon first play.
saw_051522_1 - [nin] Add switch.py to easily start with DIP switches set.
saw_042422_1 - [controldenied] Beginning to get replay captures to play.
saw_042222_1 - [controldenied] Reworking the replay system so we can now
capture keyboard and mouse actions. The command-line options
(dip switches) should now be specified as the first option only
and without spaces.
saw_041222_1 - [theprodigy] Minor changes wrap up this series.
saw_040822_1 - [theprodigy] Just a little bit better.
saw_040722_1 - [theprodigy] Introducing: ImageEngine (imagine)! Add more
command-line options (see README). Status bar now with separate
UIManager (sui). Player NUM-pad key improvements. Get FPS/avg
score readouts with TAB key.
saw_040422_1 - We now have a working sequence/segment editor.
saw_040222_1 - Implemented replay system. Improved player performance.
saw_033022_2 - Fix kush prompt paint (cursor overlap problem).
saw_033022_1 - Migration to the media/fragment system and versioned database.
saw_032622_2 - Rename directories.
saw_032622_1 - First commit.
diff --git a/k/player/ui.py b/k/player/ui.py
--- a/k/player/ui.py
+++ b/k/player/ui.py
@@ -1,729 +1,736 @@
from k.ui import *
import k.db as kdb
import k.storage as media
from k.home import IMAGE
from .core import get_size_to_fit
import random
PRECT = pygame.Rect((WIDTH-PWIDTH, 0), (PWIDTH, HEIGHT))
PRACK = pygame.Rect((140, 370), (PWIDTH-150, HEIGHT-STATUS-380))
CRACK = pygame.Rect((0, 35), (PRACK[2], PRACK[3]-35))
MOD_MAN = 666
class Player(KPanel):
def __init__(self, k, tracker, thumbnail=None, loop=None, jumps=None):
super().__init__(k, relative_rect=PRECT, visible=1)
self.trk = tracker
self.progress = pygame_gui.elements.UIHorizontalSlider(
start_value=0,
value_range=(0, self.trk.frames-1),
manager=k.gui,
container=self.container,
relative_rect=pygame.Rect((0, 315), (PWIDTH, 20)))
self.btn_pp = pygame_gui.elements.UIButton(
text='',
manager=k.gui,
container=self.container,
relative_rect=pygame.Rect((10, 335), (80, -1)))
if hasattr(self, 'save'):
self.btn_save = pygame_gui.elements.UIButton(
text='Save*',
manager=k.gui,
container=self.container,
relative_rect=pygame.Rect((110, 335), (80, -1)))
else:
self.btn_save = None
if hasattr(self, 'clip'):
self.btn_clip = pygame_gui.elements.UIButton(
text='Clip',
manager=k.gui,
container=self.container,
relative_rect=pygame.Rect((200, 335), (60, -1)))
else:
self.btn_clip = None
if hasattr(self, 'seq'):
self.btn_seq = pygame_gui.elements.UIButton(
text='Seq',
manager=k.gui,
container=self.container,
relative_rect=pygame.Rect((260, 335), (50, -1)))
else:
self.btn_seq = None
self.lbl_frame = pygame_gui.elements.UILabel(
text='',
manager=k.gui,
container=self.container,
relative_rect=pygame.Rect((420, 335), (-1, -1)))
if loop:
loop = loop.split(',')
self.loop = True if int(loop[0])==1 else False
self.loop_begin = int(loop[1])
self.loop_end = int(loop[2])
else:
self.loop = False
self.loop_begin = 0
self.loop_end = self.trk.frames - 1
if jumps:
self.jumps = [int(j) for j in jumps.split(',')]
else:
self.jumps = []
for j in range(1, 10):
self.jumps.append(int(j * self.trk.frames / 10))
self.lbl_jumps = []
for j, frame in enumerate(self.jumps):
x = 28+round((PWIDTH-65)*frame/self.trk.frames)
self.lbl_jumps.append(pygame_gui.elements.UILabel(
text=str(j+1),
manager=k.gui,
container=self.container,
relative_rect=pygame.Rect((x, 295), (-1, -1))))
self.loop_bar = pygame.Surface((PWIDTH, 3))
self.draw_loop_bar()
self.thumb_orig = thumbnail
self.thumb = self.thumb_orig
self.btn_chaos = pygame_gui.elements.UIButton(
text='?!',
manager=k.gui,
container=self.container,
relative_rect=pygame.Rect((10, 370), (120, 25)))
self.panel_chaos = Chaos(self)
self.cur_btn = self.btn_chaos
self.cur_btn.disable()
self.cur_panel = self.panel_chaos
self.reset()
def reset(self):
self.progress.disable()
self.progress.set_current_value(0)
self.btn_pp.set_text('PLAY')
self.lbl_frame.set_text(f'[{self.trk.frames}]')
#self.holding = 0
self.holding = [ ]
self.pdrag = False
self.trk.reset()
self.img = None
self.playing = None
def kill(self, replace=False):
self.stop(replace)
self.progress.kill()
self.btn_pp.kill()
if self.btn_save:
self.btn_save.kill()
if self.btn_clip:
self.btn_clip.kill()
if self.btn_seq:
self.btn_seq.kill()
self.lbl_frame.kill()
for lbl_jump in self.lbl_jumps:
lbl_jump.kill()
super().kill()
def play(self):
if self.playing:
return
self.op_start()
self.progress.enable()
self.btn_pp.set_text('PAUSE')
self.trk.play()
self.playing = True
def pause(self):
if not self.playing:
return
self.op_stop()
self.btn_pp.set_text('RESUME')
self.trk.pause()
self.playing = False
def stop(self, replace=False):
if self.playing is None:
return
self.reset()
if not replace:
self.op_stop()
self.k.replay_break()
def seek(self, frame):
if self.playing and self.pdrag \
and frame + self.trk.begin == self.trk.end:
frame -= 1
self.trk.seek(frame + self.trk.begin)
self.update_frame(frame)
if self.playing and not self.pdrag and self.holding:
self.op_seek()
def update_frame(self, frame):
self.progress.set_current_value(frame)
self.lbl_frame.set_text(str(frame))
def tick(self):
if self.playing is not None:
pframe = round(self.progress.get_current_value())
if pframe != self.trk.frame - self.trk.begin:
#print(f'seeking pframe {pframe}')
self.seek(pframe)
elif self.playing and self.pdrag:
self.seek(self.trk.frame - self.trk.begin - 1)
#elif self.holding < 0:
# self.seek(self.flast)
elif self.holding:
if len(self.holding) > 1:
self.seek(self.flast)
for key in self.holding:
self.keydown(key, MOD_MAN)
elif self.holding[0] == pygame.K_KP_PERIOD:
self.seek(self.flast)
elif self.loop and (pframe < self.loop_begin \
or pframe > self.loop_end):
#if self.holding == 0:
if not self.holding:
self.seek(self.loop_begin)
tock = self.trk.tick()
if tock is None:
self.stop()
elif tock is not False:
self.img = tock
self.update_frame(self.trk.frame - self.trk.begin)
self.paint()
def paint(self):
if not self.k.player.big and not self.k.ack:
self.paint_loop_bar()
if self.playing is None:
if self.thumb is not None:
self.paint_thumb()
else:
self.paint_video()
def paint_thumb(self):
self.paint_image(self.thumb, self.thumb.get_width(),
self.thumb.get_height())
def paint_video(self):
self.paint_image(self.img, self.trk.size[0], self.trk.size[1])
def paint_image(self, img, w, h):
if self.k.player.big:
x = int((WIDTH - w) / 2)
y = int((HEIGHT - STATUS - h) / 2)
else:
x = PRECT[0]
y = PRECT[1]
if img is True:
self.trk.res.ie.speed(self.trk.res, self.trk.frame, (x, y), (w, h))
else:
self.k.blit(img, (x, y))
def paint_loop_bar(self):
self.k.blit(self.loop_bar, (PRECT[0], 313))
def draw_loop_bar(self):
self.loop_bar.fill(BLACK)
color = RED if self.loop else GREEN
width = PWIDTH-65
x_begin = round(width * self.loop_begin / self.trk.frames)
x_end = round(width * (1+self.loop_end) / self.trk.frames)
if x_begin > width-3:
x_begin = width-3
if x_end - x_begin < 3:
x_end = x_begin+3
pygame.draw.rect(self.loop_bar, color,
pygame.Rect((32+x_begin, 0), (x_end-x_begin, 3)))
def size_to_fit(self, width, height):
self.trk.size_to_fit(width, height)
bg = pygame.Surface((width, height))
bg.fill((9, 81, 18))
if self.thumb_orig:
size = get_size_to_fit(self.thumb_orig.get_size(), (width, height))
self.thumb = pygame.transform.smoothscale(self.thumb_orig, size)
tw = self.thumb.get_width()
th = self.thumb.get_height()
if tw < width or th < height:
x = int((width - tw) / 2)
y = int((height - th) / 2)
bg.blit(self.thumb, (x, y))
self.thumb = bg
else:
self.thumb = bg
def panel_swap(self, btn, panel):
if btn is self.panel_chaos and self.cur_panel is self.panel_chaos:
self.cur_panel.show()
super().panel_swap(btn, panel, self.cur_panel is not self.panel_chaos)
def click(self, element, target=None):
if element is self.btn_pp:
self.pause() if self.playing else self.play()
elif element is self.btn_save:
self.k.job(self.save)
elif element is self.btn_clip:
self.k.job(self.clip)
elif element is self.btn_seq:
self.k.job(self.seq)
elif element is self.btn_chaos:
self.panel_swap(self.btn_chaos, self.panel_chaos)
else:
return self.cur_panel.click(element, target)
return True
def mouse_down(self, event):
if self.playing is None:
return
if event.button != pygame.BUTTON_LEFT:
return
pos = event.pos
prect = self.progress.get_relative_rect()
srect = self.progress.sliding_button.get_relative_rect()
bw = self.progress.arrow_button_width
if pos[0] > PRECT[0] + prect[0] + bw \
and pos[0] < PRECT[0] + prect[0] + prect[2] - bw \
and pos[1] > PRECT[1] + prect[1] \
and pos[1] < PRECT[1] + prect[1] + prect[3]:
self.pdrag = True
self.op_hold()
if not (pos[0] >= PRECT[0] + srect[0] \
and pos[0] <= PRECT[0] + srect[0] + srect[2]):
x = pos[0] - PRECT[0] - bw
w = prect[2] - 2*bw
f = int((x / w) * self.trk.frames)
self.seek(f)
def mouse_up(self, event):
if event.button != pygame.BUTTON_LEFT:
return
self.pdrag = False
if self.playing:
self.op_seek()
def keydown(self, key, mod):
#print(f'{self.holding} DOWN {key}')
pygame.key.set_repeat(0)
man = False
alt = False
ctrl = False
shift = False
if mod == MOD_MAN:
man = True
else:
alt = mod & pygame.KMOD_ALT
ctrl = mod & pygame.KMOD_CTRL
shift = mod & pygame.KMOD_SHIFT or mod & pygame.KMOD_LSHIFT \
or mod & pygame.KMOD_RSHIFT
nomod = not alt and not ctrl and not shift
if key == pygame.K_KP_ENTER:
if self.playing:
self.pause()
else:
self.play()
elif key == pygame.K_HOME:
self.stop()
self.play()
elif key == pygame.K_END:
self.stop()
elif key == pygame.K_UP:
if not man:
self.keyhold(key)
if self.playing:
if nomod:
self.seek(self.trk.frame + PLAY_SEEK_VERT)
elif ctrl:
self.seek(self.trk.frame + PLAY_SEEK_VERT_CTRL)
elif shift:
self.seek(self.trk.frame + PLAY_SEEK_VERT_SHIFT)
else:
if nomod:
self.seek(self.trk.frame + PAUSE_SEEK_VERT)
elif ctrl:
self.seek(self.trk.frame + PAUSE_SEEK_VERT_CTRL)
elif shift:
self.seek(self.trk.frame + PAUSE_SEEK_VERT_SHIFT)
elif key == pygame.K_DOWN:
if not man:
self.keyhold(key)
if self.playing:
if nomod:
self.seek(self.trk.frame - PLAY_SEEK_VERT)
elif ctrl:
self.seek(self.trk.frame - PLAY_SEEK_VERT_CTRL)
elif shift:
self.seek(self.trk.frame - PLAY_SEEK_VERT_SHIFT)
else:
if nomod:
self.seek(self.trk.frame - PAUSE_SEEK_VERT)
elif ctrl:
self.seek(self.trk.frame - PAUSE_SEEK_VERT_CTRL)
elif shift:
self.seek(self.trk.frame - PAUSE_SEEK_VERT_SHIFT)
elif key == pygame.K_LEFT:
if not man:
self.keyhold(key)
if nomod:
self.seek(self.trk.frame - SEEK_HORIZ)
elif ctrl:
self.seek(self.trk.frame - SEEK_HORIZ_CTRL)
elif shift:
self.seek(self.trk.frame - SEEK_HORIZ_SHIFT)
elif key == pygame.K_RIGHT:
if not man:
self.keyhold(key)
if nomod:
self.seek(self.trk.frame + SEEK_HORIZ)
elif ctrl:
self.seek(self.trk.frame + SEEK_HORIZ_CTRL)
elif shift:
self.seek(self.trk.frame + SEEK_HORIZ_SHIFT)
elif key == pygame.K_KP0:
if nomod:
if not man:
self.keyhold(key, True)
if self.loop:
self.seek(0)
else:
self.seek(self.loop_begin)
elif key >= pygame.K_KP1 and key <= pygame.K_KP9:
if nomod:
if not man:
self.keyhold(key, True)
self.seek(self.jumps[key - pygame.K_KP1])
elif ctrl and not man:
idx = key - pygame.K_KP1
frame = self.trk.frame - self.trk.begin
self.jumps[idx] = frame
self.lbl_jumps[idx].set_relative_position(
(28 + int((PWIDTH-65) * frame / self.trk.frames),
295))
elif key == pygame.K_KP_PERIOD:
if self.playing:
#if self.holding == 0 or self.holding is None:
# self.op_hold()
# self.flast = self.trk.frame
# self.holding = -1 * key
#elif self.holding > 0:
# self.op_hold()
# self.holding = -1 * self.holding
#else:
# #print('how did this happen (key already down)?')
# pass
if man:
self.seek(self.flast)
else:
self.keyhold(key, True)
#self.holding.append(key)
elif key == pygame.K_KP_DIVIDE:
if nomod:
self.loop_begin = self.trk.frame - self.trk.begin
if self.loop_begin > self.loop_end:
self.loop_end = self.trk.end - self.trk.begin
self.draw_loop_bar()
#if self.btn_save:
# self.btn_save.enable()
elif key == pygame.K_KP_MULTIPLY:
if nomod:
self.loop_end = self.trk.frame - self.trk.begin
if self.loop_end < self.loop_begin:
self.loop_begin = 0
self.draw_loop_bar()
#if self.btn_save:
# self.btn_save.enable()
elif key == pygame.K_KP_PLUS:
if nomod:
if not self.loop:
self.loop = True
self.draw_loop_bar()
#if self.btn_save:
# self.btn_save.enable()
#self.keyhold(key)
self.seek(self.loop_begin)
elif key == pygame.K_KP_MINUS:
if nomod:
if self.loop:
self.loop = False
self.draw_loop_bar()
#if self.btn_save:
# self.btn_save.enable()
if not man:
self.keyhold(key)
self.seek(self.loop_end)
def keyup(self, key, mod):
#print(f'{self.holding} UP {key}')
if key == pygame.K_ESCAPE:
self.k.player.killall()
return
#if self.holding < 0:
# self.holding = -1 * self.holding
# if self.holding == key:
# self.holding = 0
# self.op_unhold()
#elif self.holding == 0:
# if key == pygame.K_KP0:
# self.seek(self.loop_begin)
# elif key >= pygame.K_KP1 and key <= pygame.K_KP9:
# self.seek(self.jumps[key - pygame.K_KP1])
#else:
# self.holding = 0
while key in self.holding:
idx = self.holding.index(key)
del self.holding[idx]
if idx > 0:
if self.loop and key == pygame.K_KP_PERIOD:
self.seek(self.flast)
else:
self.keydown(self.holding[idx-1], MOD_MAN)
def keyhold(self, key, magic=False):
#if self.holding < 0:
# self.holding = -1 * key
#elif magic and self.holding == 0:
# self.flast = self.trk.frame - self.trk.begin
# self.holding = key
#elif magic and (self.holding >= pygame.K_KP1 \
# and self.holding <= pygame.K_KP9) \
# or self.holding == pygame.K_KP0:
# self.holding = -1 * self.holding
#else:
# self.holding = key
if magic:
self.flast = self.trk.frame - self.trk.begin
self.holding.append(key)
def op_start(self):
#self.k.replay_op(self.trk.res.video_id, kdb.OP_START,
# self.trk.frame + self.trk.begin)
pass
def op_stop(self):
#self.k.replay_op(self.trk.res.video_id, kdb.OP_STOP,
# self.trk.frame + self.trk.begin)
pass
def op_seek(self):
self.op_start()
def op_hold(self):
#self.k.replay_op(self.trk.res.video_id, kdb.OP_HOLD,
# self.trk.frame + self.trk.begin)
pass
def op_unhold(self):
self.op_start()
class Chaos(KPanel):
def __init__(self, p):
super().__init__(p.k, None, p.container, PRACK, 1)
self.btn_chaos = p.btn_chaos
self.btn_loop = pygame_gui.elements.UIButton(
text='Loop',
manager=self.k.gui,
container=self.container,
relative_rect=pygame.Rect((0, 0), (100, 25)))
self.btn_select = pygame_gui.elements.UIButton(
text='Select',
manager=self.k.gui,
container=self.container,
relative_rect=pygame.Rect((110, 0), (100, 25)))
self.btn_jumps = pygame_gui.elements.UIButton(
text='Decimate',
manager=self.k.gui,
container=self.container,
relative_rect=pygame.Rect((220, 0), (100, 25)))
images = media.list_ack_images()
if images:
image = media.get_ack_image(
images[random.randint(0, len(images)-1)])
surf = pygame.Surface(image.get_size(), flags=pygame.SRCALPHA)
surf.blit(IMAGE, (0, 0))
+ rimages = media.list_replay_images()
+ if rimages:
+ rimage = rimages[random.randint(0, len(rimages)-1)]
+ rimages = media.list_replay_images(rimage)
+ if rimages:
+ surf.blit(media.get_replay_image(rimage,
+ rimages[random.randint(0, len(rimages)-1)]), (81, 42))
surf.blit(image, (0, 0))
surf.blit(media.get_ack_image(
images[random.randint(0, len(images)-1)]), (9, 18))
image = surf
else:
image = IMAGE
self.image = pygame_gui.elements.UIImage(
image_surface=image,
manager=self.k.gui,
container=self.container,
relative_rect=CRACK)
self.cur_btn = self.btn_chaos
self.cur_panel = self.image
def show(self):
if self.cur_btn is not self.btn_chaos:
self.cur_btn.enable()
self.cur_btn = self.btn_chaos
if self.cur_panel is not self.image:
self.cur_panel.kill()
self.cur_panel = self.image
super().show()
def panel_swap(self, btn, panel):
super().panel_swap(btn, panel, self.cur_panel is not self.image)
def click(self, element, target=None):
if element is self.btn_loop:
self.k.job(self.click_loop)
elif element is self.btn_select:
self.k.job(self.click_select)
elif element is self.btn_jumps:
self.k.job(self.click_jumps)
else:
return False
return True
def click_loop(self):
self.panel_swap(self.btn_loop, Loop(self))
def click_select(self):
self.panel_swap(self.btn_select, Select(self))
def click_jumps(self):
self.panel_swap(self.btn_jumps, Jumps(self))
class Loop(KPanel):
def __init__(self, p):
super().__init__(p.k, None, p.container, CRACK, 1)
self.lbl = pygame_gui.elements.UILabel(
text='loop',
manager=self.k.gui,
container=self.container,
relative_rect=pygame.Rect((0, 0), (-1, -1)))
class Select(KPanel):
def __init__(self, p):
super().__init__(p.k, None, p.container, CRACK, 1)
self.lbl = pygame_gui.elements.UILabel(
text='select',
manager=self.k.gui,
container=self.container,
relative_rect=pygame.Rect((0, 0), (-1, -1)))
class Jumps(KPanel):
def __init__(self, p):
super().__init__(p.k, None, p.container, CRACK, 1)
self.lbl = pygame_gui.elements.UILabel(
text='decimate',
manager=self.k.gui,
container=self.container,
relative_rect=pygame.Rect((0, 0), (-1, -1)))
diff --git a/k/storage.py b/k/storage.py
--- a/k/storage.py
+++ b/k/storage.py
@@ -1,304 +1,310 @@
import k.db as kdb
from pytube import YouTube, Channel
import pygame.image
from os import mkdir, listdir
from os.path import join, exists
from urllib.request import urlretrieve
import xml.etree.ElementTree as ET
from subprocess import run, PIPE, Popen
import shutil
from datetime import datetime
from io import BytesIO
ACKS = 'a'
#BLEEDS = 'b'
#CAMS = 'c'
GFX = 'g'
DOWNLOADS = 'd'
PROJECTS = 'p'
#FOCUS = 'f'
REPLAYS = 'r'
TEMP = '/tmp'
NAKED_TEXTS = 'captions.xsl'
def get_gfx(filename):
return pygame.image.load(join(GFX, filename))
def get_video_filename(yt_channel_id, yt_video_id, yt_itag, extension):
return join(DOWNLOADS, yt_channel_id,
f'{yt_video_id}.{yt_itag}.{extension}')
def get_video_thumbnail(yt_channel_id, yt_video_id):
return join(DOWNLOADS, yt_channel_id, f'{yt_video_id}.jpg')
def get_audio_filename(yt_channel_id, yt_video_id):
return join(DOWNLOADS, yt_channel_id, f'{yt_video_id}.wav')
def get_frag_thumbnail(project_id, frag_id):
return join(PROJECTS, str(project_id), f'{frag_id}.jpg')
def start_project(project_id):
folder = join(PROJECTS, str(project_id))
print(f'creating project folder: {folder}')
mkdir(folder)
def copy_thumbnail(video_id, project_id, frag_id):
video = kdb.get_video(video_id)
channel = kdb.get_channel(video['channel'])
src = get_video_thumbnail(channel['ytid'], video['ytid'])
dst = get_frag_thumbnail(project_id, frag_id)
shutil.copyfile(src, dst)
def finish_adding_video(video_id):
video = kdb.get_video(video_id)
url = 'https://youtu.be/' + video['ytid']
add_video_to_library(YouTube(url), video_id)
def add_video_to_library(video, video_id=None):
print(f'add to: {DOWNLOADS}/')
channel_id = kdb.id_channel(video.channel_id)
print(f'channel: {channel_id}')
channel = Channel(video.channel_url)
if channel_id:
kdb.update_channel(channel_id, channel)
else:
channel_id = kdb.insert_channel(channel)
try:
mkdir(join(DOWNLOADS, video.channel_id))
except FileExistsError:
pass
if video_id:
kdb.update_video(video_id, video)
else:
video_id = kdb.insert_video(channel_id, video)
folder = join(DOWNLOADS, video.channel_id)
thumbnail = join(folder, f'{video.video_id}.jpg')
urlretrieve(video.thumbnail_url, thumbnail)
for keyword in video.keywords:
kdb.insert_keyword(video_id, keyword)
print('video streams available:')
print(video.streams)
stream = video.streams.get_lowest_resolution()
if not stream:
stream = video.streams.order_by('resolution').first()
stream_id = kdb.insert_stream(video_id, stream)
print(f'downloading {stream}...')
stream.download(folder, f'{video.video_id}.{stream.itag}.{stream.subtype}')
print(' download complete')
captions = None
try:
captions = video.captions['en']
except KeyError:
try:
captions = video.captions['a.en']
except KeyError:
pass
if captions:
print(f' captions lang: {captions.code}')
filename = f'{video.video_id}.{captions.code}.xml'
xml = join(folder, filename)
#FIXME pytube caption converter is broken right now?
#captions.download(folder, filename)
with open(xml, 'w') as outfile:
outfile.write(captions.xml_captions)
#FIXME - system call to xsltproc
text = run(['xsltproc', NAKED_TEXTS, xml], stdout=PIPE, check=True)
text = " ".join(text.stdout.decode('utf-8').split())
captions_id = kdb.insert_captions(video_id, captions, text)
root = ET.fromstring(captions.xml_captions)
if root.tag == 'timedtext':
caps = [ ]
_recurse_captions_(caps, captions_id, 0, root)
print(f' inserting {len(caps)} rows...')
count = kdb.insert_caption_list(caps)
print(f' {count} rows inserted')
else:
print(f' UNSUPPORTED captions type: {root.tag}')
else:
print(' no captions')
return video_id
def _recurse_captions_(caps, captions_id, start, elem):
if 't' in elem.attrib:
start += int(elem.attrib['t'])
text = elem.text
if text:
text = text.strip().replace('\n', ' ')
if text:
#print(f'{start}: {text}')
#kdb.insert_caption(captions_id, start, text)
caps.append((captions_id, start, text))
for child in elem:
_recurse_captions_(caps, captions_id, start, child)
def extract_audio_from_video(video_id):
video = kdb.list_video(video_id)
stream = kdb.list_video_streams(video_id)[0] #FIXME
vfn = get_video_filename(video['cytid'], video['ytid'],
stream['itag'], stream['subtype'])
afn = get_audio_filename(video['cytid'], video['ytid'])
extract_audio(vfn, afn)
def extract_audio(vfn, afn):
print(f'extracting audio: {afn}')
#FIXME - system call to ffmpeg
run(['ffmpeg', '-n', '-i', vfn, afn], check=True)
'''
def focus_peek(me=True, delay=None):
#print('peekaboo! :-P')
dt = datetime.now().strftime('%Y-%m-%d_%H-%M-%S_%f')
cmd = [
'spectacle',
'--background', '--nonotify',
]
if me:
cmd.extend([
'--output', join(FOCUS, f'{dt}.k.png'),
'--activewindow', '--no-decoration',
])
else:
cmd.extend([
'--output', join(FOCUS, f'{dt}.desk.png'),
'--fullscreen',
])
if delay:
cmd.extend(['--delay', str(delay)])
# FIXME - system call to spectacle
#run(cmd)
return Popen(cmd, shell=False, stdin=None, stdout=None, stderr=None,
close_fds=True)
'''
def start_replay(replay_id):
folder = join(REPLAYS, str(replay_id))
print(f'creating replay folder: {folder}')
mkdir(folder)
def save_replay_image(replay_id, surface, name):
fn = join(REPLAYS, str(replay_id), f'{name}.png')
pygame.image.save(surface, fn)
-def list_replay_images(replay_id):
+def list_replay_images(replay_id=None):
names = [ ]
- for fn in listdir(join(REPLAYS, str(replay_id))):
- if not fn.endswith('.png'):
- continue
+ if replay_id is not None:
+ for fn in listdir(join(REPLAYS, str(replay_id))):
+ if not fn.endswith('.png'):
+ continue
- names.append(fn[:-4])
+ names.append(fn[:-4])
+
+ else:
+ for rid in listdir(REPLAYS):
+ if not rid.startswith('.'):
+ names.append(int(rid))
return names
def get_replay_image(replay_id, name):
return pygame.image.load(join(REPLAYS, str(replay_id), f'{name}.png'))
def save_ack_image(surface, name):
fn = join(ACKS, f'{name}.png')
pygame.image.save(surface, fn)
def list_ack_images():
names = [ ]
for fn in listdir(ACKS):
if not fn.endswith('.png'):
continue
names.append(fn[:-4])
return names
def get_ack_image(name):
return pygame.image.load(join(ACKS, f'{name}.png'))
'''
The pygame scrap system (clipboard) is only giving me text/plain entries.
Any binary data (e.g. image/png) isn't working on my system.
So we implement it ourselves here using the xclip system command.
-lannocc
'''
def put_clipboard_image(surface):
#fn = join(TEMP, 'k-os_clipboard')
#with open(fn, 'wb') as out:
# out.write(data)
#return fn
with BytesIO() as io:
pygame.image.save(surface, io, 'clipboard.png')
# FIXME: system call to xclip
run(['xclip', '-selection', 'clipboard', '-i', '-t', 'image/png'],
input=io.getvalue(), check=True)
def get_clipboard_image():
# FIXME: system call to xclip
xclip = run(['xclip', '-selection', 'clipboard', '-o', '-t', 'TARGETS'],
capture_output=True, text=True, check=True)
targets = xclip.stdout.split('\n')
if 'image/png' in targets:
return _get_clipboard_image_('image/png')
else:
for target in targets:
if target.startswith('image/'):
try:
return _get_clipboard_image_(target)
except pygame.error:
pass
return None
def _get_clipboard_image_(target):
assert target.startswith('image/')
# FIXME: system call to xclip
xclip = run(['xclip', '-selection', 'clipboard', '-o', '-t', target],
capture_output=True, check=True)
with BytesIO(xclip.stdout) as io:
return pygame.image.load(io, f'clipboard.{target[6:]}')
def save_palette(surf):
fn = join(GFX, 'palette.png')
pygame.image.save(surf, fn)
def load_palette():
fn = join(GFX, 'palette.png')
if not exists(fn):
return None
return pygame.image.load(fn)
diff --git a/version.py b/version.py
--- a/version.py
+++ b/version.py
@@ -1,8 +1,8 @@
-VVHEN="6.01 (crickets) 03+04/2"
+VVHEN="6.01 (crickets) 03+30/2"
THEY="yahoo.com"
SAW="LANNOCC"
ME="shawn"
print(VVHEN+' --'+SAW)
'''can you help?'''
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Sep 13, 3:45 PM (2 w, 6 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3529
Default Alt Text
(36 KB)
Attached To
Mode
rK Media System
Attached
Detach File
Event Timeline
Log In to Comment