Skip to content
Draft
19 changes: 19 additions & 0 deletions anthias_app/migrations/0003_asset_zoom_level.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 4.2.22 on 2025-07-02 18:13

import django.core.validators
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('anthias_app', '0002_auto_20241015_1524'),
]

operations = [
migrations.AddField(
model_name='asset',
name='zoom_level',
field=models.FloatField(default=1.0, validators=[django.core.validators.MinValueValidator(0.25), django.core.validators.MaxValueValidator(5.0)]),
),
]
8 changes: 8 additions & 0 deletions anthias_app/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import uuid

from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.utils import timezone

Expand All @@ -23,6 +24,13 @@ class Asset(models.Model):
nocache = models.BooleanField(default=False)
play_order = models.IntegerField(default=0)
skip_asset_check = models.BooleanField(default=False)
zoom_level = models.FloatField(
default=1.0,
validators=[
MinValueValidator(0.25),
MaxValueValidator(5.0)
]
)

class Meta:
db_table = 'assets'
Expand Down
12 changes: 12 additions & 0 deletions api/serializers/v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
CharField,
ChoiceField,
DateTimeField,
FloatField,
IntegerField,
ModelSerializer,
Serializer,
Expand Down Expand Up @@ -39,6 +40,7 @@ class Meta:
'skip_asset_check',
'is_active',
'is_processing',
'zoom_level',
]


Expand Down Expand Up @@ -71,6 +73,16 @@ class UpdateAssetSerializerV2(UpdateAssetSerializer):
nocache = BooleanField(required=False)
skip_asset_check = BooleanField(required=False)
duration = IntegerField()
zoom_level = FloatField(required=False)

def update(self, instance, validated_data):
instance = super().update(instance, validated_data)

if 'zoom_level' in validated_data:
instance.zoom_level = validated_data['zoom_level']

instance.save()
return instance


class DeviceSettingsSerializerV2(Serializer):
Expand Down
6 changes: 6 additions & 0 deletions static/sass/_styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,12 @@ label {
width: 100%;
}
}

.ZoomLevel {
input {
width: 35%;
}
}
}

.splash {
Expand Down
1 change: 1 addition & 0 deletions static/src/components/active-assets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ export const ActiveAssetsTable = ({ onEditAsset }: ActiveAssetsTableProps) => {
skipAssetCheck={asset.skip_asset_check}
onEditAsset={onEditAsset}
showDragHandle={true}
zoomLevel={asset.zoom_level}
/>
))}
</SortableContext>
Expand Down
1 change: 1 addition & 0 deletions static/src/components/asset-row/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export const AssetRow = forwardRef<HTMLTableRowElement, AssetRowProps>(
is_enabled: props.isEnabled,
nocache: props.nocache,
skip_asset_check: props.skipAssetCheck,
zoom_level: props.zoomLevel,
});
}
};
Expand Down
9 changes: 9 additions & 0 deletions static/src/components/edit-asset-modal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { DateFields } from '@/components/edit-asset-modal/date-fields';
import { DurationField } from '@/components/edit-asset-modal/duration-field';
import { ModalFooter } from '@/components/edit-asset-modal/modal-footer';
import { AdvancedFields } from '@/components/edit-asset-modal/advanced';
import { ZoomLevelField } from '@/components/edit-asset-modal/zoom-level-field';

