zou 0.19.14__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.
Files changed (165) hide show
  1. zou/__init__.py +1 -1
  2. zou/app/__init__.py +10 -2
  3. zou/app/api.py +2 -0
  4. zou/app/blueprints/assets/__init__.py +22 -0
  5. zou/app/blueprints/assets/resources.py +241 -4
  6. zou/app/blueprints/auth/__init__.py +4 -0
  7. zou/app/blueprints/auth/resources.py +154 -22
  8. zou/app/blueprints/breakdown/resources.py +4 -4
  9. zou/app/blueprints/chats/__init__.py +22 -0
  10. zou/app/blueprints/chats/resources.py +199 -0
  11. zou/app/blueprints/comments/resources.py +36 -19
  12. zou/app/blueprints/crud/__init__.py +12 -0
  13. zou/app/blueprints/crud/attachment_file.py +14 -5
  14. zou/app/blueprints/crud/base.py +29 -28
  15. zou/app/blueprints/crud/chat.py +13 -0
  16. zou/app/blueprints/crud/chat_message.py +13 -0
  17. zou/app/blueprints/crud/comments.py +85 -29
  18. zou/app/blueprints/crud/custom_action.py +1 -1
  19. zou/app/blueprints/crud/day_off.py +47 -9
  20. zou/app/blueprints/crud/department.py +1 -25
  21. zou/app/blueprints/crud/entity.py +46 -5
  22. zou/app/blueprints/crud/entity_type.py +13 -1
  23. zou/app/blueprints/crud/event.py +1 -1
  24. zou/app/blueprints/crud/file_status.py +1 -1
  25. zou/app/blueprints/crud/metadata_descriptor.py +24 -10
  26. zou/app/blueprints/crud/organisation.py +22 -5
  27. zou/app/blueprints/crud/output_file.py +1 -1
  28. zou/app/blueprints/crud/output_type.py +1 -1
  29. zou/app/blueprints/crud/person.py +32 -24
  30. zou/app/blueprints/crud/playlist.py +1 -1
  31. zou/app/blueprints/crud/preview_background_file.py +6 -7
  32. zou/app/blueprints/crud/preview_file.py +1 -1
  33. zou/app/blueprints/crud/project.py +14 -6
  34. zou/app/blueprints/crud/project_status.py +1 -1
  35. zou/app/blueprints/crud/schedule_item.py +4 -2
  36. zou/app/blueprints/crud/software.py +1 -1
  37. zou/app/blueprints/crud/status_automation.py +1 -1
  38. zou/app/blueprints/crud/studio.py +33 -0
  39. zou/app/blueprints/crud/task.py +47 -3
  40. zou/app/blueprints/crud/task_status.py +1 -1
  41. zou/app/blueprints/crud/task_type.py +4 -4
  42. zou/app/blueprints/crud/working_file.py +4 -8
  43. zou/app/blueprints/events/resources.py +13 -12
  44. zou/app/blueprints/export/csv/assets.py +15 -6
  45. zou/app/blueprints/export/csv/edits.py +15 -5
  46. zou/app/blueprints/export/csv/playlists.py +1 -1
  47. zou/app/blueprints/export/csv/shots.py +15 -5
  48. zou/app/blueprints/export/csv/time_spents.py +1 -1
  49. zou/app/blueprints/files/resources.py +22 -23
  50. zou/app/blueprints/index/resources.py +38 -29
  51. zou/app/blueprints/news/resources.py +25 -11
  52. zou/app/blueprints/persons/__init__.py +5 -2
  53. zou/app/blueprints/persons/resources.py +126 -120
  54. zou/app/blueprints/previews/__init__.py +18 -8
  55. zou/app/blueprints/previews/resources.py +569 -328
  56. zou/app/blueprints/projects/resources.py +1 -1
  57. zou/app/blueprints/search/resources.py +18 -6
  58. zou/app/blueprints/shots/__init__.py +5 -0
  59. zou/app/blueprints/shots/resources.py +134 -4
  60. zou/app/blueprints/source/__init__.py +6 -6
  61. zou/app/blueprints/source/csv/assets.py +10 -3
  62. zou/app/blueprints/source/csv/base.py +1 -1
  63. zou/app/blueprints/source/csv/edits.py +10 -3
  64. zou/app/blueprints/source/csv/shots.py +10 -3
  65. zou/app/blueprints/source/{edl.py → otio.py} +84 -41
  66. zou/app/blueprints/tasks/__init__.py +3 -2
  67. zou/app/blueprints/tasks/resources.py +83 -52
  68. zou/app/blueprints/user/__init__.py +9 -0
  69. zou/app/blueprints/user/resources.py +170 -12
  70. zou/app/config.py +10 -0
  71. zou/app/mixin.py +6 -5
  72. zou/app/models/attachment_file.py +10 -4
  73. zou/app/models/base.py +18 -13
  74. zou/app/models/build_job.py +7 -4
  75. zou/app/models/chat.py +44 -0
  76. zou/app/models/chat_message.py +37 -0
  77. zou/app/models/comment.py +1 -0
  78. zou/app/models/day_off.py +3 -0
  79. zou/app/models/entity.py +4 -6
  80. zou/app/models/entity_type.py +2 -0
  81. zou/app/models/organisation.py +14 -15
  82. zou/app/models/person.py +6 -1
  83. zou/app/models/project.py +3 -0
  84. zou/app/models/search_filter.py +11 -0
  85. zou/app/models/search_filter_group.py +10 -0
  86. zou/app/models/serializer.py +17 -17
  87. zou/app/models/status_automation.py +2 -0
  88. zou/app/models/studio.py +13 -0
  89. zou/app/models/subscription.py +2 -2
  90. zou/app/models/task.py +6 -1
  91. zou/app/models/task_status.py +1 -0
  92. zou/app/models/task_type.py +1 -0
  93. zou/app/models/working_file.py +1 -1
  94. zou/app/services/assets_service.py +101 -14
  95. zou/app/services/auth_service.py +17 -44
  96. zou/app/services/breakdown_service.py +37 -5
  97. zou/app/services/chats_service.py +279 -0
  98. zou/app/services/comments_service.py +110 -65
  99. zou/app/services/concepts_service.py +4 -12
  100. zou/app/services/deletion_service.py +43 -30
  101. zou/app/services/edits_service.py +5 -11
  102. zou/app/services/emails_service.py +4 -4
  103. zou/app/services/entities_service.py +17 -2
  104. zou/app/services/events_service.py +12 -4
  105. zou/app/services/exception.py +5 -5
  106. zou/app/services/names_service.py +7 -2
  107. zou/app/services/news_service.py +17 -9
  108. zou/app/services/persons_service.py +38 -21
  109. zou/app/services/playlists_service.py +8 -7
  110. zou/app/services/preview_files_service.py +137 -10
  111. zou/app/services/projects_service.py +5 -14
  112. zou/app/services/shots_service.py +221 -49
  113. zou/app/services/sync_service.py +46 -42
  114. zou/app/services/tasks_service.py +185 -46
  115. zou/app/services/time_spents_service.py +67 -20
  116. zou/app/services/user_service.py +350 -107
  117. zou/app/stores/auth_tokens_store.py +2 -1
  118. zou/app/stores/file_store.py +18 -0
  119. zou/app/stores/publisher_store.py +7 -7
  120. zou/app/stores/queue_store.py +1 -0
  121. zou/app/swagger.py +36 -20
  122. zou/app/utils/cache.py +2 -0
  123. zou/app/utils/commands.py +104 -7
  124. zou/app/utils/csv_utils.py +1 -4
  125. zou/app/utils/date_helpers.py +33 -17
  126. zou/app/utils/dbhelpers.py +14 -1
  127. zou/app/utils/emails.py +2 -2
  128. zou/app/utils/fido.py +22 -0
  129. zou/app/utils/flask.py +1 -0
  130. zou/app/utils/query.py +54 -6
  131. zou/app/utils/redis.py +11 -0
  132. zou/app/utils/saml.py +51 -0
  133. zou/app/utils/string.py +2 -0
  134. zou/app/utils/thumbnail.py +4 -2
  135. zou/cli.py +76 -18
  136. zou/debug.py +4 -2
  137. zou/event_stream.py +122 -165
  138. zou/job_settings.py +1 -0
  139. zou/migrations/env.py +0 -0
  140. zou/migrations/utils/base.py +6 -6
  141. zou/migrations/versions/1bb55759146f_add_table_studio.py +67 -0
  142. zou/migrations/versions/1fab8c420678_add_attachments_to_message_chats.py +56 -0
  143. zou/migrations/versions/23122f290ca2_add_entity_chat_models.py +149 -0
  144. zou/migrations/versions/32f134ff1201_add_is_shared_flag_to_filters.py +33 -0
  145. zou/migrations/versions/57222395f2be_add_statusautomation_import_last_revision.py +41 -0
  146. zou/migrations/versions/59a7445a966c_add_entity_is_shared.py +41 -0
  147. zou/migrations/versions/5b980f0dc365_add_comment_links.py +35 -0
  148. zou/migrations/versions/680c64565f9d_for_searchfiltergroup_is_shared.py +35 -0
  149. zou/migrations/versions/8e67c183bed7_add_preference_fields.py +71 -0
  150. zou/migrations/versions/92b40d79ad3f_allow_message_attachments.py +38 -0
  151. zou/migrations/versions/971dbf5a0faf_add_short_name_for_asset_type_entity_.py +33 -0
  152. zou/migrations/versions/9b85c14fa8a7_add_day_off_new_columns.py +68 -0
  153. zou/migrations/versions/9d3bb33c6fc6_add_department_keys_to_filter_models.py +73 -0
  154. zou/migrations/versions/a252a094e977_add_descriptions_for_entities_tasks_and_.py +40 -0
  155. zou/migrations/versions/be56dc0fb760_for_is_shared_disallow_nullable.py +102 -0
  156. zou/migrations/versions/ca28796a2a62_add_is_done_field_to_the_task_model.py +108 -0
  157. zou/migrations/versions/f344b867a911_for_description_of_entity_task_working_.py +75 -0
  158. zou/remote/config_payload.py +2 -1
  159. zou/utils/movie.py +14 -4
  160. {zou-0.19.14.dist-info → zou-0.20.11.dist-info}/METADATA +75 -69
  161. {zou-0.19.14.dist-info → zou-0.20.11.dist-info}/RECORD +164 -135
  162. {zou-0.19.14.dist-info → zou-0.20.11.dist-info}/WHEEL +1 -1
  163. {zou-0.19.14.dist-info → zou-0.20.11.dist-info}/LICENSE +0 -0
  164. {zou-0.19.14.dist-info → zou-0.20.11.dist-info}/entry_points.txt +0 -0
  165. {zou-0.19.14.dist-info → zou-0.20.11.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,73 @@
1
+ """add department keys to filter models
2
+
3
+ Revision ID: 9d3bb33c6fc6
4
+ Revises: 8e67c183bed7
5
+ Create Date: 2024-10-11 15:52:29.740222
6
+
7
+ """
8
+
9
+ from alembic import op
10
+ import sqlalchemy as sa
11
+ import sqlalchemy_utils
12
+ import sqlalchemy_utils
13
+ import uuid
14
+
15
+ # revision identifiers, used by Alembic.
16
+ revision = "9d3bb33c6fc6"
17
+ down_revision = "8e67c183bed7"
18
+ branch_labels = None
19
+ depends_on = None
20
+
21
+
22
+ def upgrade():
23
+ # ### commands auto generated by Alembic - please adjust! ###
24
+ with op.batch_alter_table("search_filter", schema=None) as batch_op:
25
+ batch_op.add_column(
26
+ sa.Column(
27
+ "department_id",
28
+ sqlalchemy_utils.types.uuid.UUIDType(binary=False),
29
+ default=uuid.uuid4,
30
+ nullable=True,
31
+ )
32
+ )
33
+ batch_op.create_foreign_key(
34
+ "search_filter_department_id_fkey",
35
+ "department",
36
+ ["department_id"],
37
+ ["id"],
38
+ )
39
+
40
+ with op.batch_alter_table("search_filter_group", schema=None) as batch_op:
41
+ batch_op.add_column(
42
+ sa.Column(
43
+ "department_id",
44
+ sqlalchemy_utils.types.uuid.UUIDType(binary=False),
45
+ default=uuid.uuid4,
46
+ nullable=True,
47
+ )
48
+ )
49
+ batch_op.create_foreign_key(
50
+ "search_filter_group_department_id_fkey",
51
+ "department",
52
+ ["department_id"],
53
+ ["id"],
54
+ )
55
+
56
+ # ### end Alembic commands ###
57
+
58
+
59
+ def downgrade():
60
+ # ### commands auto generated by Alembic - please adjust! ###
61
+ with op.batch_alter_table("search_filter_group", schema=None) as batch_op:
62
+ batch_op.drop_constraint(
63
+ "search_filter_group_department_id_fkey", type_="foreignkey"
64
+ )
65
+ batch_op.drop_column("department_id")
66
+
67
+ with op.batch_alter_table("search_filter", schema=None) as batch_op:
68
+ batch_op.drop_constraint(
69
+ "search_filter_department_id_fkey", type_="foreignkey"
70
+ )
71
+ batch_op.drop_column("department_id")
72
+
73
+ # ### end Alembic commands ###
@@ -0,0 +1,40 @@
1
+ """Add descriptions for entities tasks and statuses
2
+
3
+ Revision ID: a252a094e977
4
+ Revises: 1bb55759146f
5
+ Create Date: 2024-06-20 20:37:21.885953
6
+
7
+ """
8
+
9
+ from alembic import op
10
+ import sqlalchemy as sa
11
+
12
+
13
+ # revision identifiers, used by Alembic.
14
+ revision = "a252a094e977"
15
+ down_revision = "1bb55759146f"
16
+ branch_labels = None
17
+ depends_on = None
18
+
19
+
20
+ def upgrade():
21
+ # ### commands auto generated by Alembic - please adjust! ###
22
+ op.add_column(
23
+ "task_type", sa.Column("description", sa.Text(), nullable=True)
24
+ )
25
+ op.add_column(
26
+ "task_status", sa.Column("description", sa.Text(), nullable=True)
27
+ )
28
+ op.add_column(
29
+ "entity_type", sa.Column("description", sa.Text(), nullable=True)
30
+ )
31
+ # ### end Alembic commands ###
32
+
33
+
34
+ def downgrade():
35
+ # ### commands auto generated by Alembic - please adjust! ###
36
+ op.drop_column("task_type", "description")
37
+ op.drop_column("task_status", "description")
38
+ op.drop_column("entity_type", "description")
39
+
40
+ # ### end Alembic commands ###
@@ -0,0 +1,102 @@
1
+ """For is_shared disallow nullable
2
+
3
+ Revision ID: be56dc0fb760
4
+ Revises: 680c64565f9d
5
+ Create Date: 2024-05-31 17:44:17.975779
6
+
7
+ """
8
+
9
+ from alembic import op
10
+ import sqlalchemy as sa
11
+ from sqlalchemy import orm
12
+ from sqlalchemy.ext.declarative import declarative_base
13
+ from zou.migrations.utils.base import BaseMixin
14
+
15
+
16
+ # revision identifiers, used by Alembic.
17
+ revision = "be56dc0fb760"
18
+ down_revision = "680c64565f9d"
19
+ branch_labels = None
20
+ depends_on = None
21
+
22
+
23
+ def upgrade():
24
+ # ### commands auto generated by Alembic - please adjust! ###
25
+
26
+ base = declarative_base()
27
+
28
+ class SearchFilter(base, BaseMixin):
29
+ """
30
+ Filters allow to store quick search on a list: asset list, shot list,
31
+ sequence list, todo-list...
32
+ """
33
+
34
+ __tablename__ = "search_filter"
35
+ list_type = sa.Column(sa.String(80), nullable=False, index=True)
36
+ entity_type = sa.Column(sa.String(80))
37
+ name = sa.Column(sa.String(200), nullable=False, default="")
38
+ search_query = sa.Column(sa.String(500), nullable=False, default="")
39
+ is_shared = sa.Column(
40
+ sa.Boolean,
41
+ default=False,
42
+ nullable=True,
43
+ )
44
+
45
+ class SearchFilterGroup(base, BaseMixin):
46
+ """
47
+ Groups are used to store search filters into sections.
48
+ """
49
+
50
+ __tablename__ = "search_filter_group"
51
+ list_type = sa.Column(sa.String(80), nullable=False, index=True)
52
+ entity_type = sa.Column(sa.String(80))
53
+ name = sa.Column(sa.String(200), nullable=False, default="")
54
+ color = sa.Column(sa.String(8), nullable=False, default="")
55
+ is_shared = sa.Column(
56
+ sa.Boolean,
57
+ default=False,
58
+ nullable=True,
59
+ )
60
+
61
+ bind = op.get_bind()
62
+ session = orm.Session(bind=bind)
63
+ session.query(SearchFilter).where(SearchFilter.is_shared == None).update(
64
+ {SearchFilter.is_shared: False}
65
+ )
66
+ session.query(SearchFilterGroup).where(
67
+ SearchFilterGroup.is_shared == None
68
+ ).update({SearchFilterGroup.is_shared: False})
69
+ session.commit()
70
+
71
+ with op.batch_alter_table("search_filter", schema=None) as batch_op:
72
+ batch_op.alter_column(
73
+ "is_shared",
74
+ existing_type=sa.BOOLEAN(),
75
+ server_default=sa.sql.expression.false(),
76
+ nullable=False,
77
+ )
78
+
79
+ with op.batch_alter_table("search_filter_group", schema=None) as batch_op:
80
+ batch_op.alter_column(
81
+ "is_shared",
82
+ existing_type=sa.BOOLEAN(),
83
+ server_default=sa.sql.expression.false(),
84
+ nullable=False,
85
+ )
86
+
87
+ # ### end Alembic commands ###
88
+
89
+
90
+ def downgrade():
91
+ # ### commands auto generated by Alembic - please adjust! ###
92
+ with op.batch_alter_table("search_filter_group", schema=None) as batch_op:
93
+ batch_op.alter_column(
94
+ "is_shared", existing_type=sa.BOOLEAN(), nullable=True
95
+ )
96
+
97
+ with op.batch_alter_table("search_filter", schema=None) as batch_op:
98
+ batch_op.alter_column(
99
+ "is_shared", existing_type=sa.BOOLEAN(), nullable=True
100
+ )
101
+
102
+ # ### end Alembic commands ###
@@ -0,0 +1,108 @@
1
+ """add is_done field to the task model
2
+
3
+ Revision ID: ca28796a2a62
4
+ Revises: 971dbf5a0faf
5
+ Create Date: 2024-07-19 15:24:21.064991
6
+
7
+ """
8
+
9
+ from alembic import op
10
+ import sqlalchemy as sa
11
+ from sqlalchemy import orm
12
+ from sqlalchemy.ext.declarative import declarative_base
13
+ from zou.migrations.utils.base import BaseMixin
14
+ from sqlalchemy_utils import UUIDType, ChoiceType
15
+
16
+
17
+ # revision identifiers, used by Alembic.
18
+ revision = "ca28796a2a62"
19
+ down_revision = "971dbf5a0faf"
20
+ branch_labels = None
21
+ depends_on = None
22
+
23
+
24
+ def upgrade():
25
+ base = declarative_base()
26
+
27
+ TYPES = [
28
+ ("comment", "Comment"),
29
+ ("mention", "Mention"),
30
+ ("assignation", "Assignation"),
31
+ ("reply", "Reply"),
32
+ ("reply-mention", "Reply Mention"),
33
+ ]
34
+
35
+ class Notification(base, BaseMixin):
36
+ """
37
+ A notification is stored each time a comment is posted.
38
+ """
39
+
40
+ __tablename__ = "notification"
41
+ read = sa.Column(sa.Boolean, nullable=False, default=False)
42
+ change = sa.Column(sa.Boolean, nullable=False, default=False)
43
+ type = sa.Column(ChoiceType(TYPES), nullable=False)
44
+ person_id = sa.Column(
45
+ UUIDType(binary=False),
46
+ nullable=False,
47
+ index=True,
48
+ )
49
+ author_id = sa.Column(
50
+ UUIDType(binary=False),
51
+ nullable=False,
52
+ index=True,
53
+ )
54
+ comment_id = sa.Column(
55
+ UUIDType(binary=False),
56
+ nullable=True,
57
+ index=True,
58
+ )
59
+ task_id = sa.Column(
60
+ UUIDType(binary=False),
61
+ nullable=False,
62
+ index=True,
63
+ )
64
+ reply_id = sa.Column(UUIDType(binary=False), nullable=True, index=True)
65
+
66
+ __table_args__ = (
67
+ sa.UniqueConstraint(
68
+ "person_id",
69
+ "author_id",
70
+ "comment_id",
71
+ "reply_id",
72
+ "type",
73
+ name="notification_uc",
74
+ ),
75
+ )
76
+
77
+ bind = op.get_bind()
78
+ session = orm.Session(bind=bind)
79
+ session.query(Notification).where(Notification.type == None).update(
80
+ {Notification.type: "comment"}
81
+ )
82
+ session.commit()
83
+
84
+ # ### commands auto generated by Alembic - please adjust! ###
85
+ with op.batch_alter_table("notification", schema=None) as batch_op:
86
+ batch_op.alter_column(
87
+ "type", existing_type=sa.VARCHAR(length=255), nullable=False
88
+ )
89
+
90
+ with op.batch_alter_table("task", schema=None) as batch_op:
91
+ batch_op.add_column(
92
+ sa.Column("done_date", sa.DateTime(), nullable=True)
93
+ )
94
+
95
+ # ### end Alembic commands ###
96
+
97
+
98
+ def downgrade():
99
+ # ### commands auto generated by Alembic - please adjust! ###
100
+ with op.batch_alter_table("task", schema=None) as batch_op:
101
+ batch_op.drop_column("done_date")
102
+
103
+ with op.batch_alter_table("notification", schema=None) as batch_op:
104
+ batch_op.alter_column(
105
+ "type", existing_type=sa.VARCHAR(length=255), nullable=True
106
+ )
107
+
108
+ # ### end Alembic commands ###
@@ -0,0 +1,75 @@
1
+ """For description of entity, task, working_file don't set any length
2
+
3
+ Revision ID: f344b867a911
4
+ Revises: 32f134ff1201
5
+ Create Date: 2024-05-22 15:46:07.855818
6
+
7
+ """
8
+
9
+ from alembic import op
10
+ import sqlalchemy as sa
11
+
12
+
13
+ # revision identifiers, used by Alembic.
14
+ revision = "f344b867a911"
15
+ down_revision = "32f134ff1201"
16
+ branch_labels = None
17
+ depends_on = None
18
+
19
+
20
+ def upgrade():
21
+ # ### commands auto generated by Alembic - please adjust! ###
22
+ with op.batch_alter_table("entity", schema=None) as batch_op:
23
+ batch_op.alter_column(
24
+ "description",
25
+ existing_type=sa.VARCHAR(length=1200),
26
+ type_=sa.Text(),
27
+ existing_nullable=True,
28
+ )
29
+
30
+ with op.batch_alter_table("task", schema=None) as batch_op:
31
+ batch_op.alter_column(
32
+ "description",
33
+ existing_type=sa.VARCHAR(length=200),
34
+ type_=sa.Text(),
35
+ existing_nullable=True,
36
+ )
37
+
38
+ with op.batch_alter_table("working_file", schema=None) as batch_op:
39
+ batch_op.alter_column(
40
+ "description",
41
+ existing_type=sa.VARCHAR(length=200),
42
+ type_=sa.Text(),
43
+ existing_nullable=True,
44
+ )
45
+
46
+ # ### end Alembic commands ###
47
+
48
+
49
+ def downgrade():
50
+ # ### commands auto generated by Alembic - please adjust! ###
51
+ with op.batch_alter_table("working_file", schema=None) as batch_op:
52
+ batch_op.alter_column(
53
+ "description",
54
+ existing_type=sa.Text(),
55
+ type_=sa.VARCHAR(length=200),
56
+ existing_nullable=True,
57
+ )
58
+
59
+ with op.batch_alter_table("task", schema=None) as batch_op:
60
+ batch_op.alter_column(
61
+ "description",
62
+ existing_type=sa.Text(),
63
+ type_=sa.VARCHAR(length=200),
64
+ existing_nullable=True,
65
+ )
66
+
67
+ with op.batch_alter_table("entity", schema=None) as batch_op:
68
+ batch_op.alter_column(
69
+ "description",
70
+ existing_type=sa.Text(),
71
+ type_=sa.VARCHAR(length=1200),
72
+ existing_nullable=True,
73
+ )
74
+
75
+ # ### end Alembic commands ###
@@ -67,7 +67,8 @@ def get_file_from_storage(storage, output_file_path, filename):
67
67
  or os.path.getsize(output_file_path) == 0
68
68
  ):
