zou 0.19.15__py3-none-any.whl → 0.20.11__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- zou/__init__.py +1 -1
- zou/app/__init__.py +10 -2
- zou/app/api.py +2 -0
- zou/app/blueprints/assets/__init__.py +22 -0
- zou/app/blueprints/assets/resources.py +241 -4
- zou/app/blueprints/auth/__init__.py +4 -0
- zou/app/blueprints/auth/resources.py +154 -22
- zou/app/blueprints/breakdown/resources.py +4 -4
- zou/app/blueprints/chats/__init__.py +22 -0
- zou/app/blueprints/chats/resources.py +199 -0
- zou/app/blueprints/comments/resources.py +36 -19
- zou/app/blueprints/crud/__init__.py +12 -0
- zou/app/blueprints/crud/attachment_file.py +14 -5
- zou/app/blueprints/crud/base.py +29 -28
- zou/app/blueprints/crud/chat.py +13 -0
- zou/app/blueprints/crud/chat_message.py +13 -0
- zou/app/blueprints/crud/comments.py +85 -29
- zou/app/blueprints/crud/custom_action.py +1 -1
- zou/app/blueprints/crud/day_off.py +47 -9
- zou/app/blueprints/crud/department.py +1 -25
- zou/app/blueprints/crud/entity.py +46 -5
- zou/app/blueprints/crud/entity_type.py +13 -1
- zou/app/blueprints/crud/event.py +1 -1
- zou/app/blueprints/crud/file_status.py +1 -1
- zou/app/blueprints/crud/metadata_descriptor.py +24 -10
- zou/app/blueprints/crud/organisation.py +22 -5
- zou/app/blueprints/crud/output_file.py +1 -1
- zou/app/blueprints/crud/output_type.py +1 -1
- zou/app/blueprints/crud/person.py +32 -24
- zou/app/blueprints/crud/playlist.py +1 -1
- zou/app/blueprints/crud/preview_background_file.py +6 -7
- zou/app/blueprints/crud/preview_file.py +1 -1
- zou/app/blueprints/crud/project.py +14 -6
- zou/app/blueprints/crud/project_status.py +1 -1
- zou/app/blueprints/crud/schedule_item.py +4 -2
- zou/app/blueprints/crud/software.py +1 -1
- zou/app/blueprints/crud/status_automation.py +1 -1
- zou/app/blueprints/crud/studio.py +33 -0
- zou/app/blueprints/crud/task.py +47 -3
- zou/app/blueprints/crud/task_status.py +1 -1
- zou/app/blueprints/crud/task_type.py +4 -4
- zou/app/blueprints/crud/working_file.py +4 -8
- zou/app/blueprints/events/resources.py +13 -12
- zou/app/blueprints/export/csv/assets.py +15 -6
- zou/app/blueprints/export/csv/edits.py +15 -5
- zou/app/blueprints/export/csv/playlists.py +1 -1
- zou/app/blueprints/export/csv/shots.py +15 -5
- zou/app/blueprints/export/csv/time_spents.py +1 -1
- zou/app/blueprints/files/resources.py +22 -23
- zou/app/blueprints/index/resources.py +38 -29
- zou/app/blueprints/news/resources.py +25 -11
- zou/app/blueprints/persons/__init__.py +5 -2
- zou/app/blueprints/persons/resources.py +126 -120
- zou/app/blueprints/previews/__init__.py +18 -8
- zou/app/blueprints/previews/resources.py +569 -328
- zou/app/blueprints/projects/resources.py +1 -1
- zou/app/blueprints/search/resources.py +18 -6
- zou/app/blueprints/shots/__init__.py +5 -0
- zou/app/blueprints/shots/resources.py +134 -4
- zou/app/blueprints/source/__init__.py +6 -6
- zou/app/blueprints/source/csv/assets.py +10 -3
- zou/app/blueprints/source/csv/base.py +1 -1
- zou/app/blueprints/source/csv/edits.py +10 -3
- zou/app/blueprints/source/csv/shots.py +10 -3
- zou/app/blueprints/source/{edl.py → otio.py} +82 -41
- zou/app/blueprints/tasks/__init__.py +3 -2
- zou/app/blueprints/tasks/resources.py +83 -52
- zou/app/blueprints/user/__init__.py +9 -0
- zou/app/blueprints/user/resources.py +170 -12
- zou/app/config.py +10 -0
- zou/app/mixin.py +6 -5
- zou/app/models/attachment_file.py +10 -4
- zou/app/models/base.py +18 -13
- zou/app/models/build_job.py +7 -4
- zou/app/models/chat.py +44 -0
- zou/app/models/chat_message.py +37 -0
- zou/app/models/comment.py +1 -0
- zou/app/models/day_off.py +3 -0
- zou/app/models/entity.py +4 -6
- zou/app/models/entity_type.py +2 -0
- zou/app/models/organisation.py +14 -15
- zou/app/models/person.py +6 -1
- zou/app/models/project.py +3 -0
- zou/app/models/search_filter.py +11 -0
- zou/app/models/search_filter_group.py +10 -0
- zou/app/models/serializer.py +17 -17
- zou/app/models/status_automation.py +2 -0
- zou/app/models/studio.py +13 -0
- zou/app/models/subscription.py +2 -2
- zou/app/models/task.py +6 -1
- zou/app/models/task_status.py +1 -0
- zou/app/models/task_type.py +1 -0
- zou/app/models/working_file.py +1 -1
- zou/app/services/assets_service.py +101 -14
- zou/app/services/auth_service.py +17 -44
- zou/app/services/breakdown_service.py +37 -5
- zou/app/services/chats_service.py +279 -0
- zou/app/services/comments_service.py +110 -65
- zou/app/services/concepts_service.py +4 -12
- zou/app/services/deletion_service.py +43 -30
- zou/app/services/edits_service.py +5 -11
- zou/app/services/emails_service.py +4 -4
- zou/app/services/entities_service.py +17 -2
- zou/app/services/events_service.py +12 -4
- zou/app/services/exception.py +5 -5
- zou/app/services/names_service.py +7 -2
- zou/app/services/news_service.py +17 -9
- zou/app/services/persons_service.py +38 -21
- zou/app/services/playlists_service.py +8 -7
- zou/app/services/preview_files_service.py +137 -10
- zou/app/services/projects_service.py +5 -14
- zou/app/services/shots_service.py +221 -49
- zou/app/services/sync_service.py +46 -42
- zou/app/services/tasks_service.py +185 -46
- zou/app/services/time_spents_service.py +67 -20
- zou/app/services/user_service.py +350 -107
- zou/app/stores/auth_tokens_store.py +2 -1
- zou/app/stores/file_store.py +18 -0
- zou/app/stores/publisher_store.py +7 -7
- zou/app/stores/queue_store.py +1 -0
- zou/app/swagger.py +36 -20
- zou/app/utils/cache.py +2 -0
- zou/app/utils/commands.py +104 -7
- zou/app/utils/csv_utils.py +1 -4
- zou/app/utils/date_helpers.py +33 -17
- zou/app/utils/dbhelpers.py +14 -1
- zou/app/utils/emails.py +2 -2
- zou/app/utils/fido.py +22 -0
- zou/app/utils/query.py +54 -6
- zou/app/utils/redis.py +11 -0
- zou/app/utils/saml.py +51 -0
- zou/app/utils/string.py +2 -0
- zou/app/utils/thumbnail.py +4 -2
- zou/cli.py +76 -18
- zou/debug.py +4 -2
- zou/event_stream.py +122 -165
- zou/job_settings.py +1 -0
- zou/migrations/env.py +0 -0
- zou/migrations/utils/base.py +6 -6
- zou/migrations/versions/1bb55759146f_add_table_studio.py +67 -0
- zou/migrations/versions/1fab8c420678_add_attachments_to_message_chats.py +56 -0
- zou/migrations/versions/23122f290ca2_add_entity_chat_models.py +149 -0
- zou/migrations/versions/32f134ff1201_add_is_shared_flag_to_filters.py +33 -0
- zou/migrations/versions/57222395f2be_add_statusautomation_import_last_revision.py +41 -0
- zou/migrations/versions/59a7445a966c_add_entity_is_shared.py +41 -0
- zou/migrations/versions/5b980f0dc365_add_comment_links.py +35 -0
- zou/migrations/versions/680c64565f9d_for_searchfiltergroup_is_shared.py +35 -0
- zou/migrations/versions/8e67c183bed7_add_preference_fields.py +71 -0
- zou/migrations/versions/92b40d79ad3f_allow_message_attachments.py +38 -0
- zou/migrations/versions/971dbf5a0faf_add_short_name_for_asset_type_entity_.py +33 -0
- zou/migrations/versions/9b85c14fa8a7_add_day_off_new_columns.py +68 -0
- zou/migrations/versions/9d3bb33c6fc6_add_department_keys_to_filter_models.py +73 -0
- zou/migrations/versions/a252a094e977_add_descriptions_for_entities_tasks_and_.py +40 -0
- zou/migrations/versions/be56dc0fb760_for_is_shared_disallow_nullable.py +102 -0
- zou/migrations/versions/ca28796a2a62_add_is_done_field_to_the_task_model.py +108 -0
- zou/migrations/versions/f344b867a911_for_description_of_entity_task_working_.py +75 -0
- zou/remote/config_payload.py +2 -1
- zou/utils/movie.py +14 -4
- {zou-0.19.15.dist-info → zou-0.20.11.dist-info}/METADATA +75 -69
- {zou-0.19.15.dist-info → zou-0.20.11.dist-info}/RECORD +163 -134
- {zou-0.19.15.dist-info → zou-0.20.11.dist-info}/WHEEL +1 -1
- {zou-0.19.15.dist-info → zou-0.20.11.dist-info}/LICENSE +0 -0
- {zou-0.19.15.dist-info → zou-0.20.11.dist-info}/entry_points.txt +0 -0
- {zou-0.19.15.dist-info → zou-0.20.11.dist-info}/top_level.txt +0 -0
|
@@ -4,6 +4,7 @@ import orjson as json
|
|
|
4
4
|
import os
|
|
5
5
|
import zlib
|
|
6
6
|
|
|
7
|
+
from flask import current_app
|
|
7
8
|
from operator import itemgetter
|
|
8
9
|
from pathlib import Path
|
|
9
10
|
from shutil import copyfile
|
|
@@ -781,13 +782,13 @@ def remove_build_job(playlist, build_job_id):
|
|
|
781
782
|
movie_file_path = get_playlist_movie_file_path(job.serialize())
|
|
782
783
|
if os.path.exists(movie_file_path):
|
|
783
784
|
os.remove(movie_file_path)
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
785
|
+
if config.REMOVE_FILES:
|
|
786
|
+
try:
|
|
787
|
+
file_store.remove_movie("playlists", build_job_id)
|
|
788
|
+
except Exception:
|
|
789
|
+
current_app.logger.error(
|
|
790
|
+
"Playlist file can't be deleted: %s" % build_job_id
|
|
791
|
+
)
|
|
791
792
|
job.delete()
|
|
792
793
|
events.emit(
|
|
793
794
|
"build-job:delete",
|
|
@@ -4,6 +4,7 @@ import re
|
|
|
4
4
|
import time
|
|
5
5
|
|
|
6
6
|
import ffmpeg
|
|
7
|
+
import shutil
|
|
7
8
|
|
|
8
9
|
from sqlalchemy.orm import aliased
|
|
9
10
|
from sqlalchemy.orm.exc import ObjectDeletedError
|
|
@@ -33,7 +34,7 @@ from zou.app.utils import (
|
|
|
33
34
|
thumbnail as thumbnail_utils,
|
|
34
35
|
)
|
|
35
36
|
from zou.app.services.exception import (
|
|
36
|
-
|
|
37
|
+
WrongParameterException,
|
|
37
38
|
PreviewFileNotFoundException,
|
|
38
39
|
ProjectNotFoundException,
|
|
39
40
|
EpisodeNotFoundException,
|
|
@@ -88,7 +89,7 @@ def _is_valid_partial_resolution(resolution):
|
|
|
88
89
|
return resolution is not None and bool(re.match(r"x\d{3,4}", resolution))
|
|
89
90
|
|
|
90
91
|
|
|
91
|
-
def get_preview_file_fps(project):
|
|
92
|
+
def get_preview_file_fps(project, entity=None):
|
|
92
93
|
"""
|
|
93
94
|
Return fps set at project level or default fps if the dimensions are not
|
|
94
95
|
set.
|
|
@@ -96,6 +97,12 @@ def get_preview_file_fps(project):
|
|
|
96
97
|
fps = "25.00"
|
|
97
98
|
if project.get("fps", None) is not None:
|
|
98
99
|
fps = project["fps"].replace(",", ".")
|
|
100
|
+
|
|
101
|
+
if entity is not None:
|
|
102
|
+
entity_data = entity.get("data", {}) or {}
|
|
103
|
+
if entity_data.get("fps", None):
|
|
104
|
+
fps = str(entity_data["fps"]).replace(",", ".")
|
|
105
|
+
|
|
99
106
|
return "%.3f" % float(fps)
|
|
100
107
|
|
|
101
108
|
|
|
@@ -162,7 +169,10 @@ def set_preview_file_as_ready(preview_file_id):
|
|
|
162
169
|
|
|
163
170
|
|
|
164
171
|
def prepare_and_store_movie(
|
|
165
|
-
preview_file_id,
|
|
172
|
+
preview_file_id,
|
|
173
|
+
uploaded_movie_path,
|
|
174
|
+
normalize=True,
|
|
175
|
+
add_source_to_file_store=True,
|
|
166
176
|
):
|
|
167
177
|
"""
|
|
168
178
|
Prepare movie preview, normalize the movie as a .mp4, build the thumbnails
|
|
@@ -171,6 +181,10 @@ def prepare_and_store_movie(
|
|
|
171
181
|
from zou.app import app as current_app
|
|
172
182
|
|
|
173
183
|
with current_app.app_context():
|
|
184
|
+
if add_source_to_file_store:
|
|
185
|
+
file_store.add_movie(
|
|
186
|
+
"source", preview_file_id, uploaded_movie_path
|
|
187
|
+
)
|
|
174
188
|
preview_file_raw = files_service.get_preview_file_raw(preview_file_id)
|
|
175
189
|
normalized_movie_low_path = None
|
|
176
190
|
try:
|
|
@@ -189,7 +203,7 @@ def prepare_and_store_movie(
|
|
|
189
203
|
preview_file = set_preview_file_as_broken(preview_file_id)
|
|
190
204
|
return preview_file
|
|
191
205
|
|
|
192
|
-
fps = get_preview_file_fps(project)
|
|
206
|
+
fps = get_preview_file_fps(project, entity)
|
|
193
207
|
(width, height) = get_preview_file_dimensions(project, entity)
|
|
194
208
|
|
|
195
209
|
if normalize:
|
|
@@ -199,9 +213,6 @@ def prepare_and_store_movie(
|
|
|
199
213
|
config.ENABLE_JOB_QUEUE_REMOTE
|
|
200
214
|
and len(config.JOB_QUEUE_NOMAD_NORMALIZE_JOB) > 0
|
|
201
215
|
):
|
|
202
|
-
file_store.add_movie(
|
|
203
|
-
"source", preview_file_id, uploaded_movie_path
|
|
204
|
-
)
|
|
205
216
|
result = _run_remote_normalize_movie(
|
|
206
217
|
current_app, preview_file_id, fps, width, height
|
|
207
218
|
)
|
|
@@ -556,7 +567,7 @@ def get_running_preview_files():
|
|
|
556
567
|
result = preview_file.serialize()
|
|
557
568
|
result["project_id"] = fields.serialize_value(project_id)
|
|
558
569
|
result["task_type_id"] = fields.serialize_value(task_type_id)
|
|
559
|
-
|
|
570
|
+
result["full_entity_name"], _, _ = names_service.get_full_entity_name(
|
|
560
571
|
entity_id
|
|
561
572
|
)
|
|
562
573
|
results.append(result)
|
|
@@ -614,7 +625,9 @@ def extract_frame_from_preview_file(preview_file, frame_number):
|
|
|
614
625
|
else:
|
|
615
626
|
raise PreviewFileNotFoundException
|
|
616
627
|
|
|
617
|
-
fps = get_preview_file_fps(
|
|
628
|
+
fps = get_preview_file_fps(
|
|
629
|
+
project, get_entity_from_preview_file(preview_file["id"])
|
|
630
|
+
)
|
|
618
631
|
extracted_frame_path = movie.extract_frame_from_movie(
|
|
619
632
|
preview_file_path, frame_number, fps
|
|
620
633
|
)
|
|
@@ -645,7 +658,7 @@ def extract_tile_from_preview_file(preview_file):
|
|
|
645
658
|
extracted_tile_path = movie.generate_tile(preview_file_path)
|
|
646
659
|
return extracted_tile_path
|
|
647
660
|
else:
|
|
648
|
-
|
|
661
|
+
raise WrongParameterException("Preview file is not a movie")
|
|
649
662
|
|
|
650
663
|
|
|
651
664
|
def reset_movie_files_metadata():
|
|
@@ -940,3 +953,117 @@ def _reset_preview_file_metadata(
|
|
|
940
953
|
print(
|
|
941
954
|
f"Failed to store information for preview file {preview_file.id}: {e}.",
|
|
942
955
|
)
|
|
956
|
+
|
|
957
|
+
|
|
958
|
+
def copy_preview_file_on_storage(
|
|
959
|
+
get_path_func,
|
|
960
|
+
exists_func,
|
|
961
|
+
copy_func,
|
|
962
|
+
prefix,
|
|
963
|
+
original_preview_file_id,
|
|
964
|
+
preview_file_to_update_id,
|
|
965
|
+
):
|
|
966
|
+
if config.FS_BACKEND == "local":
|
|
967
|
+
file_path = get_path_func(prefix, original_preview_file_id)
|
|
968
|
+
other_file_path = get_path_func(prefix, preview_file_to_update_id)
|
|
969
|
+
if os.path.exists(file_path):
|
|
970
|
+
os.makedirs(os.path.dirname(other_file_path), exist_ok=True)
|
|
971
|
+
shutil.copyfile(file_path, other_file_path)
|
|
972
|
+
elif exists_func(prefix, original_preview_file_id):
|
|
973
|
+
copy_func(
|
|
974
|
+
prefix, original_preview_file_id, prefix, preview_file_to_update_id
|
|
975
|
+
)
|
|
976
|
+
|
|
977
|
+
|
|
978
|
+
def copy_preview_file_in_another_one(
|
|
979
|
+
original_preview_file_id, preview_file_to_update_id
|
|
980
|
+
):
|
|
981
|
+
"""
|
|
982
|
+
Copy preview file data/files from one preview file to another one.
|
|
983
|
+
"""
|
|
984
|
+
original_preview_file = files_service.get_preview_file(
|
|
985
|
+
original_preview_file_id
|
|
986
|
+
)
|
|
987
|
+
is_movie = original_preview_file["extension"] == "mp4"
|
|
988
|
+
is_picture = original_preview_file["extension"] == "png"
|
|
989
|
+
|
|
990
|
+
if is_movie:
|
|
991
|
+
prefixes = ["previews", "lowdef"]
|
|
992
|
+
for prefix in prefixes:
|
|
993
|
+
copy_preview_file_on_storage(
|
|
994
|
+
file_store.get_local_movie_path,
|
|
995
|
+
file_store.exists_movie,
|
|
996
|
+
file_store.copy_movie,
|
|
997
|
+
prefix,
|
|
998
|
+
original_preview_file_id,
|
|
999
|
+
preview_file_to_update_id,
|
|
1000
|
+
)
|
|
1001
|
+
|
|
1002
|
+
if is_movie or is_picture:
|
|
1003
|
+
prefixes = [
|
|
1004
|
+
"previews",
|
|
1005
|
+
"original",
|
|
1006
|
+
"thumbnails",
|
|
1007
|
+
"thumbnails-square",
|
|
1008
|
+
]
|
|
1009
|
+
if is_movie:
|
|
1010
|
+
prefixes.append("tiles")
|
|
1011
|
+
|
|
1012
|
+
for prefix in prefixes:
|
|
1013
|
+
copy_preview_file_on_storage(
|
|
1014
|
+
file_store.get_local_picture_path,
|
|
1015
|
+
file_store.exists_picture,
|
|
1016
|
+
file_store.copy_picture,
|
|
1017
|
+
prefix,
|
|
1018
|
+
original_preview_file_id,
|
|
1019
|
+
preview_file_to_update_id,
|
|
1020
|
+
)
|
|
1021
|
+
else:
|
|
1022
|
+
copy_preview_file_on_storage(
|
|
1023
|
+
file_store.get_local_file_path,
|
|
1024
|
+
file_store.exists_file,
|
|
1025
|
+
file_store.copy_file,
|
|
1026
|
+
"previews",
|
|
1027
|
+
original_preview_file_id,
|
|
1028
|
+
preview_file_to_update_id,
|
|
1029
|
+
)
|
|
1030
|
+
|
|
1031
|
+
preview_file_to_update = update_preview_file(
|
|
1032
|
+
preview_file_to_update_id,
|
|
1033
|
+
{
|
|
1034
|
+
"extension": original_preview_file["extension"],
|
|
1035
|
+
"original_name": original_preview_file["original_name"],
|
|
1036
|
+
"status": original_preview_file["status"],
|
|
1037
|
+
"file_size": original_preview_file["file_size"],
|
|
1038
|
+
"width": original_preview_file["width"],
|
|
1039
|
+
"height": original_preview_file["height"],
|
|
1040
|
+
"duration": original_preview_file["duration"],
|
|
1041
|
+
},
|
|
1042
|
+
)
|
|
1043
|
+
tasks_service.update_preview_file_info(preview_file_to_update)
|
|
1044
|
+
comment = tasks_service.get_comment_by_preview_file_id(
|
|
1045
|
+
preview_file_to_update_id
|
|
1046
|
+
)
|
|
1047
|
+
task = tasks_service.get_task(preview_file_to_update["task_id"])
|
|
1048
|
+
comment_id = None
|
|
1049
|
+
if comment is not None:
|
|
1050
|
+
comment_id = comment["id"]
|
|
1051
|
+
events.emit(
|
|
1052
|
+
"comment:update",
|
|
1053
|
+
{"comment_id": comment_id},
|
|
1054
|
+
project_id=task["project_id"],
|
|
1055
|
+
)
|
|
1056
|
+
events.emit(
|
|
1057
|
+
"preview-file:add-file",
|
|
1058
|
+
{
|
|
1059
|
+
"comment_id": comment_id,
|
|
1060
|
+
"task_id": preview_file_to_update["task_id"],
|
|
1061
|
+
"preview_file_id": preview_file_to_update["id"],
|
|
1062
|
+
"revision": preview_file_to_update["revision"],
|
|
1063
|
+
"extension": preview_file_to_update["extension"],
|
|
1064
|
+
"status": preview_file_to_update["status"],
|
|
1065
|
+
},
|
|
1066
|
+
project_id=task["project_id"],
|
|
1067
|
+
)
|
|
1068
|
+
|
|
1069
|
+
return preview_file_to_update
|
|
@@ -38,7 +38,7 @@ from sqlalchemy import or_
|
|
|
38
38
|
|
|
39
39
|
def clear_project_cache(project_id):
|
|
40
40
|
cache.cache.delete_memoized(get_project, project_id)
|
|
41
|
-
cache.cache.delete_memoized(
|
|
41
|
+
cache.cache.delete_memoized(get_project, project_id, True)
|
|
42
42
|
cache.cache.delete_memoized(get_project_by_name)
|
|
43
43
|
cache.cache.delete_memoized(open_projects)
|
|
44
44
|
|
|
@@ -259,21 +259,12 @@ def get_project_raw(project_id):
|
|
|
259
259
|
|
|
260
260
|
|
|
261
261
|
@cache.memoize_function(240)
|
|
262
|
-
def get_project(project_id):
|
|
262
|
+
def get_project(project_id, relations=False):
|
|
263
263
|
"""
|
|
264
264
|
Get project matching given id, as a dict. Raises an exception if project is
|
|
265
265
|
not found.
|
|
266
266
|
"""
|
|
267
|
-
return get_project_raw(project_id).serialize()
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
@cache.memoize_function(240)
|
|
271
|
-
def get_project_with_relations(project_id):
|
|
272
|
-
"""
|
|
273
|
-
Get project matching given id, as a dict. Raises an exception if project is
|
|
274
|
-
not found.
|
|
275
|
-
"""
|
|
276
|
-
return get_project_raw(project_id).serialize(relations=True)
|
|
267
|
+
return get_project_raw(project_id).serialize(relations=relations)
|
|
277
268
|
|
|
278
269
|
|
|
279
270
|
@cache.memoize_function(120)
|
|
@@ -487,7 +478,7 @@ def add_metadata_descriptor(
|
|
|
487
478
|
field_name=slugify.slugify(name, separator="_"),
|
|
488
479
|
)
|
|
489
480
|
except Exception:
|
|
490
|
-
raise WrongParameterException
|
|
481
|
+
raise WrongParameterException("Metadata descriptor already exists.")
|
|
491
482
|
events.emit(
|
|
492
483
|
"metadata-descriptor:new",
|
|
493
484
|
{"metadata_descriptor_id": str(descriptor.id)},
|
|
@@ -687,7 +678,7 @@ def get_project_fps(project_id):
|
|
|
687
678
|
Return fps set at project level or default fps if it not set.
|
|
688
679
|
"""
|
|
689
680
|
project = get_project(project_id)
|
|
690
|
-
return float(project["fps"] or "
|
|
681
|
+
return float(project["fps"] or "25.00")
|
|
691
682
|
|
|
692
683
|
|
|
693
684
|
def get_task_type_priority_map(project_id, for_entity="Asset"):
|