From a63f1fd039d018f89b020f91069eda49fbceecd8 Mon Sep 17 00:00:00 2001 From: daxcay Date: Fri, 4 Oct 2024 12:08:19 +0530 Subject: [PATCH] Initial Commit --- .github/workflows/publish.yml | 20 ++++ .gitignore | 159 +++++++++++++++++++++++++++++++ LICENSE | 21 +++++ README.md | 2 - __init__.py | 29 ++++++ classes/YouTubeVideoPlayer.py | 27 ++++++ pyproject.toml | 15 +++ web/assets/logo.png | Bin 0 -> 3634 bytes web/util.js | 171 ++++++++++++++++++++++++++++++++++ 9 files changed, 442 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/publish.yml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 __init__.py create mode 100644 classes/YouTubeVideoPlayer.py create mode 100644 pyproject.toml create mode 100644 web/assets/logo.png create mode 100644 web/util.js diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..e3c5c78 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,20 @@ +name: Publish to Comfy registry +on: + workflow_dispatch: + push: + branches: + - main + paths: + - "pyproject.toml" + +jobs: + publish-node: + name: Publish Custom Node to registry + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v4 + - name: Publish Custom Node + uses: Comfy-Org/publish-node-action@main + with: + personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }} ## Add your own personal access token to your Github Repository secrets and reference it here. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..76a388e --- /dev/null +++ b/.gitignore @@ -0,0 +1,159 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*.pyc +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +*.wwebjs_cache + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintainted in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ +*.pyc + +.idea +/node_modules +/frontend diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e550b32 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 daxcay + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 41f9a31..e69de29 100644 --- a/README.md +++ b/README.md @@ -1,2 +0,0 @@ -# ComfyUI-YT -Play youtube videos inside comfyui diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..b6b2506 --- /dev/null +++ b/__init__.py @@ -0,0 +1,29 @@ +# By Jerry Davos & Daxton Caylor +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to +# deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +""" +@author: Daxton Caylor & Jerry Davos +@title: ComfyUI-YouTubeVideoPlayer +@nickname: ComfyUI-YouTubeVideoPlayer +@description: YouTube Video Player in Comfy. +""" + +from .classes.YouTubeVideoPlayer import N_CLASS_MAPPINGS as YouTubeVideoPlayerMappings, N_DISPLAY_NAME_MAPPINGS as YouTubeVideoPlayerNameMappings + +NODE_CLASS_MAPPINGS = {} +NODE_CLASS_MAPPINGS.update(YouTubeVideoPlayerMappings) + +NODE_DISPLAY_NAME_MAPPINGS = {} +NODE_DISPLAY_NAME_MAPPINGS.update(YouTubeVideoPlayerNameMappings) + +WEB_DIRECTORY = "./web" \ No newline at end of file diff --git a/classes/YouTubeVideoPlayer.py b/classes/YouTubeVideoPlayer.py new file mode 100644 index 0000000..14cd3ac --- /dev/null +++ b/classes/YouTubeVideoPlayer.py @@ -0,0 +1,27 @@ +class YouTubeVideoPlayer: + + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(s): + return { + "required": {}, + "optional": {}, + "hidden": {} + } + + FUNCTION = "doit" + RETURN_TYPES = () + OUTPUT_NODE = True + + def doit(self): + return () + +N_CLASS_MAPPINGS = { + "YouTubeVideoPlayer": YouTubeVideoPlayer, +} + +N_DISPLAY_NAME_MAPPINGS = { + "YouTubeVideoPlayer": "YouTubeVideoPlayer", +} \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..edd303b --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,15 @@ +[project] +name = "comfyui-youtubevideoplayer" +description = "youtube video player in comfyui" +version = "1.0.0" +license = { file = "LICENSE" } +dependencies = [] + +[project.urls] +Repository = "https://github.com/daxcay/ComfyUI-YouTubeVideoPlayer" +# Used by Comfy Registry https://comfyregistry.org + +[tool.comfy] +PublisherId = "daxcay" +DisplayName = "ComfyUI-YouTubeVideoPlayer" +Icon = "https://raw.githubusercontent.com/daxcay/ComfyUI-YouTubeVideoPlayer/main/web/assets/img/logo.png" \ No newline at end of file diff --git a/web/assets/logo.png b/web/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..3f5dc8863a510cc0e5d28a92d3edb9c4cb24986b GIT binary patch literal 3634 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 0) { + this.updateIframeSRC(this.elements[node.id].iframe, node.properties['url']) + this.elements[node.id].input.value = `https://www.youtube.com/embed/${node.properties['url']}` + } + } + else { + if (this.elements[node.id] && node.properties['url'] && node.properties['url'].length > 0) { + this.first_time = true + this.updateIframeSRC(this.elements[node.id].iframe, node.properties['url']) + this.elements[node.id].input.value = `https://www.youtube.com/embed/${node.properties['url']}` + } + } + } + else if (mode == 0) { + if (this.nodes.includes(node)) { + this.nodes.splice(this.nodes.indexOf(node), 1) + if (this.elements[node.id]) { + delete this.elements[node.id] + } + } + } + } + + registerNodes() { + this.app.graph._nodes.forEach(node => { + if (node.type === "YouTubePlayer") { + this.registerNode(node, 1) + } + }) + } + + createInput(nodeid) { + var input = document.createElement('input'); + input.type = "text" + input.id = nodeid + input.placeholder = "Enter youtube url: " + input.style.border = "none" + input.style.textIndent = "10px" + input.style.borderRadius = "4px" + input.style.backgroundColor = 'rgb(40,40,40)' + return input; + } + + updateIframeSRC(iframe, videoId) { + iframe.src = `https://www.youtube.com/embed/${videoId}`; + } + + createYouTubeIframe(nodeid) { + var iframe = document.createElement('iframe'); + iframe.id = nodeid; + iframe.frameBorder = '0'; + iframe.allow = 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share'; + iframe.allowFullscreen = true; + iframe.style.borderRadius = "5px" + iframe.style.backgroundColor = 'black' + return iframe; + } + + getYouTubeVideoId(url) { + var videoId = null; + var standardPattern = /(?:https?:\/\/)?(?:www\.)?youtube\.com\/.*[?&]v=([^&]+)/; + var shortPattern = /(?:https?:\/\/)?(?:www\.)?youtu\.be\/([^?&]+)/; + var match = url.match(standardPattern); + if (match && match[1]) { + videoId = match[1]; + } else { + match = url.match(shortPattern); + if (match && match[1]) { + videoId = match[1]; + } + } + return videoId; + } + +} + +let youtube = new ComfyUIYouTubeVideoPlayer(app) + +let youtubeExt = { + name: "ComfyUIYoutube", + async setup() { + youtube.app.graph.onNodeAdded = youtube.onNodeAdded.bind(this); + youtube.app.graph.onNodeRemoved = youtube.onNodeRemoved.bind(this); + }, + async afterConfigureGraph() { + console.log("YouTubePlayer Extension Loaded"); + youtube.registerNodes() + } +}; + +app.registerExtension(youtubeExt);