File size: 6,321 Bytes
7312c42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cbbe5c7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7312c42
cbbe5c7
 
 
 
7312c42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
import os
import server
import yaml
import nodes as _nodes

from typing_extensions import override
from comfy_api.latest import ComfyExtension, io
from aiohttp import web
from .nodes import *
from .routes import *
from .utils.bernini_s2v_model_patch import apply_bernini_s2v_model_patches
from .utils.log import install_console_privacy_filter

from .moxie_loader import MoxieMultimediaLoader
from .moxie_project import MoxieProject
from .moxie_combine import MoxieCombine
from .moxie_preview import ModelPreviewOverrideKJ as MoxiePreviewOverride

apply_bernini_s2v_model_patches()
# Keep internal model/vendor names out of the console (incl. ComfyUI core logs).
install_console_privacy_filter()

# Define the path
root_path = os.path.dirname(__file__)
project_name = os.path.basename(root_path)

web_default_version = 'release'
data = None
config_path = os.path.join(root_path, "config.yaml")
if os.path.isfile(config_path):
    with open(config_path, 'r') as f:
        data = yaml.load(f, Loader=yaml.FullLoader)
    if data and "WEB_VERSION" in data:
        dist_path = f"dist/{data['WEB_VERSION']}"
    else:
        dist_path = f"dist/{web_default_version}"
else:
    dist_path = f"dist/{web_default_version}"

web_version = data.get("WEB_VERSION", web_default_version) if data else web_default_version
if not os.path.exists(os.path.join(root_path, dist_path)):
    print(f"web root {web_version} not found, using default")
    dist_path = f"dist/{web_default_version}"

dist_path = os.path.join(root_path, dist_path)
if os.path.exists(dist_path):
    # Editor delivery (see web/js/editor-loader.js for the full story):
    # core lets WEB_DIRECTORY (./web) overwrite this pack's EXTENSION_WEB_DIRS
    # entry, so the browser /extensions listing only contains the helper JS in
    # web/js - never dist/release. web/js/editor-loader.js is listed instead
    # and imports the editor entry from this pack's own static mount below.
    #
    # The entry file (index.js) keeps a stable URL across releases, so it is
    # served with no-cache and always revalidated (304 when unchanged); its
    # relative chunk imports resolve against the same mount and are
    # content-hashed, so those may be cached forever.
    async def _serve_editor_index(request: web.Request) -> web.StreamResponse:
        return web.FileResponse(
            os.path.join(dist_path, 'index.js'),
            headers={'Cache-Control': 'no-cache, must-revalidate'},
        )

    server.PromptServer.instance.app.add_routes([
        # Do NOT register "/extensions/<pack>/index.js" here: relative chunk
        # imports under that URL would resolve into web/ (core's mount) and
        # 404. The loader imports "/<pack>/index.js" from this mount instead.
        web.get(f"/{project_name}/index.js", _serve_editor_index),
        web.static(f"/{project_name}/", dist_path),
    ])
else:
    print(f"Warning: Dist directory not found for {project_name}. Frontend assets will be served.")

# Our own frontend assets (Moxie Preview Override widget)
WEB_DIRECTORY = "./web"

# Register extension
class EasyMediaExtension(ComfyExtension):
    @override
    async def get_node_list(self) -> list[type[io.ComfyNode]]:
        nodes = [
            # Moxie consolidated nodes
            MoxieMultimediaLoader,
            MoxiePreviewOverride,
            MoxieProject,
            MoxieCombine,
            # TimelineEditor
            TimelineEditor,
            TimelineInfoOutput,
            TimelineSegmentOutput,
            TimelineSegmentCount,
            # MultiTrack
            MultiTrackEditor,
            MultiImagesLoader,
            MultiTrackTaskOutput,
            MultiTrackPromptEnhancer,
            MultiTrackPromptEnhanceToProject,
            MultiTrackPromptEnhanceToProjectApply,
            MultiTrackPromptEnhancerImageListBridge,
            MultiTrackInfoOutput,
            MultiTrackAudioOutput,
            # Subtitle
            MultiTrackAddSubtitleToVideo,
            RecognizeSubtitle,
            AddSubtitleToVideo,
            # Image
            MakeRefsCompositeBySam3,
            ImageIndexesToIntList,
            # Audio
            EasyAudioMerge,
            EasyMinimaxH3AudioLock,
            # Video
            EasySaveVideo,
            EasyCompareVideos,
            EasyGetAudioFromVideo,
            EasyMergeVideos,
            EasyMergeVideosFromPaths,
            # Split
            SplitImages,
            SplitAudios,
            SplitVideos,
            # Make List
            MakeImageList,
            MakeAudioList,
            MakeVideoList,
            # Common
            EasyModelLoaderPack,
            MatchLine,
            APIWorkflowGate,
            # MiniMax
            EasyMiniMaxH3MotionContextHard,
            EasyMiniMaxH3ContextSwap,
            EasyMiniMaxH3HiResContinuity,
            EasyH3MotionContextLatentTrim,
            EasyH3ProjectContextLatentLoad,
            EasyH3ConditioningCache,
            EasyH3SegmentSamplingStart,
            EasyH3SamplingPreviewSampler,
            EasyMiniMaxH3LatentUpscaler,
            EasyMiniMaxH3SelfLiftSampler,
            EasyH3SegmentSaveEnd,
            EasyH3ContextMediaTrim,
            EasyH3PassthroughVideo,
            EasyH3AudioContextLatent,
            EasyH3LockedAudioDurationAlign,
            EasyH3LockedAudioSelect,
            EasyH3ProjectArtifact,
            EasyH3ProjectStaticPrepare,
            EasyMultiTrackProject,
            EasyMultiTrackProjectVideoCombine,
            EasyMiniMaxH3ToVideo,
            EasyMinimaxPromptOverride,
            EasyMiniMaxH3ReferenceToVideoBridge,
            EasyRemoveH3MotionContextLatent,
            # Wan
            BerniniModelPatch,
            EasyBerniniS2VConditioning,
            # LTXV
            LTXMultiTrackEncode,
            LTXI2VInplaceAndUpsample,
            LTXSamplerSimple,
            LTXVAddGuidesFromBatchIndexes,
            LTXVMakeRefVideo,
        ]
        try:
            from comfy_extras.nodes_bernini import BerniniConditioning as CoreBerniniConditioning
        except ImportError:
            nodes.extend([BerniniConditioning])
        nodes.extend(get_minimax_h3_fallback_nodes())
        return nodes

async def comfy_entrypoint() -> EasyMediaExtension:
    return EasyMediaExtension()