Skip to content

Commit ea5f5ff

Browse files
committed
Merge branch 'fb-leap-1737/playpause-audio-hotkey' of ssh://github.com/heartexlabs/label-studio into fb-leap-1737/playpause-audio-hotkey
2 parents 3c960a6 + 26579e6 commit ea5f5ff

File tree

22 files changed

+180
-32
lines changed

22 files changed

+180
-32
lines changed

.github/workflows/build_pypi.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,9 @@ jobs:
9898
run: yarn version:libs
9999

100100
- name: "Install poetry"
101-
run: pipx install poetry
101+
env:
102+
POETRY_VERSION: ${{ vars.POETRY_VERSION }}
103+
run: pipx install "poetry==${POETRY_VERSION}"
102104

103105
- name: "Set up Python"
104106
id: setup_python

.github/workflows/docker-build.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,9 @@ jobs:
143143
cache-from: type=gha
144144
cache-to: type=gha,mode=max
145145
build-args: |
146-
BRANCH_OVERRIDE: ${{ inputs.branch_name }}
147-
VERSION_OVERRIDE: ${{ steps.version.outputs.build_version }}
146+
BRANCH_OVERRIDE=${{ inputs.branch_name }}
147+
VERSION_OVERRIDE=${{ steps.version.outputs.build_version }}
148+
POETRY_VERSION=${{ vars.POETRY_VERSION }}
148149
149150
- name: Create Docker image tag Check
150151
uses: actions/github-script@v7

.github/workflows/follow-merge-upstream-repo-sync.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,9 @@ jobs:
9191

9292
- name: "Install poetry"
9393
if: steps.details.outputs.poetry
94-
run: pipx install poetry
94+
env:
95+
POETRY_VERSION: ${{ vars.POETRY_VERSION }}
96+
run: pipx install "poetry==${POETRY_VERSION}"
9597

9698
- name: "Set up Python"
9799
id: setup_python

.github/workflows/test_conda.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ jobs:
4747
activate-environment: test-environment
4848

4949
- name: "Install poetry"
50-
run: pipx install poetry
50+
env:
51+
POETRY_VERSION: ${{ vars.POETRY_VERSION }}
52+
run: pipx install "poetry==${POETRY_VERSION}"
5153

5254
- name: "Set up Python"
5355
id: setup_python

.github/workflows/test_migrations.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ jobs:
3535
ref: ${{ inputs.ref }}
3636

3737
- name: "Install poetry"
38-
run: pipx install poetry
38+
env:
39+
POETRY_VERSION: ${{ vars.POETRY_VERSION }}
40+
run: pipx install "poetry==${POETRY_VERSION}"
3941

4042
- name: "Set up Python"
4143
id: setup_python
@@ -98,7 +100,9 @@ jobs:
98100
fetch-depth: 0
99101

100102
- name: "Install poetry"
101-
run: pipx install poetry
103+
env:
104+
POETRY_VERSION: ${{ vars.POETRY_VERSION }}
105+
run: pipx install "poetry==${POETRY_VERSION}"
102106

103107
- name: "Set up Python"
104108
id: setup_python

.github/workflows/tests.yml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ jobs:
4646
ref: ${{ inputs.head_sha }}
4747

4848
- name: "Install poetry"
49-
run: pipx install poetry
49+
env:
50+
POETRY_VERSION: ${{ vars.POETRY_VERSION }}
51+
run: pipx install "poetry==${POETRY_VERSION}"
5052

5153
- name: "Set up Python ${{ matrix.python-version }}"
5254
id: setup_python
@@ -131,7 +133,9 @@ jobs:
131133
ref: ${{ inputs.head_sha }}
132134

133135
- name: "Install poetry"
134-
run: pipx install poetry
136+
env:
137+
POETRY_VERSION: ${{ vars.POETRY_VERSION }}
138+
run: pipx install "poetry==${POETRY_VERSION}"
135139

136140
- name: "Set up Python ${{ matrix.python-version }}"
137141
id: setup_python
@@ -208,7 +212,9 @@ jobs:
208212
ref: ${{ inputs.head_sha }}
209213

210214
- name: "Install poetry"
211-
run: pipx install "poetry<2.0.0"
215+
env:
216+
POETRY_VERSION: ${{ vars.POETRY_VERSION }}
217+
run: pipx install "poetry==${POETRY_VERSION}"
212218

213219
- name: "Set up Python ${{ matrix.python-version }}"
214220
id: setup_python

.github/workflows/update_pip_dependency.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ jobs:
2929
git config --global user.email '[email protected]'
3030
3131
- name: "Install poetry"
32-
run: pipx install poetry
32+
env:
33+
POETRY_VERSION: ${{ vars.POETRY_VERSION }}
34+
run: pipx install "poetry==${POETRY_VERSION}"
3335