69
69
  with open(output_file_path, "wb") as output_file:
70
- output_file.write(storage.read(filename))
70
+ for chunk in storage.read_chunks(filename):
71
+ output_file.write(chunk)
71
72
  return output_file_path
72
73
 
73
74
 
zou/utils/movie.py CHANGED
@@ -103,14 +103,20 @@ def generate_tile(movie_path):
103
103
  duration = get_movie_duration(video_track=video_track)
104
104
  fps = get_movie_fps(video_track=video_track)
105
105
  duration_in_frames = int(duration * fps)
106
- rows = min(math.ceil(duration_in_frames / 8), 240)
106
+ rows = min(math.ceil(duration_in_frames / 8), 480)
107
107
  ratio = get_movie_display_aspect_ratio(video_track=video_track)
108
108
  height = 100
109
109
  width = math.ceil(height * ratio)
110
-
110
+ if rows == 480:
111
+ select = (
112
+ rf"select='not(mod(n\,{math.ceil(duration_in_frames/3840)}))',"
113
+ )
114
+ else:
115
+ select = ""
111
116
  try:
112
117
  ffmpeg.input(movie_path).output(
113
- file_target_path, vf=f"scale={width}:{height},tile=8x{rows}"
118
+ file_target_path,
119
+ vf=f"{select}scale={width}:{height},tile=8x{rows}",
114
120
  ).overwrite_output().run(quiet=True)
