-
Notifications
You must be signed in to change notification settings - Fork 376
Feat: video to image mode #235
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from 27 commits
Commits
Show all changes
30 commits
Select commit
Hold shift + click to select a range
f13f8d5
feat: add video reference support with YouTube URL input and selectio…
dalkerman bb6a2ec
refactor: move AssetReferenceDto to common DTOs and implement video r…
dalkerman 0c51cd6
feat: add YouTube thumbnail preview functionality for reference video…
dalkerman 40bd93c
feat: add YouTubeInputComponent for URL entry and validation with cli…
dalkerman 1719d87
refactor: introduce shared YouTube utilities and enforce mutual exclu…
dalkerman 9c66b41
style: apply glassmorphism design to YouTube input dialog and remove …
dalkerman 91648ee
feat: filter model selection by video reference capability and auto-s…
dalkerman 8502b54
fix: default image reference index to zero when out of bounds in imag…
dalkerman 16a1b78
feat: disable unsupported models in flow prompt box instead of hiding…
dalkerman 1cb60ff
refactor: move video reference validation from state updates to ngOnC…
dalkerman 8c695d3
refactor: migrate YouTube video ID and thumbnail URL getters to signals
dalkerman 81acc66
refactor: move video-to-image model compatibility check from FlowProm…
dalkerman 9f0cd5f
feat: add YouTube URL validation to CreateImagenDto and expand backgr…
dalkerman 97da836
refactor: convert referenceVideoYoutubeUrl to a signal and improve Yo…
dalkerman 4a0dfc3
fix: raise errors for missing media assets and improve YouTube URL va…
dalkerman 120c6c3
feat: add support for YouTube video assets by updating database schem…
dalkerman db3b041
feat: add youtube_url to source_assets and implement dynamic aspect r…
dalkerman 3e8524e
refactor: rename youtube_url to external_url in source assets schema …
dalkerman a876aad
feat: add support for YouTube video assets by integrating external UR…
dalkerman acc5714
feat: add YouTube video support with iframe embedding and custom safe…
dalkerman 5ea4f96
Merge branch 'develop' into feat/video-to-image-mode
dalkerman 96296c6
feat: update unified_gallery_view schema and refine YouTube URL sanit…
dalkerman 4a386f7
style: apply consistent formatting to regex declaration and return st…
dalkerman b6c0652
feat: add include_external filter to gallery search and expose it in …
dalkerman e349c60
refactor: rename reference_video_youtube_url to external_url across i…
dalkerman 23a4f59
refactor: improve YouTube URL parsing robustness and add validation f…
dalkerman 8dd9e58
refactor: centralize YouTube URL extraction logic and update async se…
dalkerman 2b8538d
refactor: clean up imports, handle empty YouTube URLs, and improve so…
dalkerman 0303966
refactor: validate YouTube URLs by requiring successful video ID extr…
dalkerman 14beb42
fix: tighten YouTube hostname validation, enforce video/mp4 mime type…
dalkerman File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
76 changes: 76 additions & 0 deletions
76
backend/alembic/versions/582bbd507011_add_youtube_url_to_source_assets.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| # Copyright 2026 Google LLC | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| """add_external_url_to_source_assets | ||
|
|
||
| Revision ID: 582bbd507011 | ||
| Revises: 7ec3aed70c3f | ||
| Create Date: 2026-07-13 16:06:40.894364 | ||
|
|
||
| """ | ||
| from typing import Sequence, Union | ||
|
|
||
| from alembic import op | ||
| import sqlalchemy as sa | ||
| from sqlalchemy.dialects import postgresql | ||
|
|
||
| # revision identifiers, used by Alembic. | ||
| revision: str = "582bbd507011" | ||
| down_revision: Union[str, None] = "7ec3aed70c3f" | ||
| branch_labels: Union[str, Sequence[str], None] = None | ||
| depends_on: Union[str, Sequence[str], None] = None | ||
|
|
||
|
|
||
| def upgrade() -> None: | ||
| # ### commands auto generated by Alembic - please adjust! ### | ||
| op.add_column( | ||
| "source_assets", sa.Column("external_url", sa.String(), nullable=True) | ||
| ) | ||
| op.alter_column( | ||
| "source_assets", "gcs_uri", existing_type=sa.VARCHAR(), nullable=True | ||
| ) | ||
| op.alter_column( | ||
| "source_assets", | ||
| "original_filename", | ||
| existing_type=sa.VARCHAR(), | ||
| nullable=True, | ||
| ) | ||
| op.alter_column( | ||
| "source_assets", "mime_type", existing_type=sa.VARCHAR(), nullable=True | ||
| ) | ||
| op.alter_column( | ||
| "source_assets", "file_hash", existing_type=sa.VARCHAR(), nullable=True | ||
| ) | ||
| # ### end Alembic commands ### | ||
|
|
||
|
|
||
| def downgrade() -> None: | ||
| # ### commands auto generated by Alembic - please adjust! ### | ||
| op.alter_column( | ||
| "source_assets", "file_hash", existing_type=sa.VARCHAR(), nullable=False | ||
| ) | ||
| op.alter_column( | ||
| "source_assets", "mime_type", existing_type=sa.VARCHAR(), nullable=False | ||
| ) | ||
| op.alter_column( | ||
| "source_assets", | ||
| "original_filename", | ||
| existing_type=sa.VARCHAR(), | ||
| nullable=False, | ||
| ) | ||
| op.alter_column( | ||
| "source_assets", "gcs_uri", existing_type=sa.VARCHAR(), nullable=False | ||
| ) | ||
| op.drop_column("source_assets", "external_url") | ||
| # ### end Alembic commands ### |
208 changes: 208 additions & 0 deletions
208
backend/alembic/versions/7a8b9c0d1e2f_update_unified_gallery_view_external_url.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,208 @@ | ||
| # Copyright 2026 Google LLC | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| """update_unified_gallery_view_external_url | ||
|
|
||
| Revision ID: 7a8b9c0d1e2f | ||
| Revises: 582bbd507011 | ||
| Create Date: 2026-07-15 03:40:00.000000 | ||
|
|
||
| """ | ||
| from typing import Sequence, Union | ||
|
|
||
| from alembic import op | ||
|
|
||
| # revision identifiers, used by Alembic. | ||
| revision: str = "7a8b9c0d1e2f" | ||
| down_revision: Union[str, None] = "582bbd507011" | ||
| branch_labels: Union[str, Sequence[str], None] = None | ||
| depends_on: Union[str, Sequence[str], None] = None | ||
|
|
||
|
|
||
| def upgrade() -> None: | ||
| op.execute("DROP VIEW IF EXISTS unified_gallery_view;") | ||
| op.execute( | ||
| """ | ||
| CREATE VIEW unified_gallery_view AS | ||
| WITH unified_base AS ( | ||
| SELECT | ||
| mi.id, | ||
| mi.workspace_id, | ||
| mi.user_id, | ||
| mi.created_at, | ||
| 'media_item'::text AS item_type, | ||
| mi.status, | ||
| mi.gcs_uris, | ||
| mi.thumbnail_uris, | ||
| mi.deleted_at, | ||
| mi.titles, | ||
| mi.descriptions, | ||
| jsonb_build_object( | ||
| 'model', mi.model, | ||
| 'prompt', mi.prompt, | ||
| 'original_prompt', mi.original_prompt, | ||
| 'negative_prompt', mi.negative_prompt, | ||
| 'aspect_ratio', mi.aspect_ratio, | ||
| 'mime_type', mi.mime_type, | ||
| 'style', mi.style, | ||
| 'lighting', mi.lighting, | ||
| 'num_media', mi.num_media, | ||
| 'generation_time', mi.generation_time, | ||
| 'file_name', mi.comment, | ||
| 'source_assets', mi.source_assets, | ||
| 'source_media_items', mi.source_media_items, | ||
| 'is_video', (mi.mime_type LIKE 'video%'), | ||
| 'is_audio', (mi.mime_type LIKE 'audio%'), | ||
| 'tags', ( | ||
| SELECT jsonb_agg(jsonb_build_object('id', t.id, 'name', t.name, 'color', t.color, 'workspace_id', t.workspace_id)) | ||
| FROM media_item_tags mit | ||
| JOIN tags t ON mit.tag_id = t.id | ||
| WHERE mit.media_item_id = mi.id | ||
| ) | ||
| ) AS metadata | ||
| FROM media_items mi | ||
| UNION ALL | ||
| SELECT | ||
| sa.id, | ||
| sa.workspace_id, | ||
| sa.user_id, | ||
| sa.created_at, | ||
| 'source_asset'::text AS item_type, | ||
| 'completed'::text AS status, | ||
| CASE | ||
| WHEN (sa.gcs_uri IS NOT NULL) THEN ARRAY[sa.gcs_uri] | ||
| ELSE '{}'::text[] | ||
| END AS gcs_uris, | ||
| CASE | ||
| WHEN (sa.thumbnail_gcs_uri IS NOT NULL) THEN ARRAY[sa.thumbnail_gcs_uri] | ||
| ELSE '{}'::text[] | ||
| END AS thumbnail_uris, | ||
| sa.deleted_at, | ||
| sa.titles, | ||
| sa.descriptions, | ||
| jsonb_build_object( | ||
| 'file_name', sa.original_filename, | ||
| 'original_filename', sa.original_filename, | ||
| 'mime_type', sa.mime_type, | ||
| 'aspect_ratio', sa.aspect_ratio, | ||
| 'asset_type', sa.asset_type, | ||
| 'external_url', sa.external_url, | ||
| 'is_video', (sa.mime_type LIKE 'video%' OR sa.asset_type = 'youtube_video' OR sa.external_url IS NOT NULL), | ||
| 'is_audio', (sa.mime_type LIKE 'audio%'), | ||
| 'tags', ( | ||
| SELECT jsonb_agg(jsonb_build_object('id', t.id, 'name', t.name, 'color', t.color, 'workspace_id', t.workspace_id)) | ||
| FROM source_asset_tags sat | ||
| JOIN tags t ON sat.tag_id = t.id | ||
| WHERE sat.source_asset_id = sa.id | ||
| ) | ||
| ) AS metadata | ||
| FROM source_assets sa | ||
| ) | ||
| SELECT | ||
| ub.*, | ||
| w.name AS workspace_name, | ||
| u.picture AS user_picture, | ||
| u.email AS user_email | ||
| FROM unified_base ub | ||
| LEFT JOIN workspaces w ON ub.workspace_id = w.id | ||
| LEFT JOIN users u ON ub.user_id = u.id; | ||
| """ | ||
| ) | ||
|
|
||
|
|
||
| def downgrade() -> None: | ||
| op.execute("DROP VIEW IF EXISTS unified_gallery_view;") | ||
| op.execute( | ||
| """ | ||
| CREATE VIEW unified_gallery_view AS | ||
| SELECT | ||
| mi.id, | ||
| mi.workspace_id, | ||
| mi.user_id, | ||
| mi.created_at, | ||
| 'media_item'::text AS item_type, | ||
| mi.status, | ||
| mi.gcs_uris, | ||
| mi.thumbnail_uris, | ||
| mi.deleted_at, | ||
| w.name AS workspace_name, | ||
| u.picture AS user_picture, | ||
| u.email AS user_email, | ||
| mi.titles, | ||
| mi.descriptions, | ||
| jsonb_build_object( | ||
| 'model', mi.model, | ||
| 'prompt', mi.prompt, | ||
| 'original_prompt', mi.original_prompt, | ||
| 'negative_prompt', mi.negative_prompt, | ||
| 'aspect_ratio', mi.aspect_ratio, | ||
| 'mime_type', mi.mime_type, | ||
| 'style', mi.style, | ||
| 'lighting', mi.lighting, | ||
| 'num_media', mi.num_media, | ||
| 'generation_time', mi.generation_time, | ||
| 'file_name', mi.comment, | ||
| 'source_assets', mi.source_assets, | ||
| 'source_media_items', mi.source_media_items, | ||
| 'is_video', (mi.mime_type like 'video%'), | ||
| 'is_audio', (mi.mime_type like 'audio%'), | ||
| 'tags', ( | ||
| SELECT jsonb_agg(jsonb_build_object('id', t.id, 'name', t.name, 'color', t.color, 'workspace_id', t.workspace_id)) | ||
| FROM media_item_tags mit | ||
| JOIN tags t ON mit.tag_id = t.id | ||
| WHERE mit.media_item_id = mi.id | ||
| ) | ||
| ) AS metadata | ||
| FROM media_items mi | ||
| LEFT JOIN workspaces w ON mi.workspace_id = w.id | ||
| LEFT JOIN users u ON mi.user_id = u.id | ||
| UNION ALL | ||
| SELECT | ||
| sa.id, | ||
| sa.workspace_id, | ||
| sa.user_id, | ||
| sa.created_at, | ||
| 'source_asset'::text AS item_type, | ||
| 'completed'::text AS status, | ||
| ARRAY[sa.gcs_uri] AS gcs_uris, | ||
| CASE | ||
| WHEN (sa.thumbnail_gcs_uri IS NOT NULL) THEN ARRAY[sa.thumbnail_gcs_uri] | ||
| ELSE '{}'::text[] | ||
| END AS thumbnail_uris, | ||
| sa.deleted_at, | ||
| w.name AS workspace_name, | ||
| u.picture AS user_picture, | ||
| u.email AS user_email, | ||
| sa.titles, | ||
| sa.descriptions, | ||
| jsonb_build_object( | ||
| 'file_name', sa.original_filename, | ||
| 'original_filename', sa.original_filename, | ||
| 'mime_type', sa.mime_type, | ||
| 'aspect_ratio', sa.aspect_ratio, | ||
| 'asset_type', sa.asset_type, | ||
| 'is_video', (sa.mime_type like 'video%'), | ||
| 'is_audio', (sa.mime_type like 'audio%'), | ||
| 'tags', ( | ||
| SELECT jsonb_agg(jsonb_build_object('id', t.id, 'name', t.name, 'color', t.color, 'workspace_id', t.workspace_id)) | ||
| FROM source_asset_tags sat | ||
| JOIN tags t ON sat.tag_id = t.id | ||
| WHERE sat.source_asset_id = sa.id | ||
| ) | ||
| ) AS metadata | ||
| FROM source_assets sa | ||
| LEFT JOIN workspaces w ON sa.workspace_id = w.id | ||
| LEFT JOIN users u ON sa.user_id = u.id; | ||
| """ | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.