3436
- name: "Set up Python"
3537
id: setup_python

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# syntax=docker/dockerfile:1
22
ARG NODE_VERSION=18
33
ARG PYTHON_VERSION=3.12
4-
ARG POETRY_VERSION=1.8.4
4+
ARG POETRY_VERSION=2.0.1
55
ARG VERSION_OVERRIDE
66
ARG BRANCH_OVERRIDE
77

docs/source/guide/script_examples.md

Lines changed: 128 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -470,4 +470,131 @@ This is an example of a "hard" block, meaning that the user must resolve the iss
470470
* [View](/tags/view.html)
471471
* [RectangleLabels](/tags/rectanglelabels.html)
472472
* [TextArea](/tags/textarea.html)
473-
* [Labels](/tags/labels.html)
473+
* [Labels](/tags/labels.html)
474+
475+
## Sync videos with frame offset
476+
477+
This labeling configuration arranges three video players vertically, making it easier to view and annotate each video frame.
478+
479+
The script ensures the videos are synced, with one player showing one frame forward, and another player the previous frame.
480+
481+
![Screenshot of JSON error message](/images/project/video_sync.png)
482+
483+
#### Script
484+
485+
```javascript
486+
// Wait for the Label Studio Interface to be ready
487+
await LSI;
488+
489+
// Get references to the video objects by their names
490+
var videoMinus1 = LSI.annotation.names.get('videoMinus1');
491+
var video0 = LSI.annotation.names.get('video0');
492+
var videoPlus1 = LSI.annotation.names.get('videoPlus1');
493+
494+
if (!videoMinus1 || !video0 || !videoPlus1) return;
495+
496+
// Convert frameRate to a number and ensure it's valid
497+
var frameRate = Number.parseFloat(video0.framerate) || 24;
498+
var frameDuration = 1 / frameRate;
499+
500+
// Function to adjust video sync with offset and guard against endless loops
501+
function adjustVideoSync(video, offsetFrames) {
502+
video.isSyncing = false;
503+
504+
["seek", "play", "pause"].forEach(event => {
505+
video.syncHandlers.set(event, function(data) {
506+
if (video.isSyncing) return;
507+
508+
video.isSyncing = true;
509+
510+
if (!video.ref.current || video === video0) {
511+
video.isSyncing = false;
512+
return;
513+
}
514+
515+
const videoElem = video.ref.current;
516+
517+
adjustedTime = (video0.ref.current.currentFrame + offsetFrames) * frameDuration;
518+
adjustedTime = Math.max(0, Math.min(adjustedTime, video.ref.current.duration));
519+
520+
if (data.playing) {
521+
if (!videoElem.playing) videoElem.play();
522+
} else {
523+
if (videoElem.playing) videoElem.pause();
524+
}
525+
526+
if (data.speed) {
527+
video.speed = data.speed;
528+
}
529+
530+
videoElem.currentTime = adjustedTime;
531+
if (Math.abs(videoElem.currentTime - adjustedTime) > frameDuration/2) {
532+
videoElem.currentTime = adjustedTime;
533+
}
534+
535+
video.isSyncing = false;
536+
});
537+
});
538+
}
539+
540+
// Adjust offsets for each video
541+
adjustVideoSync(videoMinus1, -1);
542+
adjustVideoSync(videoPlus1, 1);
543+
adjustVideoSync(video0, 0);
544+
```
545+
546+
**Related LSI instance methods:**
547+
548+
* [annotation](scripts#LSI-annotation)
549+
550+
#### Labeling config
551+
552+
Each video is wrapped in a `<View>` tag with a width of 100% to ensure they stack on top of each other. The `Header` tag provides a title for
553+
each video, indicating which frame is being displayed.
554+
555+
The `Video` tags are used to load the video content, with the `name` attribute uniquely identifying each video player.
556+
557+
The `TimelineLabels` tag is connected to the second video (`video0`), allowing annotators to label specific segments of that video. The labels `class1` and `class2` can be used to categorize the content of the video, enhancing the annotation process.
558+
559+
```xml
560+
<View>
561+
<View style="display: flex">
562+
<View style="width: 100%">
563+
<Header value="Video -1 Frame"/>
564+
<Video name="videoMinus1" value="$video_url"
565+
height="200" sync="lag" frameRate="29.97"/>
566+
</View>
567+
<View style="width: 100%">
568+
<Header value="Video +1 Frame"/>
569+
<Video name="videoPlus1" value="$video_url"
570+
height="200" sync="lag" frameRate="29.97"/>
571+
</View>
572+
</View>
573+
<View style="width: 100%; margin-bottom: 1em;">
574+
<Header value="Video 0 Frame"/>
575+
<Video name="video0" value="$video_url"
576+
height="400" sync="lag" frameRate="29.97"/>
577+
</View>
578+
<TimelineLabels name="timelinelabels" toName="video0">
579+
<Label value="class1"/>
580+
<Label value="class2"/>
581+
</TimelineLabels>
582+
</View>
583+
```
584+
585+
**Related tags:**
586+
587+
* [View](/tags/view.html)
588+
* [Video](/tags/video.html)
589+
* [TimelineLabels](/tags/timelinelabels.html)
590+
* [Label](/tags/label.html)
591+
592+
#### Data
593+
594+
```json
595+
{
596+
"data": {
597+
"video_url": "https://example.com/path/to/video.mp4"
598+
}
599+
}
600+
```
Loading

label_studio/core/utils/common.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import traceback as tb
1515
import uuid
1616
from collections import defaultdict
17-
from datetime import datetime
1817
from functools import wraps
1918
from typing import Any, Callable, Generator, Iterable, Mapping, Optional
2019

@@ -41,6 +40,7 @@
4140
pre_save,
4241
)
4342
from django.db.utils import OperationalError
43+
from django.utils import timezone
4444
from django.utils.crypto import get_random_string
4545
from django.utils.module_loading import import_string
4646
from django_filters.rest_framework import DjangoFilterBackend
@@ -261,7 +261,7 @@ def datetime_to_timestamp(dt):
261261