115
121
  except ffmpeg._run.Error as e:
116
122
  log_ffmpeg_error(e, "An error occured while generating the tile.")
@@ -489,7 +495,11 @@ def concat_demuxer(in_files, output_path, *args):
489
495
 
490
496
  stream = ffmpeg.input(temp.name, format="concat", safe=0)
491
497
  stream = ffmpeg.output(
492
- stream.video, stream.audio, output_path, c="copy"
498
+ stream.video,
499
+ stream.audio,
500
+ output_path,
501
+ vf="select=concatdec_select",
502
+ af="aselect=concatdec_select,aresample=async=1",
493
503
  )
494
504
  return run_ffmpeg(stream, "-xerror")
495
505
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: zou
3
- Version: 0.19.14
3
+ Version: 0.20.11
4
4
  Summary: API to store and manage the data of your animation production
5
5
  Home-page: https://zou.cg-wire.com
6
6
  Author: CG Wire
@@ -11,82 +11,88 @@ Classifier: Development Status :: 5 - Production/Stable
11
11
  Classifier: Environment :: Web Environment
12
12
  Classifier: Framework :: Flask
13
13
  Classifier: Intended Audience :: Developers
14
- Classifier: Programming Language :: Python :: 3.8
15
14
  Classifier: Programming Language :: Python :: 3.9