interface EditAssetModalProps {
isOpen: boolean;
Expand All @@ -35,6 +36,7 @@ export const EditAssetModal = ({
mimetype: 'webpage',
nocache: false,
skip_asset_check: false,
zoom_level: 1.0,
});
const [loopTimes, setLoopTimes] = useState('manual');
const [startDateDate, setStartDateDate] = useState('');
Expand Down Expand Up @@ -71,6 +73,7 @@ export const EditAssetModal = ({
mimetype: asset.mimetype || 'webpage',
nocache: asset.nocache || false,
skip_asset_check: asset.skip_asset_check || false,
zoom_level: asset.zoom_level ?? 1.0,
});

setStartDateDate(formatDatePart(startDate));
Expand Down Expand Up @@ -223,6 +226,12 @@ export const EditAssetModal = ({
formData={formData}
handleInputChange={handleInputChange}
/>
{formData.mimetype === 'webpage' && (
<ZoomLevelField
formData={formData}
handleInputChange={handleInputChange}
/>
)}
<AdvancedFields
formData={formData}
handleInputChange={handleInputChange}
Expand Down
33 changes: 33 additions & 0 deletions static/src/components/edit-asset-modal/zoom-level-field.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { EditFormData } from '@/types';

interface ZoomLevelFieldProps {
formData: EditFormData;
handleInputChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
}

export const ZoomLevelField = ({
formData,
handleInputChange,
}: ZoomLevelFieldProps) => {
return (
<div className="form-group row duration ZoomLevel">
<label htmlFor="zoom_level" className="col-4 col-form-label">
Zoom Level
</label>
<div className="col-7 controls">
<input
id="zoom_level"
className="form-control shadow-none"
name="zoom_level"
type="number"
value={formData.zoom_level}
min={0.25}
max={5.0}
step={0.25}
onChange={handleInputChange}
disabled={formData.mimetype === 'video'}
/>
</div>
</div>
);
};
1 change: 1 addition & 0 deletions static/src/components/inactive-assets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export const InactiveAssetsTable = ({
skipAssetCheck={asset.skip_asset_check}
onEditAsset={onEditAsset}
showDragHandle={false}
zoomLevel={asset.zoom_level}
/>
))}
</tbody>
Expand Down
4 changes: 4 additions & 0 deletions static/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface Asset {
is_active: boolean;
play_order: number;
is_processing: boolean;
zoom_level?: number;
}

export interface AssetEditData {
Expand All @@ -30,6 +31,7 @@ export interface AssetEditData {
nocache: boolean;
skip_asset_check: boolean;
play_order?: number;
zoom_level?: number;
}

export interface EditFormData {
Expand All @@ -40,6 +42,7 @@ export interface EditFormData {
mimetype: string;
nocache: boolean;
skip_asset_check: boolean;
zoom_level?: number;
}

export interface HandleSubmitParams {
Expand Down Expand Up @@ -135,6 +138,7 @@ export interface AssetRowProps {
dragHandleProps?: React.HTMLAttributes<HTMLElement>;
isDragging?: boolean;
onEditAsset?: (asset: AssetEditData) => void;
zoomLevel?: number;
}

// Settings-related types
Expand Down
7 changes: 5 additions & 2 deletions tests/test_scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
'nocache': 0,
'is_processing': 0,
'play_order': 1,
'skip_asset_check': 0
'skip_asset_check': 0,
'zoom_level': 1.0
}

ASSET_X_DIFF = {
Expand All @@ -44,7 +45,8 @@
'nocache': 0,
'is_processing': 0,
'play_order': 0,
'skip_asset_check': 0
'skip_asset_check': 0,
'zoom_level': 1.0
}

ASSET_Z = {
Expand Down Expand Up @@ -156,4 +158,5 @@ def test_playlist_should_be_updated_after_deadline_reached(self):
scheduler.refresh_playlist()

self.assertEqual([ASSET_X], scheduler.assets)

traveller.stop()
4 changes: 2 additions & 2 deletions tools/image_builder/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ def get_test_context() -> dict:
def get_viewer_context(board: str) -> dict:
releases_url = f'{GITHUB_REPO_URL}/releases/download'

webview_git_hash = 'fb2cb9e'
webview_base_url = f'{releases_url}/WebView-v0.3.7'
webview_git_hash = 'b084a6f'
webview_base_url = f'{releases_url}/WebView-v0.3.8'

qt_version = '5.15.14'

Expand Down
6 changes: 3 additions & 3 deletions viewer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,13 @@ def load_browser():
sleep(1)


def view_webpage(uri):
def view_webpage(uri, zoom_level=1.0):
global current_browser_url

if browser is None or not browser.process.alive:
load_browser()
if current_browser_url is not uri:
browser_bus.loadPage(uri)
browser_bus.loadPage(uri, zoom_level)
current_browser_url = uri
logging.info('Current url is {0}'.format(current_browser_url))

Expand Down Expand Up @@ -239,7 +239,7 @@ def asset_loop(scheduler):
if 'image' in mime:
view_image(uri)
elif 'web' in mime:
view_webpage(uri)
view_webpage(uri, zoom_level=asset['zoom_level'])
elif 'video' or 'streaming' in mime:
view_video(uri, asset['duration'])
else:
Expand Down