262262

263263
def timestamp_now():
264-
return datetime_to_timestamp(datetime.utcnow())
264+
return datetime_to_timestamp(timezone.now())
265265

266266

267267
def find_first_one_to_one_related_field_by_prefix(instance, prefix):

label_studio/io_storages/azure_blob/models.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import json
44
import logging
55
import re
6-
from datetime import datetime, timedelta
6+
from datetime import timedelta
77
from typing import Union
88
from urllib.parse import urlparse
99

@@ -15,6 +15,7 @@
1515
from django.db import models
1616
from django.db.models.signals import post_save
1717
from django.dispatch import receiver
18+
from django.utils import timezone
1819
from django.utils.translation import gettext_lazy as _
1920
from io_storages.base_models import (
2021
ExportStorage,
@@ -144,7 +145,7 @@ def generate_http_url(self, url):
144145
container = r.netloc
145146
blob = r.path.lstrip('/')
146147

147-
expiry = datetime.utcnow() + timedelta(minutes=self.presign_ttl)
148+
expiry = timezone.now() + timedelta(minutes=self.presign_ttl)
148149

149150
sas_token = generate_blob_sas(
150151
account_name=self.get_account_name(),

web/dist/apps/labelstudio/543.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

web/dist/apps/labelstudio/543.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

web/dist/apps/labelstudio/main.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

web/dist/apps/labelstudio/main.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

web/dist/apps/labelstudio/vendor.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

web/dist/apps/labelstudio/vendor.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"message": "refactor: OPTIC-1518: remove a batch of ff (#6892)",
3-
"commit": "3c03818a813dfcd19fd5a0600a8933c84405507a",
4-
"date": "2025-01-13T17:05:01.000Z",
2+
"message": "feat: OPTIC-1417: Improve projects list API usage with frontend query cache. (#6844)",
3+
"commit": "62e08b0f79eb7ff27712b4031e09e1c2a796b3b4",
4+
"date": "2025-01-14T13:56:44.000Z",
55
"branch": "develop"
66
}
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"message": "refactor: OPTIC-1518: remove a batch of ff (#6892)",
3-
"commit": "3c03818a813dfcd19fd5a0600a8933c84405507a",
4-
"date": "2025-01-13T17:05:01.000Z",
2+
"message": "feat: OPTIC-1417: Improve projects list API usage with frontend query cache. (#6844)",
3+
"commit": "62e08b0f79eb7ff27712b4031e09e1c2a796b3b4",
4+
"date": "2025-01-14T13:56:44.000Z",
55
"branch": "develop"
66
}

web/dist/libs/editor/version.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"message": "refactor: OPTIC-1518: remove a batch of ff (#6892)",
3-
"commit": "3c03818a813dfcd19fd5a0600a8933c84405507a",
4-
"date": "2025-01-13T17:05:01.000Z",
2+
"message": "feat: OPTIC-1417: Improve projects list API usage with frontend query cache. (#6844)",
3+
"commit": "62e08b0f79eb7ff27712b4031e09e1c2a796b3b4",
4+
"date": "2025-01-14T13:56:44.000Z",
55
"branch": "develop"
66
}

web/libs/datamanager/src/components/DataManager/Toolbar/ActionsButton.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export const ActionsButton = injector(
4949
onOk() {
5050
const body = formRef.current?.assembleFormData({ asJSON: true });
5151

52+
store.SDK.invoke("actionDialogOk", action.id, { body });
5253
store.invokeAction(action.id, { body });
5354
},
5455
});

0 commit comments

Comments
 (0)