feat: Implement basic general folder support (#2051)

- Related: #1748
This commit is contained in:
Dermot Duffy authored and GitHub committed 2025-05-21 19:59:21 -07:00
1 parent 2eb0d9e35e
commit c6a4c8aea2
350 files changed
+12837 -4509

No files matched your search

@@ -10,7 +10,7 @@ import {
getActionConfigGivenAction,
isAdvancedCameraCardCustomAction,
} from '../../utils/action.js';
import { allPromises } from '../../utils/basic.js';
import { allPromises, errorToConsole } from '../../utils/basic.js';
import { TemplateRenderer } from '../templates/index.js';
import { CardActionsManagerAPI } from '../types.js';
import { ActionSet } from './actions/set.js';
@@ -53,7 +53,7 @@ export class ActionsManager implements ActionsExecutor {
let specificActions: Actions | undefined = undefined;
if (view?.is('live')) {
specificActions = config?.live.actions;
} else if (view?.isGalleryView()) {
} else if (view?.isMediaGalleryView()) {
specificActions = config?.media_gallery?.actions;
} else if (view?.isViewerView()) {
specificActions = config?.media_viewer.actions;
@@ -150,6 +150,7 @@ export class ActionsManager implements ActionsExecutor {
await actionSet.execute(this._api);
forwardHaptic('success');
} catch (e) {
errorToConsole(e as Error);
forwardHaptic('warning');
}
this._actionsInFlight = this._actionsInFlight.filter((a) => a !== actionSet);
@@ -6,6 +6,9 @@ export class DownloadAction extends AdvancedCameraCardAction<GeneralActionConfig
public async execute(api: CardActionsAPI): Promise<void> {
await super.execute(api);
await api.getDownloadManager().downloadViewerMedia();
const item = api.getViewManager().getView()?.queryResults?.getSelectedResult();
if (item) {
await api.getViewItemManager().download(item);
}
}
}
@@ -0,0 +1,27 @@
import { FolderActionConfig } from '../../../config/schema/actions/custom/folder';
import { FolderViewQuery } from '../../../view/query';
import { CardActionsAPI } from '../../types';
import { AdvancedCameraCardAction } from './base';
export class FolderAction extends AdvancedCameraCardAction<FolderActionConfig> {
public async execute(api: CardActionsAPI): Promise<void> {
await super.execute(api);
const folder = api.getFoldersManager().getFolder(this._action.folder);
if (!folder) {
return;
}
const query = api.getFoldersManager().generateDefaultFolderQuery(folder);
if (!query) {
return;
}
await api.getViewManager().setViewByParametersWithExistingQuery({
params: {
view: 'folder',
query: new FolderViewQuery(query),
},
});
}
}
@@ -1,5 +1,6 @@
import { MediaPlayerActionConfig } from '../../../config/schema/actions/custom/media-player';
import { getStreamCameraID } from '../../../utils/substream';
import { ViewItemClassifier } from '../../../view/item-classifier';
import { CardActionsAPI } from '../../types';
import { AdvancedCameraCardAction } from './base';
@@ -9,15 +10,19 @@ export class MediaPlayerAction extends AdvancedCameraCardAction<MediaPlayerActio
const mediaPlayer = this._action.media_player;
const mediaPlayerController = api.getMediaPlayerManager();
const view = api.getViewManager().getView();
const media = view?.queryResults?.getSelectedResult() ?? null;
if (this._action.media_player_action === 'stop') {
await mediaPlayerController.stop(mediaPlayer);
} else if (view?.is('live')) {
return;
}
const view = api.getViewManager().getView();
const item = view?.queryResults?.getSelectedResult() ?? null;
if (view?.is('live')) {
await mediaPlayerController.playLive(mediaPlayer, getStreamCameraID(view));
} else if (view?.isViewerView() && media) {
await mediaPlayerController.playMedia(mediaPlayer, media);
} else if (view?.isViewerView() && item && ViewItemClassifier.isMedia(item)) {
await mediaPlayerController.playMedia(mediaPlayer, item);
}
}
}
@@ -1,4 +1,4 @@
import clamp from 'lodash-es/clamp';
import { clamp } from 'lodash-es';
import {
PartialZoomSettings,
ZOOM_DEFAULT_PAN_X,
+12 -6
View File
@@ -86,12 +86,15 @@ export class PTZAction extends AdvancedCameraCardAction<PTZActionConfig> {
setInProgressForThisTarget(ptzCameraID, this._context, 'ptz', this);
const singleStep = async (): Promise<void> => {
this._action.ptz_action &&
(await api
/* istanbul ignore else: the else path cannot be reached as ptz_action
being present is checked above -- @preserve */
if (this._action.ptz_action) {
await api
.getCameraManager()
.executePTZAction(ptzCameraID, this._action.ptz_action, {
preset: this._action.ptz_preset,
}));
});
}
if (!this._stopped) {
// Only start the timer for the next step after this step returns, and
@@ -121,13 +124,16 @@ export class PTZAction extends AdvancedCameraCardAction<PTZActionConfig> {
});
this._timer.start(ptzConfiguration.c2r_delay_between_calls_seconds, async () => {
this._action.ptz_action &&
(await api
/* istanbul ignore else: the else path cannot be reached as ptz_action
being present is checked above -- @preserve */
if (this._action.ptz_action) {
await api
.getCameraManager()
.executePTZAction(ptzCameraID, this._action.ptz_action, {
preset: this._action.ptz_preset,
phase: 'stop',
}));
});
}
});
}
}
@@ -1,4 +1,6 @@
import { GeneralActionConfig } from '../../../config/schema/actions/custom/general';
import { downloadURL } from '../../../utils/download';
import { generateScreenshotFilename } from '../../../utils/screenshot';
import { CardActionsAPI } from '../../types';
import { AdvancedCameraCardAction } from './base';
@@ -6,6 +8,13 @@ export class ScreenshotAction extends AdvancedCameraCardAction<GeneralActionConf
public async execute(api: CardActionsAPI): Promise<void> {
await super.execute(api);
await api.getDownloadManager().downloadScreenshot();
const url = await api
.getMediaLoadedInfoManager()
.get()
?.mediaPlayerController?.getScreenshotURL();
if (url) {
downloadURL(url, generateScreenshotFilename(api.getViewManager().getView()));
}
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
import { SleepActionConfig } from '../../../config/schema/actions/custom/sleep';
import { sleep } from '../../../utils/basic';
import { sleep } from '../../../utils/sleep';
import { CardActionsAPI } from '../../types';
import { timeDeltaToSeconds } from '../utils/time-delta';
import { AdvancedCameraCardAction } from './base';
@@ -3,7 +3,6 @@ import { CardActionsAPI } from '../../types';
import { AdvancedCameraCardAction } from './base';
export class StatusBarAction extends AdvancedCameraCardAction<StatusBarActionConfig> {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public async execute(api: CardActionsAPI): Promise<void> {
await super.execute(api);
+3
View File
@@ -10,6 +10,7 @@ import { DefaultAction } from './actions/default';
import { DisplayModeSelectAction } from './actions/display-mode-select';
import { DownloadAction } from './actions/download';
import { ExpandAction } from './actions/expand';
import { FolderAction } from './actions/folder';
import { FullscreenAction } from './actions/fullscreen';
import { InternalCallbackAction } from './actions/internal-callback';
import { LogAction } from './actions/log';
@@ -150,6 +151,8 @@ export class ActionFactory {
return new StatusBarAction(context, action, options?.config);
case INTERNAL_CALLBACK_ACTION:
return new InternalCallbackAction(context, action, options?.config);
case 'folder':
return new FolderAction(context, action, options?.config);
}
/* istanbul ignore next: this path cannot be reached -- @preserve */
@@ -1,4 +1,4 @@
import merge from 'lodash-es/merge';
import { merge } from 'lodash-es';
import { Action, TargetedActionContext } from '../types';
import { ActionContext } from 'action';