Skip to content

Commit

Permalink
Implement working folder plugin in Python
Browse files Browse the repository at this point in the history
  • Loading branch information
elonen committed May 4, 2024
1 parent 4d07ed2 commit 3efc862
Show file tree
Hide file tree
Showing 28 changed files with 1,279 additions and 294 deletions.
137 changes: 88 additions & 49 deletions client/src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,34 @@ function connectWebsocketAfterAuthCheck(ws_url: string)
}
else if ( msg.type === Proto3.UserMessage_Type.VIDEO_UPDATED ) {
refreshMyVideos();
} else {
}
else if ( msg.type === Proto3.UserMessage_Type.VIDEO_ADDED ) {
console.log("Handling VIDEO_ADDED: ", msg);
if (!msg.refs?.videoId) { console.error("VIDEO_ADDED message with no videoId. This is a bug."); }
// Parse details and extract JSON data (added by FileUpload) from msg
const uploadCookies = JSON.parse(msg.details ?? '{}');
const listingData = JSON.parse(uploadCookies.listing_data_json ?? '{}');
const videoAddedAction = uploadCookies.video_added_action;
// Call organizer script if defined, otherwise refresh video list
if (videoAddedAction) {
const action = $serverDefinedActions[videoAddedAction];
if (!action) {
const errorMsg = `Undefined video_added_action: '${videoAddedAction}'`;
acts.add({ mode: 'error', message: errorMsg, lifetime: 5 });
console.error(errorMsg);
} else {
callOrganizerScript(action.action, {
video_id: msg.refs?.videoId,
listing_data: listingData,
});
}
} else {
refreshMyVideos();
}
}
else {
$userMessages = $userMessages.filter((m) => m.id != msg.id);
if (msg.created) { $userMessages.push(msg); }
if (!msg.seen) {
Expand Down Expand Up @@ -584,12 +611,10 @@ function onReorderItems(e: {detail: {ids: Proto3.FolderItemID[], listingData: St
function openVideoListItem(e: { detail: { item: Proto3.PageItem_FolderListing_Item, listingData: StringMap }}): void {
let {item, listingData} = e.detail;
if (item.openAction) {
if ( item.openAction.lang == Proto3.ScriptCall_Lang.JAVASCRIPT )
callOrganizerScript(item.openAction.code, [item], listingData);
else {
console.error("BUG: Unsupported Organizer script language: " + item.openAction.lang);
acts.add({mode: 'error', message: "BUG: Unsupported script lang. See log.", lifetime: 5});
}
callOrganizerScript(item.openAction, {
listing_data: listingData,
item_to_open: item
});
} else {
console.error("No openAction script for item: " + item);
acts.add({mode: 'error', message: "No open action for item. See log.", lifetime: 5});
Expand Down Expand Up @@ -617,17 +642,22 @@ function openVideoListItem(e: { detail: { item: Proto3.PageItem_FolderListing_It
};
/// Evalute a string as Javascript from Organizer (or Server)
function callOrganizerScript(code: string|undefined, items: any[], listingData: StringMap): void {
if (!code) {
function callOrganizerScript(script: Proto3.ScriptCall|undefined, action_args: Object): void {
if (!script || !script.code ) {
console.log("callOrganizerScript called with empty code. Ignoring.");
return;
}
if (script.lang != Proto3.ScriptCall_Lang.JAVASCRIPT ) {
console.error("BUG: Unsupported Organizer script language: " + script.lang);
acts.add({mode: 'error', message: "BUG: Unsupported script lang. See log.", lifetime: 5});
return;
}
const Function = function () {}.constructor;
// @ts-ignore
let scriptFn = new Function("items", "listingData", code);
console.log("Calling organizer script:", {listingData, items, code});
let scriptFn = new Function("_action_args", script.code);
console.log("Calling organizer script:", {action_args, code: script.code});
try {
scriptFn(items, listingData);
scriptFn(action_args);
} catch (e: any) {
console.error("Error in organizer script:", e);
acts.add({mode: 'error', message: "Organizer script error. See log.", lifetime: 5});
Expand All @@ -639,7 +669,10 @@ function onVideoListPopupAction(e: { detail: { action: Proto3.ActionDef, items:
let {action, items, listingData} = e.detail;
let itemsObjs = items.map((it) => it.obj);
console.log("onVideoListPopupAction():", {action, itemsObjs, listingData});
callOrganizerScript(action.action?.code, itemsObjs, listingData);
callOrganizerScript(action.action, {
listing_data: listingData,
selected_items: itemsObjs
});
}
</script>

Expand Down Expand Up @@ -717,46 +750,52 @@ function onVideoListPopupAction(e: { detail: { action: Proto3.ActionDef, items:

{:else}

<!-- ========== page components ============= -->
<div class="organizer_page">
{#each $curPageItems as pit}
{#if pit.html }
<div>
<RawHtmlItem html={pit.html} />
</div>
{:else if pit.folderListing}
<div class="my-6">
<VideoList
listingData={pit.folderListing.listingData}
items={pit.folderListing.items.map((it)=>({
id: (it.video?.id ?? it.folder?.id ?? "[BUG: BAD ITEM TYPE]"),
obj: it }))}
dragDisabled = {pit.folderListing.allowReordering ? false : true}
listPopupActions = {pit.folderListing.popupActions}
on:open-item = {openVideoListItem}
on:reorder-items = {onReorderItems}
on:move-to-folder = {onMoveItemsToFolder}
on:popup-action = {onVideoListPopupAction}
/>
</div>
{/if}
{/each}
</div>

<!-- ========== upload widget ============= -->
<div class="m-6 h-24 border-4 border-dashed border-gray-700">
<FileUpload postUrl={uploadUrl}>
<div class="flex flex-col justify-center items-center h-full">
<div class="text-2xl text-gray-700">
<i class="fas fa-upload"></i>
<!-- ========== page components ============= -->
<div class="organizer_page">
{#each $curPageItems as pit}
{#if pit.html }
<div>
<RawHtmlItem html={pit.html} />
</div>
<div class="text-xl text-gray-700">
Drop video files here to upload
{:else if pit.folderListing}
<div class="my-6">
<VideoList
listingData={pit.folderListing.listingData}
items={pit.folderListing.items.map((it)=>({
id: (it.video?.id ?? it.folder?.id ?? "[BUG: BAD ITEM TYPE]"),
obj: it }))}
dragDisabled = {pit.folderListing.allowReordering ? false : true}
listPopupActions = {pit.folderListing.popupActions}
on:open-item = {openVideoListItem}
on:reorder-items = {onReorderItems}
on:move-to-folder = {onMoveItemsToFolder}
on:popup-action = {onVideoListPopupAction}
/>
</div>
</div>
</FileUpload>
<!-- ========== upload widget ============= -->
{#if pit.folderListing.allowUpload }
<div class="h-24 border-4 border-dashed border-gray-700">
<FileUpload
postUrl={uploadUrl}
listingData={pit.folderListing.listingData ?? {}}
videoAddedAction={pit.folderListing.videoAddedAction}
>
<div class="flex flex-col justify-center items-center h-full">
<div class="text-2xl text-gray-700">
<i class="fas fa-upload"></i>
</div>
<div class="text-xl text-gray-700">
Drop video files here to upload
</div>
</div>
</FileUpload>
</div>
{/if}
{/if}
{/each}
</div>


<div>
{#if $userMessages.length>0}
<h1 class="text-2xl m-6 mt-12 text-slate-500">
Expand Down
17 changes: 14 additions & 3 deletions client/src/lib/FileUpload.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import LocalStorageCookies from "@/cookies";
import Dropzone from "svelte-file-dropzone"
import Dropzone from "svelte-file-dropzone"
let dragActive: boolean = false;
let files = {
Expand All @@ -9,6 +9,10 @@ let files = {
};
export let postUrl: string;
// Passed to HTTP POST request:
export let listingData: Object;
export let videoAddedAction: string|undefined;
let progressBar: HTMLProgressElement;
let statusTxt: string = "";
Expand All @@ -31,7 +35,8 @@ function progressHandler(event: ProgressEvent<XMLHttpRequestEventTarget>)
uploadingNow = true;
// loaded_total = "Uploaded " + event.loaded + " bytes of " + event.total;
var percent = (event.loaded / event.total) * 100;
progressBar.value = Math.round(percent);
if (progressBar)
progressBar.value = Math.round(percent);
statusTxt = Math.round(percent) + "% uploaded... please wait";
}
Expand Down Expand Up @@ -64,7 +69,13 @@ function upload() {
ajax.addEventListener("abort", abortHandler, false);
ajax.open("POST", postUrl);
ajax.setRequestHeader("X-FILE-NAME", file.name);
ajax.setRequestHeader("X-CLAPSHOT-COOKIES", JSON.stringify(LocalStorageCookies.getAllNonExpired()));
let upload_cookies = { ...LocalStorageCookies.getAllNonExpired() };
if (videoAddedAction)
upload_cookies["video_added_action"] = videoAddedAction;
upload_cookies["listing_data_json"] = JSON.stringify(listingData);
ajax.setRequestHeader("X-CLAPSHOT-COOKIES", JSON.stringify(upload_cookies));
ajax.send(formdata);
}
files.accepted = [];
Expand Down
19 changes: 12 additions & 7 deletions client/src/lib/video_list/VideoList.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ function handleMouseOrKeyDown(id: string, e: any) {
console.log("(dragging => videolist: ignore key/mouse down)");
return;
}
hidePopupMenus();
// Open item by keyboard
if (e.key) {
if (e.key == "Enter") {
Expand All @@ -97,7 +99,7 @@ function handleMouseOrKeyDown(id: string, e: any) {
}
}
// (Multi-)selecting items
if (!e.ctrlKey && !e.metaKey) return;
if (!e.shiftKey ) return;
if (e.key && e.key !== "Shift") return;
if (Object.keys($selectedTiles).includes(id)) {
delete($selectedTiles[id]);
Expand Down Expand Up @@ -125,23 +127,26 @@ function transformDraggedElement(el: any) {
function handleMouseUp(e: MouseEvent, item: VideoListDefItem) {
if (e.button > 0) return; // ignore right click
if (!isDragging && !e.ctrlKey) {
if (!isDragging && !e.shiftKey) {
$selectedTiles = {};
$selectedTiles[item.id] = item;
}
}
// Show a popup menu when right-clicking on a video tile
function onContextMenu(e: MouseEvent, item: VideoListDefItem|null)
{
function hidePopupMenus() {
let popupContainer = document.querySelector('#popup-container');
if (!popupContainer) { alert("UI BUG: popup container missing"); return; }
// Remove any existing popups
for (let child of popupContainer.children as any) {
if (!('hide' in child)) { alert("UI BUG: popup container child missing hide()"); }
child.hide();
}
}
// Show a popup menu when right-clicking on a video tile
function onContextMenu(e: MouseEvent, item: VideoListDefItem|null)
{
let popupContainer = document.querySelector('#popup-container');
hidePopupMenus();
let actions: Proto3.ActionDef[] = [];
let targetTiles: VideoListDefItem[] = [];
Expand Down
6 changes: 6 additions & 0 deletions client/src/lib/video_list/VideoListFolder.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ function finalize(e: any) {
thumbSheetCols={prev.video.previewData?.thumbSheet?.cols}
/>
</div>
{:else if prev.folder }
<div class="w-full aspect-square overflow-clip inline-block shadow-md relative rounded-md">
<div class="w-full h-full video-list-folder flex items-center justify-center">
<span class="text-xs text-slate-500 text-center leading-none italic">{prev.folder?.title}</span>
</div>
</div>
{/if}
{/each}
</div>
Expand Down
Loading

0 comments on commit 3efc862

Please sign in to comment.