16
15
  Classifier: Programming Language :: Python :: 3.10
17
16
  Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
18
19
  Classifier: Programming Language :: Python :: Implementation :: CPython
19
20
  Classifier: Programming Language :: Python :: Implementation :: PyPy
20
21
  Classifier: Topic :: Multimedia :: Graphics
21
- Requires-Python: >=3.8.1, <3.12
22
+ Requires-Python: >=3.9, <3.14
22
23
  License-File: LICENSE
23
- Requires-Dist: babel ==2.14.0
24
- Requires-Dist: click ==8.1.7
25
- Requires-Dist: discord.py ==2.3.2
26
- Requires-Dist: email-validator ==2.1.1
27
- Requires-Dist: ffmpeg-python ==0.2.0
28
- Requires-Dist: fido2 ==1.1.2
29
- Requires-Dist: flasgger ==0.9.7.1
30
- Requires-Dist: flask-bcrypt ==1.0.1
31
- Requires-Dist: flask-caching ==2.1.0
32
- Requires-Dist: flask-fixtures ==0.3.8
33
- Requires-Dist: flask-mail ==0.9.1
34
- Requires-Dist: flask-principal ==0.4.0
35
- Requires-Dist: flask-restful ==0.3.10
36
- Requires-Dist: flask-sqlalchemy ==3.1.1
37
- Requires-Dist: flask-fs2[s3,swift] ==0.7.24
38
- Requires-Dist: flask-jwt-extended ==4.6.0
39
- Requires-Dist: flask-migrate ==4.0.7
40
- Requires-Dist: flask-socketio ==5.3.6
41
- Requires-Dist: flask ==3.0.2
42
- Requires-Dist: gazu ==0.10.1
43
- Requires-Dist: gevent-websocket ==0.10.1
44
- Requires-Dist: gevent ==24.2.1
45
- Requires-Dist: gunicorn ==21.2.0
46
- Requires-Dist: isoweek ==1.3.3
47
- Requires-Dist: itsdangerous ==2.1.2
48
- Requires-Dist: Jinja2 ==3.1.3
49
- Requires-Dist: ldap3 ==2.9.1
50
- Requires-Dist: matterhook ==0.2
51
- Requires-Dist: meilisearch ==0.31.0
52
- Requires-Dist: numpy ==1.24.4
53
- Requires-Dist: opencv-python ==4.9.0.80
54
- Requires-Dist: OpenTimelineIO ==0.15.0
55
- Requires-Dist: orjson ==3.9.15
56
- Requires-Dist: pillow ==10.2.0
57
- Requires-Dist: psutil ==5.9.8
58
- Requires-Dist: psycopg[binary] ==3.1.18
59
- Requires-Dist: pyotp ==2.9.0
60
- Requires-Dist: python-nomad ==2.0.0
61
- Requires-Dist: python-slugify ==8.0.4
62
- Requires-Dist: python-socketio ==5.11.1
63
- Requires-Dist: pytz ==2024.1
64
- Requires-Dist: redis ==5.0.3
65
- Requires-Dist: requests ==2.31.0
66
- Requires-Dist: rq ==1.16.1
67
- Requires-Dist: slackclient ==2.9.4
68
- Requires-Dist: sqlalchemy-utils ==0.41.1
69
- Requires-Dist: sqlalchemy ==2.0.28
70
- Requires-Dist: ua-parser ==0.18.0
71
- Requires-Dist: werkzeug ==3.0.1
72
- Provides-Extra: dev
73
- Requires-Dist: wheel ; extra == 'dev'
74
- Provides-Extra: lint
75
- Requires-Dist: autoflake ==2.3.0 ; extra == 'lint'
76
- Requires-Dist: black ==24.2.0 ; extra == 'lint'
77
- Requires-Dist: pre-commit ==3.6.2 ; (python_version >= "3.9") and extra == 'lint'
78
- Provides-Extra: monitoring
79
- Requires-Dist: prometheus-flask-exporter ==0.23.0 ; extra == 'monitoring'
80
- Requires-Dist: pygelf ==0.4.2 ; extra == 'monitoring'
81
- Requires-Dist: sentry-sdk ==1.41.0 ; extra == 'monitoring'
24
+ Requires-Dist: audioop-lts==0.2.1; python_version >= "3.13"
25
+ Requires-Dist: babel==2.16.0
26
+ Requires-Dist: click==8.1.8
27
+ Requires-Dist: discord.py==2.4.0
28
+ Requires-Dist: email-validator==2.2.0
29
+ Requires-Dist: ffmpeg-python==0.2.0
30
+ Requires-Dist: fido2==1.2.0
31
+ Requires-Dist: flasgger==0.9.7.1
32
+ Requires-Dist: flask_bcrypt==1.0.1
33
+ Requires-Dist: flask_caching==2.3.0
34
+ Requires-Dist: flask_fixtures==0.3.8
35
+ Requires-Dist: flask_mail==0.10.0
36
+ Requires-Dist: flask_principal==0.4.0
37
+ Requires-Dist: flask_restful==0.3.10
38
+ Requires-Dist: flask_sqlalchemy==3.1.1
39
+ Requires-Dist: flask-fs2[s3,swift]==0.7.28
40
+ Requires-Dist: flask-jwt-extended==4.7.1
41
+ Requires-Dist: flask-migrate==4.1.0
42
+ Requires-Dist: flask-socketio==5.5.1
43
+ Requires-Dist: flask==3.1.0
44
+ Requires-Dist: gazu==0.10.22
45
+ Requires-Dist: gevent-websocket==0.10.1
46
+ Requires-Dist: gevent==24.11.1
47
+ Requires-Dist: gunicorn==23.0.0
48
+ Requires-Dist: isoweek==1.3.3
49
+ Requires-Dist: itsdangerous==2.2.0
50
+ Requires-Dist: Jinja2==3.1.5
51
+ Requires-Dist: ldap3==2.9.1
52
+ Requires-Dist: matterhook==0.2
53
+ Requires-Dist: meilisearch==0.33.1
54
+ Requires-Dist: numpy==2.0.1; python_version == "3.9"
55
+ Requires-Dist: numpy==2.2.1; python_version >= "3.10"
56
+ Requires-Dist: opencv-python==4.10.0.84
57
+ Requires-Dist: OpenTimelineIO==0.17.0
58
+ Requires-Dist: OpenTimelineIO-Plugins==0.17.0
59
+ Requires-Dist: orjson==3.10.14
60
+ Requires-Dist: pillow==11.1.0
61
+ Requires-Dist: psutil==6.1.1
62
+ Requires-Dist: psycopg[binary]==3.2.3
63
+ Requires-Dist: pyotp==2.9.0
64
+ Requires-Dist: pysaml2==7.5.0
65
+ Requires-Dist: python-nomad==2.0.1
66
+ Requires-Dist: python-slugify==8.0.4
67
+ Requires-Dist: python-socketio==5.12.1
68
+ Requires-Dist: pytz==2024.2
69
+ Requires-Dist: redis==5.2.1
70
+ Requires-Dist: requests==2.32.3
71
+ Requires-Dist: rq==2.1.0
72
+ Requires-Dist: slackclient==2.9.4
73
+ Requires-Dist: sqlalchemy_utils==0.41.2
74
+ Requires-Dist: sqlalchemy==2.0.37
75
+ Requires-Dist: ua-parser==1.0.0
76
+ Requires-Dist: werkzeug==3.1.3
82
77
  Provides-Extra: prod
83
- Requires-Dist: gunicorn ; extra == 'prod'
84
- Requires-Dist: gevent ; extra == 'prod'
78
+ Requires-Dist: gunicorn; extra == "prod"
79
+ Requires-Dist: gevent; extra == "prod"
80
+ Provides-Extra: dev
81
+ Requires-Dist: wheel; extra == "dev"
85
82
  Provides-Extra: test
86
- Requires-Dist: fakeredis ==2.21.3 ; extra == 'test'
87
- Requires-Dist: mixer ==7.2.2 ; extra == 'test'
88
- Requires-Dist: pytest-cov ==4.1.0 ; extra == 'test'
89
- Requires-Dist: pytest ==8.1.1 ; extra == 'test'
83
+ Requires-Dist: fakeredis==2.26.2; extra == "test"
84
+ Requires-Dist: mixer==7.2.2; extra == "test"
85
+ Requires-Dist: pytest-cov==6.0.0; extra == "test"
86
+ Requires-Dist: pytest==8.3.4; extra == "test"
87
+ Provides-Extra: monitoring
88
+ Requires-Dist: prometheus-flask-exporter==0.23.1; extra == "monitoring"
89
+ Requires-Dist: pygelf==0.4.2; extra == "monitoring"
90
+ Requires-Dist: sentry-sdk==2.19.2; extra == "monitoring"
91
+ Provides-Extra: lint
92
+ Requires-Dist: autoflake==2.3.1; extra == "lint"
93
+ Requires-Dist: black==24.10.0; extra == "lint"
94
+ Requires-Dist: pre-commit==4.0.1; extra == "lint"
95
+ Dynamic: requires-python
90
96
 
91
97
  .. figure:: https://zou.cg-wire.com/kitsu.png
92
98
  :alt: Kitsu Logo