├── .github └── pull_request_template.md ├── .gitignore ├── Apprise ├── Apprise.csproj ├── Communication │ └── Apprise.cs ├── ExtensionMethods.cs ├── GlobalUsings.cs ├── IAppriseApi.cs ├── Plugin.cs ├── PluginSettings.cs ├── Tests │ └── AppriseTests.cs └── i18n │ ├── de.json │ ├── en.json │ ├── es.json │ ├── fr.json │ ├── it.json │ ├── ja.json │ ├── ko.json │ ├── nl.json │ ├── pt.json │ ├── ru.json │ ├── sv.json │ ├── zh.json │ └── zht.json ├── AudioNodes ├── AudioBooks │ └── CreateAudioBook.cs ├── AudioFormatInfo.cs ├── AudioInfo.cs ├── AudioInfoHelper.cs ├── AudioNodes.csproj ├── ExtensionMethods.cs ├── GlobalUsings.cs ├── Helpers │ ├── IFFmpegHelper.cs │ └── MetadataHelper.cs ├── InputNodes │ └── AudioFile.cs ├── Nodes │ ├── AudioBitrateMatches.cs │ ├── AudioFileNormalization.cs │ ├── AudioNode.cs │ ├── ConvertFlowElements │ │ ├── ConvertAudio.cs │ │ ├── ConvertNode.cs │ │ ├── ConvertToAAC.cs │ │ ├── ConvertToALAC.cs │ │ ├── ConvertToFLAC.cs │ │ ├── ConvertToMP3.cs │ │ ├── ConvertToOGG.cs │ │ └── ConvertToWAV.cs │ └── EmbedArtwork.cs ├── Plugin.cs ├── Tests │ ├── AudioBitrateMatchesTests.cs │ ├── AudioFileNormalizationTests.cs │ ├── AudioInfoTests.cs │ ├── ConvertTests.cs │ ├── CreateAudioBookTests.cs │ ├── EmbedArtworkTests.cs │ ├── Resources │ │ ├── Book │ │ │ ├── Chapter 01.mp3 │ │ │ ├── Chapter 02.mp3 │ │ │ └── Chapter 03.mp3 │ │ ├── audio.flac │ │ ├── audio.jpg │ │ ├── audio.mp3 │ │ ├── audio.ogg │ │ ├── audio.wav │ │ ├── metadata.flac │ │ └── metadata.mp3 │ └── _AudioTestBase.cs └── i18n │ ├── de.json │ ├── en.json │ ├── es.json │ ├── fr.json │ ├── it.json │ ├── ja.json │ ├── ko.json │ ├── nl.json │ ├── pt.json │ ├── ru.json │ ├── sv.json │ ├── zh.json │ └── zht.json ├── BasicNodes ├── BasicNodes.csproj ├── Conditions │ ├── IfBase.cs │ ├── IfBoolean.cs │ └── IfString.cs ├── File │ ├── CopyFile.cs │ ├── Delete.cs │ ├── DeleteOriginal.cs │ ├── DeleteSourceDirectory.cs │ ├── FileDateCompare.cs │ ├── FileExists.cs │ ├── FileExtension.cs │ ├── FileNameMatches.cs │ ├── FileSize.cs │ ├── FileSizeCompare.cs │ ├── FileSizeWithin.cs │ ├── FolderDateCompare.cs │ ├── HasHardLinks.cs │ ├── InputFile.cs │ ├── InputFolder.cs │ ├── MoveFile.cs │ ├── MoveFolder.cs │ ├── OriginalFile.cs │ ├── PatternReplacer.cs │ ├── Renamer.cs │ ├── ReplaceOriginal.cs │ ├── SetWorkingFile.cs │ ├── Touch.cs │ ├── VariableExists.cs │ └── WriteText.cs ├── FileProperties │ ├── FilePropertyExists.cs │ ├── FilePropertyMatches.cs │ └── SetFileProperty.cs ├── FlowFailure.cs ├── Functions │ ├── CompleteFlow.cs │ ├── FailFlow.cs │ ├── FolderIterator.cs │ ├── GotoFlow.cs │ ├── IsFromLibrary.cs │ ├── IsProcessingOnNode.cs │ ├── ListIterator.cs │ ├── Matches.cs │ ├── MatchesAll.cs │ ├── PatternMatch.cs │ ├── Random.cs │ ├── Reprocess.cs │ ├── SetFileFlowsThumbnail.cs │ ├── SetVariable.cs │ ├── Sleep.cs │ ├── Tag.cs │ └── VariableMatch.cs ├── GlobalUsings.cs ├── Helpers │ └── FolderHelper.cs ├── Logging │ ├── Log.cs │ ├── LogImage.cs │ └── LogVariables.cs ├── Plugin.cs ├── Scripting │ ├── BatchScript.cs │ ├── CSharpScript.cs │ ├── Function.cs │ ├── PowerShellScript.cs │ ├── ScriptBase.cs │ └── ShellScript.cs ├── SystemElements │ ├── IsDocker.cs │ ├── IsLinux.cs │ ├── IsMacOS.cs │ └── IsWindows.cs ├── Templating │ ├── OutputPath.cs │ └── TemplatingNode.cs ├── Tests │ ├── AdditionalFilesTests.cs │ ├── DeleteSourceDirectoryTests.cs │ ├── ExecutorTests.cs │ ├── FileExistsTests.cs │ ├── FileExtensionTests.cs │ ├── FileSizeCompareTests.cs │ ├── FileSizeTests.cs │ ├── HasHardLinksTest.cs │ ├── LogVariablesTests.cs │ ├── MatchAllTests.cs │ ├── MatchesTests.cs │ ├── MoveTests.cs │ ├── MoveTests2.cs │ ├── PatternMatchTests.cs │ ├── PatternReplacerTests.cs │ ├── RenamerTests.cs │ ├── TouchTests.cs │ ├── VariableMatchTests.cs │ ├── WebRequestTests.cs │ └── WriteTextTests.cs ├── Tools │ ├── Executor.cs │ ├── SevenZip.cs │ ├── Unpack.cs │ └── Zip.cs └── i18n │ ├── de.json │ ├── en.json │ ├── es.json │ ├── fr.json │ ├── it.json │ ├── ja.json │ ├── ko.json │ ├── nl.json │ ├── pt.json │ ├── ru.json │ ├── sv.json │ ├── zh.json │ └── zht.json ├── ChecksumNodes ├── ChecksumNodes.csproj ├── GlobalUsings.cs ├── Nodes │ ├── MD5.cs │ ├── SHA1.cs │ ├── SHA256.cs │ └── SHA512.cs ├── Plugin.cs ├── Tests │ └── ChecksumTests.cs └── i18n │ ├── de.json │ ├── en.json │ ├── es.json │ ├── fr.json │ ├── it.json │ ├── ja.json │ ├── ko.json │ ├── nl.json │ ├── pt.json │ ├── ru.json │ ├── sv.json │ ├── zh.json │ └── zht.json ├── ComicNodes ├── ComicInfo.cs ├── ComicNodes.csproj ├── Comics │ ├── ComicConverter.cs │ ├── ComicExtractor.cs │ └── CreateComicInfo.cs ├── ExtensionMethods.cs ├── GlobalUsings.cs ├── Helpers │ ├── ComicExtractor.cs │ ├── GenericExtractor.cs │ ├── PageNameHelper.cs │ ├── PdfHelper.cs │ └── ZipHelper.cs ├── Plugin.cs ├── Tests │ ├── ComicInfoTests.cs │ ├── ComicTests.cs │ └── ExtractTests.cs └── i18n │ ├── de.json │ ├── en.json │ ├── es.json │ ├── fr.json │ ├── it.json │ ├── ja.json │ ├── ko.json │ ├── nl.json │ ├── pt.json │ ├── ru.json │ ├── sv.json │ ├── zh.json │ └── zht.json ├── DiscordNodes ├── Communication │ └── Discord.cs ├── DiscordNodes.csproj ├── ExtensionMethods.cs ├── GlobalUsings.cs ├── IDiscordApi.cs ├── Plugin.cs ├── PluginSettings.cs ├── Tests │ └── DiscordTests.cs └── i18n │ ├── de.json │ ├── en.json │ ├── es.json │ ├── fr.json │ ├── it.json │ ├── ja.json │ ├── ko.json │ ├── nl.json │ ├── pt.json │ ├── ru.json │ ├── sv.json │ ├── zh.json │ └── zht.json ├── Docker ├── Docker.csproj ├── FlowElements │ └── DockerExecute.cs ├── Plugin.cs └── i18n │ ├── de.json │ ├── en.json │ ├── es.json │ ├── fr.json │ ├── it.json │ ├── ja.json │ ├── ko.json │ ├── nl.json │ ├── pt.json │ ├── ru.json │ ├── sv.json │ ├── zh.json │ └── zht.json ├── EmailNodes ├── Communication │ └── SendEmail.cs ├── EmailNodes.csproj ├── ExtensionMethods.cs ├── GlobalUsings.cs ├── Plugin.cs ├── PluginSettings.cs ├── Tests │ └── EmailTests.cs └── i18n │ ├── de.json │ ├── en.json │ ├── es.json │ ├── fr.json │ ├── it.json │ ├── ja.json │ ├── ko.json │ ├── nl.json │ ├── pt.json │ ├── ru.json │ ├── sv.json │ ├── zh.json │ └── zht.json ├── Emby ├── Emby.csproj ├── ExtensionMethods.cs ├── GlobalUsings.cs ├── MediaManagement │ └── EmbyUpdater.cs ├── Plugin.cs ├── PluginSettings.cs ├── Tests │ ├── EmbyTests.cs │ └── MockHttpMessageHandler.cs └── i18n │ ├── de.json │ ├── en.json │ ├── es.json │ ├── fr.json │ ├── it.json │ ├── ja.json │ ├── ko.json │ ├── nl.json │ ├── pt.json │ ├── ru.json │ ├── sv.json │ ├── zh.json │ └── zht.json ├── FileDrop ├── FileDrop.csproj ├── FlowElements │ ├── MoveToUserFolder.cs │ └── SetFileDropDisplayName.cs ├── GlobalUsings.cs ├── Plugin.cs └── i18n │ ├── de.json │ ├── en.json │ ├── es.json │ ├── fr.json │ ├── it.json │ ├── ja.json │ ├── ko.json │ ├── nl.json │ ├── pt.json │ ├── ru.json │ ├── sv.json │ ├── zh.json │ └── zht.json ├── FileFlows.Common.dll ├── FileFlows.Common.pdb ├── FileFlows.Plugin.deps.json ├── FileFlows.Plugin.dll ├── FileFlows.Plugin.pdb ├── FileFlowsPlugins.sln ├── Gotify ├── Communication │ └── Gotify.cs ├── ExtensionMethods.cs ├── GlobalUsings.cs ├── Gotify.csproj ├── Plugin.cs ├── PluginSettings.cs ├── Tests │ └── GotifyTests.cs └── i18n │ ├── de.json │ ├── en.json │ ├── es.json │ ├── fr.json │ ├── it.json │ ├── ja.json │ ├── ko.json │ ├── nl.json │ ├── pt.json │ ├── ru.json │ ├── sv.json │ ├── zh.json │ └── zht.json ├── ImageNodes ├── ExtensionMethods.cs ├── GlobalUsings.cs ├── ImageNodes.csproj ├── Images │ ├── AutoCropImage.cs │ ├── Constants.cs │ ├── ImageBaseNode.cs │ ├── ImageConvert.cs │ ├── ImageFile.cs │ ├── ImageFlip.cs │ ├── ImageIsLandscape.cs │ ├── ImageIsPortrait.cs │ ├── ImageNode.cs │ ├── ImageResizer.cs │ ├── ImageRotate.cs │ ├── IsImage.cs │ └── PixelCheck.cs ├── Plugin.cs ├── Tests │ └── ImageTests.cs └── i18n │ ├── de.json │ ├── en.json │ ├── es.json │ ├── fr.json │ ├── it.json │ ├── ja.json │ ├── ko.json │ ├── nl.json │ ├── pt.json │ ├── ru.json │ ├── sv.json │ ├── zh.json │ └── zht.json ├── MetaNodes ├── AniList │ ├── AniListAiringSchedule.cs │ ├── AniListDateData.cs │ ├── AniListEpisodeData.cs │ ├── AniListEpisodeInfo.cs │ ├── AniListEpisodeResponse.cs │ ├── AniListInterface.cs │ ├── AniListMediaData.cs │ ├── AniListMediaEpisodeData.cs │ ├── AniListShowInfo.cs │ └── AniListTitle.cs ├── AniListElements │ ├── AnimeEpisodeLookup.cs │ └── AnimeShowLookup.cs ├── ExtensionMethods.cs ├── Globals.cs ├── Helpers │ └── TVShowHelper.cs ├── MetaNodes.csproj ├── Music │ └── MusicMeta.cs ├── Plugin.cs ├── Tests │ ├── AniListTests │ │ ├── AniListEpisodeLookupTests.cs │ │ └── AnimeShowLookupIntegrationTests.cs │ └── TheMovieDb │ │ ├── MovieLookupTests.cs │ │ ├── TVEpisodeLookupTests.cs │ │ └── TVShowLookupTests.cs ├── TheMovieDb │ ├── GenreMatches.cs │ ├── MovieLookup.cs │ ├── NfoFileCreator.cs │ ├── TVEpisodeLookup.cs │ └── TVShowLookup.cs ├── ThirdParty │ └── TheMovieDbWrapper │ │ ├── ApiRequest │ │ ├── ApiRequestBase.cs │ │ ├── IApiRequest.cs │ │ └── IsoDateTimeConverterEx.cs │ │ ├── ApiResponse │ │ ├── ApiError.cs │ │ ├── ApiQueryResponse.cs │ │ ├── ApiResponseBase.cs │ │ ├── ApiSearchResponse.cs │ │ └── TmdbStatusCode.cs │ │ ├── IApiSettings.cs │ │ ├── IMovieDbApi.cs │ │ ├── LICENSE │ │ ├── MovieDb │ │ ├── Certifications │ │ │ ├── ApiMovieRatingRequest.cs │ │ │ ├── IApiMovieRatingRequest.cs │ │ │ └── MovieRatings.cs │ │ ├── Collections │ │ │ └── CollectionInfo.cs │ │ ├── Companies │ │ │ ├── ApiCompanyRequest.cs │ │ │ ├── IApiCompanyRequest.cs │ │ │ ├── ParentCompany.cs │ │ │ ├── ProductionCompany.cs │ │ │ └── ProductionCompanyInfo.cs │ │ ├── Configuration │ │ │ ├── ApiConfiguration.cs │ │ │ ├── ApiConfigurationRequest.cs │ │ │ ├── IApiConfigurationRequest.cs │ │ │ └── ImageConfiguration.cs │ │ ├── Country.cs │ │ ├── Discover │ │ │ ├── ApiDiscoverRequest.cs │ │ │ ├── DiscoverEnums.cs │ │ │ ├── DiscoverMovieParameterBuilder.cs │ │ │ └── IApiDiscoverRequest.cs │ │ ├── Genres │ │ │ ├── ApiGenreRequest.cs │ │ │ ├── Genre.cs │ │ │ ├── GenreFactory.cs │ │ │ ├── GenreIdCollectionMappingExtensions.cs │ │ │ └── IApiGenreRequest.cs │ │ ├── IndustryProfessions │ │ │ ├── ApiProfessionRequest.cs │ │ │ ├── IApiProfessionRequest.cs │ │ │ └── Profession.cs │ │ ├── Keywords │ │ │ ├── Keyword.cs │ │ │ └── KeywordConverter.cs │ │ ├── Language.cs │ │ ├── Movies │ │ │ ├── ApiMovieRequest.cs │ │ │ ├── IApiMovieRequest.cs │ │ │ ├── Movie.cs │ │ │ ├── MovieCredit.cs │ │ │ └── MovieInfo.cs │ │ ├── People │ │ │ ├── ApiPeopleRequest.cs │ │ │ ├── Gender.cs │ │ │ ├── IApiPeopleRequest.cs │ │ │ ├── Person.cs │ │ │ ├── PersonInfo.cs │ │ │ ├── PersonMovieCredit.cs │ │ │ └── PersonTVCredit.cs │ │ └── TV │ │ │ ├── ApiTVShowRequest.cs │ │ │ ├── Crew.cs │ │ │ ├── Episode.cs │ │ │ ├── GuestStars.cs │ │ │ ├── IApiTVShowRequest.cs │ │ │ ├── Network.cs │ │ │ ├── Season.cs │ │ │ ├── SeasonInfo.cs │ │ │ ├── TVShow.cs │ │ │ ├── TVShowCreator.cs │ │ │ └── TVShowInfo.cs │ │ ├── MovieDbApi.cs │ │ ├── MovieDbFactory.cs │ │ ├── Shims │ │ ├── CollectionExtensions.cs │ │ ├── EnumExtensions.cs │ │ └── ImportingConstructorAttribute.cs │ │ └── info ├── VideoMetadata.cs └── i18n │ ├── de.json │ ├── en.json │ ├── es.json │ ├── fr.json │ ├── it.json │ ├── ja.json │ ├── ko.json │ ├── nl.json │ ├── pt.json │ ├── ru.json │ ├── sv.json │ ├── zh.json │ └── zht.json ├── Nextcloud ├── ExtensionMethods.cs ├── FlowElements │ └── UploadToNextcloud.cs ├── GlobalUsings.cs ├── Helpers │ └── NextcloudUploader.cs ├── Nextcloud.csproj ├── Plugin.cs ├── PluginSettings.cs ├── Tests │ └── UploadTest.cs └── i18n │ ├── de.json │ ├── en.json │ ├── es.json │ ├── fr.json │ ├── it.json │ ├── ja.json │ ├── ko.json │ ├── nl.json │ ├── pt.json │ ├── ru.json │ ├── sv.json │ ├── zh.json │ └── zht.json ├── Pdf ├── FlowElements │ ├── PdfContainsText.cs │ ├── PdfMatchesText.cs │ └── PdfToTextFile.cs ├── GlobalUsings.cs ├── Pdf.csproj ├── Plugin.cs └── i18n │ ├── de.json │ ├── en.json │ ├── es.json │ ├── fr.json │ ├── it.json │ ├── ja.json │ ├── ko.json │ ├── nl.json │ ├── pt.json │ ├── ru.json │ ├── sv.json │ ├── zh.json │ └── zht.json ├── Plex ├── ExtensionMethods.cs ├── GlobalUsings.cs ├── MediaManagement │ ├── PlexAnalyze.cs │ ├── PlexUpdater.cs │ └── _PlexNode.cs ├── Models │ ├── PlexDirectory.cs │ ├── PlexMedia.cs │ ├── PlexMetadata.cs │ └── PlexSections.cs ├── Plex.csproj ├── Plugin.cs ├── PluginSettings.cs ├── Tests │ ├── PlexAnayzeTests.cs │ └── PlexUpdaterTests.cs └── i18n │ ├── de.json │ ├── en.json │ ├── es.json │ ├── fr.json │ ├── it.json │ ├── ja.json │ ├── ko.json │ ├── nl.json │ ├── pt.json │ ├── ru.json │ ├── sv.json │ ├── zh.json │ └── zht.json ├── PluginTestLibrary ├── GlobalUsings.cs ├── PluginTestLibrary.csproj ├── _LocalFileService.cs ├── _TestBase.cs └── _TestLogger.cs ├── Pushbullet ├── Communication │ └── Pushbullet.cs ├── ExtensionMethods.cs ├── GlobalUsings.cs ├── Plugin.cs ├── PluginSettings.cs ├── Pushbullet.csproj ├── Tests │ └── PushbulletTests.cs └── i18n │ ├── de.json │ ├── en.json │ ├── es.json │ ├── fr.json │ ├── it.json │ ├── ja.json │ ├── ko.json │ ├── nl.json │ ├── pt.json │ ├── ru.json │ ├── sv.json │ ├── zh.json │ └── zht.json ├── Pushover ├── Communication │ └── Pushover.cs ├── ExtensionMethods.cs ├── GlobalUsings.cs ├── Plugin.cs ├── PluginSettings.cs ├── Pushover.csproj ├── Tests │ └── PushoverTests.cs └── i18n │ ├── de.json │ ├── en.json │ ├── es.json │ ├── fr.json │ ├── it.json │ ├── ja.json │ ├── ko.json │ ├── nl.json │ ├── pt.json │ ├── ru.json │ ├── sv.json │ ├── zh.json │ └── zht.json ├── Telegram ├── Communication │ └── Telegram.cs ├── ExtensionMethods.cs ├── GlobalUsings.cs ├── Plugin.cs ├── PluginSettings.cs ├── Telegram.csproj ├── Tests │ └── TelegramTests.cs └── i18n │ ├── de.json │ ├── en.json │ ├── es.json │ ├── fr.json │ ├── it.json │ ├── ja.json │ ├── ko.json │ ├── nl.json │ ├── pt.json │ ├── ru.json │ ├── sv.json │ ├── zh.json │ └── zht.json ├── VideoNodes ├── ExtensionMethods.cs ├── FFMpegEncoder.cs ├── FfmpegBuilderNodes │ ├── Audio │ │ ├── FfmpegBuilderAudioAddTrack.cs │ │ ├── FfmpegBuilderAudioAdjustVolume.cs │ │ ├── FfmpegBuilderAudioConvert.cs │ │ ├── FfmpegBuilderAudioConverter.cs │ │ ├── FfmpegBuilderAudioLanguageConverter.cs │ │ ├── FfmpegBuilderAudioNormalization.cs │ │ ├── FfmpegBuilderAudioSetLanguage.cs │ │ └── FfmpegBuilderAudioTrackRemover.cs │ ├── EncoderAdjustments │ │ ├── EncoderAdjustment.cs │ │ ├── IEncoderAdjustment.cs │ │ └── VaapiAdjustments.cs │ ├── FFmpegBuilderDurationStart.cs │ ├── FFmpegBuilderLanguageRemover.cs │ ├── FFmpegBuilderWatermark.cs │ ├── FfmpegBuilderAddInputFile.cs │ ├── FfmpegBuilderChangeLanguageCode.cs │ ├── FfmpegBuilderCustomParameters.cs │ ├── FfmpegBuilderDefaultOriginalLanguage.cs │ ├── FfmpegBuilderExecutor.cs │ ├── FfmpegBuilderKeepOriginalLanguage.cs │ ├── FfmpegBuilderMetadataRemover.cs │ ├── FfmpegBuilderNode.cs │ ├── FfmpegBuilderParameterReplacer.cs │ ├── FfmpegBuilderPreExecute.cs │ ├── FfmpegBuilderSetDefaultTrack.cs │ ├── FfmpegBuilderSetDevice.cs │ ├── FfmpegBuilderSetTrackTitles.cs │ ├── FfmpegBuilderStart.cs │ ├── FfmpegBuilderTrackSorter.cs │ ├── Metadata │ │ ├── FfmpegBuilderAutoChapters.cs │ │ ├── FfmpegBuilderComskipChapters.cs │ │ ├── FfmpegBuilderRemoveAttachments.cs │ │ └── FfmpegBuilderVideoMetadata.cs │ ├── Models │ │ ├── FfmpegAudioStream.cs │ │ ├── FfmpegModel.cs │ │ ├── FfmpegStream.cs │ │ ├── FfmpegSubtitleStream.cs │ │ └── FfmpegVideoStream.cs │ ├── PreExecutor.cs │ ├── Subtitle │ │ ├── FfmpegBuilderRemoveUnsupportedSubtitles.cs │ │ ├── FfmpegBuilderSubtitleBurnIn.cs │ │ ├── FfmpegBuilderSubtitleClearDefault.cs │ │ ├── FfmpegBuilderSubtitleFormatRemover.cs │ │ └── FfmpegBuilderSubtitleTrackMerge.cs │ ├── TrackSelectorFlowElement.cs │ └── Video │ │ ├── FFmpegBuilderVideoBitrateEncode │ │ ├── AV1.cs │ │ ├── FfmpegBuilderVideoBitrateEncode.cs │ │ ├── VP9.cs │ │ └── h26x.cs │ │ ├── FfmpegBuilderAspectRatio.cs │ │ ├── FfmpegBuilderCropBlackBars.cs │ │ ├── FfmpegBuilderDeinterlace.cs │ │ ├── FfmpegBuilderHdr10.cs │ │ ├── FfmpegBuilderHdrToSdr.cs │ │ ├── FfmpegBuilderProres.cs │ │ ├── FfmpegBuilderRemuxToMP4.cs │ │ ├── FfmpegBuilderRemuxToMkv.cs │ │ ├── FfmpegBuilderRemuxToMov.cs │ │ ├── FfmpegBuilderRemuxToMxf.cs │ │ ├── FfmpegBuilderRemuxToWebm.cs │ │ ├── FfmpegBuilderScaler.cs │ │ ├── FfmpegBuilderSetFps.cs │ │ ├── FfmpegBuilderTrackRemover.cs │ │ ├── FfmpegBuilderVideoBitrate.cs │ │ ├── FfmpegBuilderVideoCodec.cs │ │ ├── FfmpegBuilderVideoEncode │ │ ├── AV1.cs │ │ ├── FfmpegBuilderVideoEncode.cs │ │ ├── VP9.cs │ │ └── h26x.cs │ │ ├── FfmpegBuilderVideoEncodeManual.cs │ │ ├── FfmpegBuilderVideoEncodeSimple │ │ ├── AV1.cs │ │ ├── FfmpegBuilderVideoEncodeSimple.cs │ │ ├── VP9.cs │ │ └── h26x.cs │ │ ├── FfmpegBuilderVideoMaxBitrate.cs │ │ ├── FfmpegBuilderVideoTag.cs │ │ ├── HardwareEncoders │ │ ├── DisableAmd.cs │ │ ├── DisableEncoder.cs │ │ ├── DisableIntelQsv.cs │ │ ├── DisableNvidia.cs │ │ └── DisableVaapi.cs │ │ └── _VideoEncodeBase.cs ├── GlobalUsings.cs ├── Globals.cs ├── Helpers │ ├── ChannelHelper.cs │ ├── ComskipHelper.cs │ ├── FFmpegParameterHelper.cs │ ├── GeneralHelper.cs │ ├── SubtitleHelper.cs │ ├── VaapiHelper.cs │ └── VideoHelper.cs ├── ITrackSelectionNode.cs ├── InputNodes │ └── VideoFile.cs ├── LanguageCodes.cs ├── LogicalNodes │ ├── CanUseHardwareEncoding.cs │ ├── SizePerHour.cs │ ├── VideoAlreadyProcessed.cs │ ├── VideoBitCheck.cs │ ├── VideoBitrateGreaterThan.cs │ ├── VideoDuration.cs │ ├── VideoDurationCompare.cs │ ├── VideoHasAudio.cs │ ├── VideoHasErrors.cs │ ├── VideoHasStream.cs │ ├── VideoIs10Bit.cs │ ├── VideoIs12Bit.cs │ ├── VideoIs8Bit.cs │ ├── VideoIsAV1.cs │ ├── VideoIsAvi.cs │ ├── VideoIsCodec.cs │ ├── VideoIsDolbyVision.cs │ ├── VideoIsH264.cs │ ├── VideoIsHdr.cs │ ├── VideoIsHevc.cs │ ├── VideoIsInterlaced.cs │ ├── VideoIsMkv.cs │ ├── VideoIsMov.cs │ ├── VideoIsMp4.cs │ ├── VideoIsMpeg.cs │ ├── VideoIsMxf.cs │ ├── VideoIsTs.cs │ ├── VideoIsWebm.cs │ ├── VideoIsWmv.cs │ └── VideoResolution.cs ├── Plugin.cs ├── ResolutionHelper.cs ├── StaticMethods.cs ├── Tests │ ├── AudioExtractorTests.cs │ ├── AudioToVideoTests.cs │ ├── ComSkipTests.cs │ ├── CreateThumbnailTests.cs │ ├── EndCreditsTests.cs │ ├── FFmpegParameterHelperTests.cs │ ├── FfmpegBuilderTests │ │ ├── FFmpegBuilder_AspectRatioTests.cs │ │ ├── FFmpegBuilder_ChangeLanguageCodeTests.cs │ │ ├── FFmpegBuilder_ErrorFile.cs │ │ ├── FFmpegBuilder_SetTrackTitlesTests.cs │ │ ├── FFmpegBuilder_SubtitleBurnInTests.cs │ │ ├── FFmpegBuilder_SubtitleTrackMergeTests.cs │ │ ├── FFmpegBuilder_TrackRemoverTests.cs │ │ ├── FfmpegBuilder_AddAudioTests.cs │ │ ├── FfmpegBuilder_AudioConverterTests.cs │ │ ├── FfmpegBuilder_BasicTests.cs │ │ ├── FfmpegBuilder_HdrTests.cs │ │ ├── FfmpegBuilder_KeepOriginalLanguageTests.cs │ │ ├── FfmpegBuilder_LanguageRemoverTests.cs │ │ ├── FfmpegBuilder_MetadataTests.cs │ │ ├── FfmpegBuilder_TrackSorterTests.cs │ │ ├── FfmpegBuilder_VideoEncodeTests.cs │ │ ├── FfmpegBuilder_VideoEncodeTests_AV1.cs │ │ └── FfmpegBuilder_VideoEncodeTests_H26x.cs │ ├── Resources │ │ ├── audio.mp3 │ │ ├── corrupt.mkv │ │ ├── eng_ger_audio.mp4 │ │ ├── hevc.mkv │ │ ├── subtitles.mkv │ │ ├── video.mkv │ │ ├── video.mp4 │ │ ├── video4by3.mp4 │ │ └── video4by7.mkv │ ├── SubtitleExtractorTests.cs │ ├── VaapiAdjustmentTests.cs │ ├── VideoBitrateGreaterThanTests.cs │ ├── VideoHasErrorsTests.cs │ ├── VideoHasStreamTests.cs │ ├── VideoInfoHelperTests.cs │ ├── VideoIsInterlacedTests.cs │ └── _VideoTestBase.cs ├── TrackSelector.cs ├── VideoInfo.cs ├── VideoInfoHelper.cs ├── VideoMetadata.cs ├── VideoNodes.csproj ├── VideoNodes │ ├── AudioSelectionEncodingNode.cs │ ├── AudioToVideo.cs │ ├── ComskipRemoveAds.cs │ ├── CreateThumbnail.cs │ ├── DetectEndCredits.cs │ ├── EncodingNode.cs │ ├── ReadVideoInfo.cs │ ├── SubtitleExtractor.cs │ ├── VideoExtractAudio.cs │ └── VideoNode.cs └── i18n │ ├── de.json │ ├── en.json │ ├── es.json │ ├── fr.json │ ├── it.json │ ├── ja.json │ ├── ko.json │ ├── nl.json │ ├── pt.json │ ├── ru.json │ ├── sv.json │ ├── zh.json │ └── zht.json ├── Web ├── ExtensionMethods.cs ├── FlowElements │ ├── Downloader.cs │ ├── InputUrl.cs │ ├── Parsers │ │ ├── HtmlImageParser.cs │ │ ├── HtmlLinkParser.cs │ │ └── HtmlParser.cs │ └── WebRequest.cs ├── GlobalUsings.cs ├── Helpers │ ├── DownloadHelper.cs │ ├── HtmlHelper.cs │ └── JsonUtils.cs ├── Plugin.cs ├── Tests │ └── DownloadUrlTests.cs ├── Web.csproj └── i18n │ ├── de.json │ ├── en.json │ ├── es.json │ ├── fr.json │ ├── it.json │ ├── ja.json │ ├── ko.json │ ├── nl.json │ ├── pt.json │ ├── ru.json │ ├── sv.json │ ├── zh.json │ └── zht.json └── plugins.json /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## CLA 2 | 3 | [X] I agree that by opening a pull requests I am handing over copyright ownership of my work contained in that pull request to the FileFlows project and the project owner. My contribution will become licensed under the same license as the overall project. 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Plugin.dll 2 | */bin 3 | */obj 4 | */.vs 5 | *.suo 6 | TestStore/ 7 | .vs/FileFlowsPlugins/v17/.futdcache.v1 8 | .vs/FileFlowsPlugins/DesignTimeBuild/.dtbcache.v2 9 | .vs/FileFlowsPlugins/project-colors.json 10 | /Plugin.pdb 11 | deploy/** 12 | *.ffplugin 13 | DiscordNodes/settings.json 14 | Gotify/settings.json 15 | Plex/settings.json 16 | Emby/settings.invalid.json 17 | Emby/settings.json 18 | VideoNodes/test.settings.dev.json 19 | Apprise/settings.json 20 | build/utils/PluginInfoGenerator/ 21 | .idea/ -------------------------------------------------------------------------------- /Apprise/ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Apprise; 2 | 3 | /// 4 | /// Extension methods 5 | /// 6 | internal static class ExtensionMethods 7 | { 8 | /// 9 | /// Returns an empty string as null, otherwise returns the original string 10 | /// 11 | /// the input string 12 | /// the string or null if empty 13 | public static string? EmptyAsNull(this string str) 14 | { 15 | return str == string.Empty ? null : str; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Apprise/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Text; 3 | global using System.ComponentModel.DataAnnotations; 4 | global using FileFlows.Plugin; 5 | global using FileFlows.Plugin.Attributes; 6 | global using FileFlows.Common; -------------------------------------------------------------------------------- /Apprise/Plugin.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Apprise; 2 | 3 | /// 4 | /// Apprise Plugin 5 | /// 6 | public class Plugin : FileFlows.Plugin.IPlugin 7 | { 8 | /// 9 | public Guid Uid => new Guid("32d0e2ad-7617-4b52-bc39-338d2cfe468c"); 10 | /// 11 | public string Name => "Apprise"; 12 | /// 13 | public string MinimumVersion => "1.0.4.2019"; 14 | /// 15 | public string Icon => "svg:apprise"; 16 | 17 | /// 18 | public void Init() 19 | { 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Apprise/PluginSettings.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Apprise; 2 | 3 | using FileFlows.Plugin; 4 | using FileFlows.Plugin.Attributes; 5 | using System.ComponentModel.DataAnnotations; 6 | 7 | /// 8 | /// The plugin settings for Apprise 9 | /// 10 | public class PluginSettings:IPluginSettings 11 | { 12 | /// 13 | /// Gets or sets the URL of the Apprise server 14 | /// 15 | [Text(1)] 16 | [Required] 17 | public string ServerUrl { get; set; } = string.Empty; 18 | 19 | /// 20 | /// Gets or sets the endpoint of the Apprise server 21 | /// 22 | [Text(2)] 23 | [Required] 24 | public string Endpoint { get; set; } = string.Empty; 25 | } 26 | -------------------------------------------------------------------------------- /Apprise/i18n/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Apprise": { 5 | "Description": "Sends a message to a Apprise server.", 6 | "Fields": { 7 | "Message": "Message", 8 | "Message-Help": "The message to send to the Apprise server", 9 | "MessageType": "Type", 10 | "MessageType-Help": "The type of message to be sent", 11 | "Tag": "Tag", 12 | "Tag-Help": "A list of tags to send this message as, if empty it will be tagged with ''all''." 13 | }, 14 | "Outputs": { 15 | "1": "Apprise message sent", 16 | "2": "Apprise message failed to send" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "Apprise": { 23 | "Description": "A plugin that allows you to send messages to a Apprise server.", 24 | "Label": "Apprise", 25 | "Fields": { 26 | "Endpoint": "Endpoint", 27 | "Endpoint-Help": "The endpoint on the Apprise server, for example notify/apprise", 28 | "Endpoint-Placeholder": "notify/apprise", 29 | "ServerUrl": "Server", 30 | "ServerUrl-Help": "The URL of the Apprise server", 31 | "ServerUrl-Placeholder": "http://apprise.lan" 32 | } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /Apprise/i18n/it.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Apprise": { 5 | "Description": "Invia un messaggio a un server Apprise.", 6 | "Fields": { 7 | "Message": "Messaggio", 8 | "Message-Help": "Il messaggio da inviare al server Apprise", 9 | "MessageType": "Tipo", 10 | "MessageType-Help": "Il tipo di messaggio da inviare", 11 | "Tag": "Tag", 12 | "Tag-Help": "Un elenco di tag per inviare questo messaggio, se vuoto verrà etichettato come 'tutti'." 13 | }, 14 | "Outputs": { 15 | "1": "Messaggio Apprise inviato", 16 | "2": "Invio del messaggio Apprise fallito" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "Apprise": { 23 | "Description": "Un plugin che ti consente di inviare messaggi a un server Apprise.", 24 | "Label": "Apprise", 25 | "Fields": { 26 | "Endpoint": "Endpoint", 27 | "Endpoint-Help": "L'endpoint sul server Apprise, ad esempio notify/apprise", 28 | "Endpoint-Placeholder": "notify/apprise", 29 | "ServerUrl": "Server", 30 | "ServerUrl-Help": "L'URL del server Apprise", 31 | "ServerUrl-Placeholder": "http://apprise.lan" 32 | } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /Apprise/i18n/ja.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Apprise": { 5 | "Description": "Appriseサーバーにメッセージを送信します。", 6 | "Fields": { 7 | "Message": "メッセージ", 8 | "Message-Help": "Appriseサーバーに送信するメッセージ", 9 | "MessageType": "タイプ", 10 | "MessageType-Help": "送信するメッセージのタイプ", 11 | "Tag": "タグ", 12 | "Tag-Help": "このメッセージに送信するタグのリスト、空白の場合は 'all' でタグ付けされます。" 13 | }, 14 | "Outputs": { 15 | "1": "Appriseメッセージが送信されました", 16 | "2": "Appriseメッセージの送信に失敗しました" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "Apprise": { 23 | "Description": "Appriseサーバーにメッセージを送信できるプラグインです。", 24 | "Label": "Apprise", 25 | "Fields": { 26 | "Endpoint": "エンドポイント", 27 | "Endpoint-Help": "Appriseサーバー上のエンドポイント、例: notify/apprise", 28 | "Endpoint-Placeholder": "notify/apprise", 29 | "ServerUrl": "サーバー", 30 | "ServerUrl-Help": "AppriseサーバーのURL", 31 | "ServerUrl-Placeholder": "http://apprise.lan" 32 | } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /Apprise/i18n/ko.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Apprise": { 5 | "Description": "Apprise 서버에 메시지를 보냅니다.", 6 | "Fields": { 7 | "Message": "메시지", 8 | "Message-Help": "Apprise 서버에 보낼 메시지", 9 | "MessageType": "유형", 10 | "MessageType-Help": "보낼 메시지의 유형", 11 | "Tag": "태그", 12 | "Tag-Help": "이 메시지를 보낼 태그 목록, 비어 있으면 ''all''로 태그가 지정됩니다." 13 | }, 14 | "Outputs": { 15 | "1": "Apprise 메시지가 전송되었습니다", 16 | "2": "Apprise 메시지 전송에 실패했습니다" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "Apprise": { 23 | "Description": "Apprise 서버에 메시지를 보낼 수 있는 플러그인입니다.", 24 | "Label": "Apprise", 25 | "Fields": { 26 | "Endpoint": "엔드포인트", 27 | "Endpoint-Help": "Apprise 서버의 엔드포인트, 예: notify/apprise", 28 | "Endpoint-Placeholder": "notify/apprise", 29 | "ServerUrl": "서버", 30 | "ServerUrl-Help": "Apprise 서버의 URL", 31 | "ServerUrl-Placeholder": "http://apprise.lan" 32 | } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /Apprise/i18n/ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Apprise": { 5 | "Description": "Отправляет сообщение на сервер Apprise.", 6 | "Fields": { 7 | "Message": "Сообщение", 8 | "Message-Help": "Сообщение для отправки на сервер Apprise", 9 | "MessageType": "Тип", 10 | "MessageType-Help": "Тип отправляемого сообщения", 11 | "Tag": "Тег", 12 | "Tag-Help": "Список тегов для отправки сообщения. Если список пуст, будет использован тег 'all'." 13 | }, 14 | "Outputs": { 15 | "1": "Сообщение Apprise отправлено", 16 | "2": "Не удалось отправить сообщение Apprise" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "Apprise": { 23 | "Description": "Плагин, позволяющий отправлять сообщения на сервер Apprise.", 24 | "Label": "Apprise", 25 | "Fields": { 26 | "Endpoint": "Конечная точка", 27 | "Endpoint-Help": "Конечная точка на сервере Apprise, например notify/apprise", 28 | "Endpoint-Placeholder": "notify/apprise", 29 | "ServerUrl": "Сервер", 30 | "ServerUrl-Help": "URL сервера Apprise", 31 | "ServerUrl-Placeholder": "http://apprise.lan" 32 | } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /Apprise/i18n/zh.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Apprise": { 5 | "Description": "向Apprise服务器发送消息。", 6 | "Fields": { 7 | "Message": "消息", 8 | "Message-Help": "要发送到Apprise服务器的消息", 9 | "MessageType": "类型", 10 | "MessageType-Help": "要发送的消息类型", 11 | "Tag": "标签", 12 | "Tag-Help": "发送此消息的标签列表,如果为空,将使用'tall'标签。" 13 | }, 14 | "Outputs": { 15 | "1": "Apprise消息已发送", 16 | "2": "Apprise消息发送失败" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "Apprise": { 23 | "Description": "允许您向Apprise服务器发送消息的插件。", 24 | "Label": "Apprise", 25 | "Fields": { 26 | "Endpoint": "端点", 27 | "Endpoint-Help": "Apprise服务器上的端点,例如notify/apprise", 28 | "Endpoint-Placeholder": "notify/apprise", 29 | "ServerUrl": "服务器", 30 | "ServerUrl-Help": "Apprise服务器的URL", 31 | "ServerUrl-Placeholder": "http://apprise.lan" 32 | } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /Apprise/i18n/zht.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Apprise": { 5 | "Description": "向 Apprise 伺服器發送消息。", 6 | "Fields": { 7 | "Message": "消息", 8 | "Message-Help": "要發送到 Apprise 伺服器的消息", 9 | "MessageType": "類型", 10 | "MessageType-Help": "要發送的消息類型", 11 | "Tag": "標籤", 12 | "Tag-Help": "發送此消息的標籤列表,如果為空則會標記為 ''all''。" 13 | }, 14 | "Outputs": { 15 | "1": "Apprise 消息已發送", 16 | "2": "Apprise 消息發送失敗" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "Apprise": { 23 | "Description": "一個允許您向 Apprise 伺服器發送消息的插件。", 24 | "Label": "Apprise", 25 | "Fields": { 26 | "Endpoint": "端點", 27 | "Endpoint-Help": "Apprise 伺服器上的端點,例如 notify/apprise", 28 | "Endpoint-Placeholder": "notify/apprise", 29 | "ServerUrl": "伺服器", 30 | "ServerUrl-Help": "Apprise 伺服器的 URL", 31 | "ServerUrl-Placeholder": "http://apprise.lan" 32 | } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /AudioNodes/ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.AudioNodes 2 | { 3 | internal static class ExtensionMethods 4 | { 5 | public static void AddOrUpdate(this Dictionary dict, string key, object value) 6 | { 7 | if (dict.ContainsKey(key)) 8 | dict[key] = value; 9 | else 10 | dict.Add(key, value); 11 | } 12 | public static string? EmptyAsNull(this string str) 13 | { 14 | return str == string.Empty ? null : str; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /AudioNodes/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Text; 3 | global using System.Linq; 4 | global using System.Text.RegularExpressions; 5 | global using System.ComponentModel.DataAnnotations; 6 | global using System.Collections.Generic; 7 | global using FileFlows.Plugin; 8 | global using FileFlows.Plugin.Attributes; 9 | 10 | global using FileFlows.Common; 11 | global using FileHelper = FileFlows.Plugin.Helpers.FileHelper; 12 | #if(DEBUG) 13 | global using PluginTestLibrary; 14 | #endif -------------------------------------------------------------------------------- /AudioNodes/Nodes/ConvertFlowElements/ConvertToAAC.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.AudioNodes; 2 | 3 | public class ConvertToAAC : ConvertNode 4 | { 5 | /// 6 | public override string HelpUrl => "https://fileflows.com/docs/plugins/audio-nodes/convert-to-aac"; 7 | /// 8 | protected override string DefaultExtension => "aac"; 9 | /// 10 | public override string Icon => "svg:aac"; 11 | 12 | /// 13 | /// Gets or sets if high efficiency should be used 14 | /// 15 | [Boolean(6)] 16 | public bool HighEfficiency { get => base.HighEfficiency; set =>base.HighEfficiency = value; } 17 | 18 | } -------------------------------------------------------------------------------- /AudioNodes/Nodes/ConvertFlowElements/ConvertToALAC.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.AudioNodes; 2 | 3 | /// 4 | /// A node for converting an audio file to ALAC (Apple Lossless Audio Codec). 5 | /// 6 | public class ConvertToALAC : ConvertNode 7 | { 8 | /// 9 | public override string HelpUrl => "https://fileflows.com/docs/plugins/audio-nodes/convert-to-alac"; 10 | 11 | /// 12 | protected override string DefaultExtension => "alac"; 13 | 14 | /// 15 | public override string Icon => "svg:alac"; 16 | } -------------------------------------------------------------------------------- /AudioNodes/Nodes/ConvertFlowElements/ConvertToFLAC.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.AudioNodes; 2 | 3 | /// 4 | /// A node used for converting audio files to the FLAC format. 5 | /// 6 | /// 7 | /// This class inherits from the and provides specific implementation 8 | /// details for handling audio conversions to the FLAC format. It defines the default extension 9 | /// for output files as "flac" and uses a specific icon for identification. 10 | /// 11 | public class ConvertToFLAC : ConvertNode 12 | { 13 | /// 14 | public override string HelpUrl => "https://fileflows.com/docs/plugins/audio-nodes/convert-to-flac"; 15 | 16 | /// 17 | protected override string DefaultExtension => "flac"; 18 | 19 | /// 20 | public override string Icon => "svg:flac"; 21 | } 22 | -------------------------------------------------------------------------------- /AudioNodes/Nodes/ConvertFlowElements/ConvertToMP3.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.AudioNodes; 2 | 3 | public class ConvertToMP3 : ConvertNode 4 | { 5 | /// 6 | public override string HelpUrl => "https://fileflows.com/docs/plugins/audio-nodes/convert-to-mp3"; 7 | /// 8 | protected override string DefaultExtension => "mp3"; 9 | 10 | /// 11 | public override string Icon => "svg:mp3"; 12 | } -------------------------------------------------------------------------------- /AudioNodes/Plugin.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.AudioNodes; 2 | 3 | using System.ComponentModel.DataAnnotations; 4 | using FileFlows.Plugin.Attributes; 5 | 6 | public class Plugin : FileFlows.Plugin.IPlugin 7 | { 8 | /// 9 | public Guid Uid => new Guid("d951a39e-4296-4801-ab41-4070b0789465"); 10 | public string Name => "Audio"; 11 | /// 12 | public string MinimumVersion => "1.0.4.2019"; 13 | /// 14 | public string Icon => "svg:audio"; 15 | 16 | /// 17 | public void Init() 18 | { 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /AudioNodes/Tests/CreateAudioBookTests.cs: -------------------------------------------------------------------------------- 1 | #if(DEBUG) 2 | 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | 5 | namespace FileFlows.AudioNodes.Tests; 6 | 7 | 8 | [TestClass] 9 | public class CreateAudioBookTests : AudioTestBase 10 | { 11 | /// 12 | /// Book Dir 13 | /// 14 | protected static readonly string BookDir = ResourcesTestFilesDir + "/Book"; 15 | 16 | [TestMethod] 17 | public void CreateAudioBookTest() 18 | { 19 | var location = new System.IO.FileInfo(this.GetType().Assembly.Location).Directory.FullName; 20 | var bookDir = System.IO.Path.Combine(location, "Tests/Resources/Book"); 21 | RunTest(bookDir); 22 | } 23 | 24 | private void RunTest(string folder, int expected = 1) 25 | { 26 | CreateAudioBook node = new (); 27 | var args = GetAudioNodeParameters(folder, isDirectory: true); 28 | 29 | node.PreExecute(args); 30 | int output = node.Execute(args); 31 | 32 | Assert.AreEqual(expected, output); 33 | 34 | } 35 | } 36 | #endif -------------------------------------------------------------------------------- /AudioNodes/Tests/Resources/Book/Chapter 01.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revenz/FileFlowsPlugins/9a42dee278704c4d0e74a9e4cb546d5acdf2d28f/AudioNodes/Tests/Resources/Book/Chapter 01.mp3 -------------------------------------------------------------------------------- /AudioNodes/Tests/Resources/Book/Chapter 02.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revenz/FileFlowsPlugins/9a42dee278704c4d0e74a9e4cb546d5acdf2d28f/AudioNodes/Tests/Resources/Book/Chapter 02.mp3 -------------------------------------------------------------------------------- /AudioNodes/Tests/Resources/Book/Chapter 03.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revenz/FileFlowsPlugins/9a42dee278704c4d0e74a9e4cb546d5acdf2d28f/AudioNodes/Tests/Resources/Book/Chapter 03.mp3 -------------------------------------------------------------------------------- /AudioNodes/Tests/Resources/audio.flac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revenz/FileFlowsPlugins/9a42dee278704c4d0e74a9e4cb546d5acdf2d28f/AudioNodes/Tests/Resources/audio.flac -------------------------------------------------------------------------------- /AudioNodes/Tests/Resources/audio.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revenz/FileFlowsPlugins/9a42dee278704c4d0e74a9e4cb546d5acdf2d28f/AudioNodes/Tests/Resources/audio.jpg -------------------------------------------------------------------------------- /AudioNodes/Tests/Resources/audio.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revenz/FileFlowsPlugins/9a42dee278704c4d0e74a9e4cb546d5acdf2d28f/AudioNodes/Tests/Resources/audio.mp3 -------------------------------------------------------------------------------- /AudioNodes/Tests/Resources/audio.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revenz/FileFlowsPlugins/9a42dee278704c4d0e74a9e4cb546d5acdf2d28f/AudioNodes/Tests/Resources/audio.ogg -------------------------------------------------------------------------------- /AudioNodes/Tests/Resources/audio.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revenz/FileFlowsPlugins/9a42dee278704c4d0e74a9e4cb546d5acdf2d28f/AudioNodes/Tests/Resources/audio.wav -------------------------------------------------------------------------------- /AudioNodes/Tests/Resources/metadata.flac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revenz/FileFlowsPlugins/9a42dee278704c4d0e74a9e4cb546d5acdf2d28f/AudioNodes/Tests/Resources/metadata.flac -------------------------------------------------------------------------------- /AudioNodes/Tests/Resources/metadata.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revenz/FileFlowsPlugins/9a42dee278704c4d0e74a9e4cb546d5acdf2d28f/AudioNodes/Tests/Resources/metadata.mp3 -------------------------------------------------------------------------------- /BasicNodes/File/InputFolder.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.BasicNodes.File 2 | { 3 | using System.ComponentModel; 4 | using FileFlows.Plugin; 5 | using FileFlows.Plugin.Attributes; 6 | 7 | public class InputFolder: Node 8 | { 9 | public override int Outputs => 1; 10 | public override FlowElementType Type => FlowElementType.Input; 11 | public override string Icon => "far fa-folder"; 12 | public override string HelpUrl => "https://fileflows.com/docs/plugins/basic-nodes/input-folder"; 13 | public override int Execute(NodeParameters args) 14 | { 15 | try 16 | { 17 | if (args.FileService.DirectoryExists(args.WorkingFile).Is(true) == false) 18 | { 19 | args.Logger?.ELog("Directory not found: " + args.WorkingFile); 20 | return -1; 21 | } 22 | return 1; 23 | } 24 | catch (Exception ex) 25 | { 26 | args.Logger?.ELog("Failed in InputDirectory: " + ex.Message + Environment.NewLine + ex.StackTrace); 27 | return -1; 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /BasicNodes/File/OriginalFile.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.BasicNodes.File 2 | { 3 | using System.Text; 4 | using System.Text.RegularExpressions; 5 | using FileFlows.Plugin; 6 | 7 | public class OriginalFile : Node 8 | { 9 | public override int Inputs => 1; 10 | public override int Outputs => 1; 11 | public override string Icon => "fas fa-file"; 12 | public override FlowElementType Type => FlowElementType.Logic; 13 | public override string HelpUrl => "https://fileflows.com/docs/plugins/basic-nodes/original-file"; 14 | 15 | public override int Execute(NodeParameters args) 16 | { 17 | args.SetWorkingFile(args.FileName); 18 | args.Logger?.ILog("Set working file to: " + args.FileName); 19 | return 1; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /BasicNodes/File/Touch.cs: -------------------------------------------------------------------------------- 1 | using FileFlows.Plugin; 2 | using FileFlows.Plugin.Attributes; 3 | 4 | namespace FileFlows.BasicNodes.File; 5 | 6 | public class Touch : Node 7 | { 8 | public override int Inputs => 1; 9 | public override int Outputs => 1; 10 | public override FlowElementType Type => FlowElementType.Process; 11 | public override string Icon => "fas fa-hand-point-right"; 12 | public override string HelpUrl => "https://fileflows.com/docs/plugins/basic-nodes/touch"; 13 | 14 | [TextVariable(1)] 15 | public string FileName { get; set; } 16 | 17 | public override int Execute(NodeParameters args) 18 | { 19 | string filename = args.ReplaceVariables(this.FileName ?? string.Empty, stripMissing: true); 20 | if (string.IsNullOrEmpty(filename)) 21 | filename = args.WorkingFile; 22 | 23 | var result = args.FileService.Touch(filename); 24 | if (result.IsFailed) 25 | { 26 | args.Logger?.ELog("Failed to touch: " + result.Error); 27 | return -1; 28 | } 29 | 30 | return 1; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /BasicNodes/Functions/CompleteFlow.cs: -------------------------------------------------------------------------------- 1 | using FileFlows.Plugin; 2 | 3 | namespace FileFlows.BasicNodes.Functions; 4 | 5 | /// 6 | /// A flow element that simply completes/finishes a flow 7 | /// 8 | public class CompleteFlow : Node 9 | { 10 | /// 11 | public override int Inputs => 1; 12 | /// 13 | public override FlowElementType Type => FlowElementType.Logic; 14 | /// 15 | public override string Icon => "fas fa-check-square"; 16 | /// 17 | public override string HelpUrl => "https://fileflows.com/docs/plugins/basic-nodes/complete-flow"; 18 | 19 | /// 20 | public override string CustomColor => "var(--success)"; 21 | 22 | /// 23 | public override int Execute(NodeParameters args) 24 | => 0; 25 | } -------------------------------------------------------------------------------- /BasicNodes/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Collections.Generic; 3 | global using System.Linq; 4 | global using System.Net.Http; 5 | global using System.Threading; 6 | global using System.Threading.Tasks; 7 | global using FileFlows.Common; 8 | #if(DEBUG) 9 | global using PluginTestLibrary; 10 | #endif -------------------------------------------------------------------------------- /BasicNodes/Plugin.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.BasicNodes 2 | { 3 | using System.ComponentModel.DataAnnotations; 4 | 5 | public class Plugin : FileFlows.Plugin.IPlugin 6 | { 7 | /// 8 | public Guid Uid => new Guid("789b5213-4ca5-42da-816e-f2117f00cd16"); 9 | /// 10 | public string Name => "Basic"; 11 | /// 12 | public string MinimumVersion => "1.0.4.2019"; 13 | 14 | /// 15 | public string Icon => "svg:basic"; 16 | 17 | /// 18 | public void Init() { } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /BasicNodes/SystemElements/IsDocker.cs: -------------------------------------------------------------------------------- 1 | using FileFlows.Plugin; 2 | 3 | namespace FileFlows.BasicNodes.SystemElements; 4 | 5 | /// 6 | /// A flow to determine if this is running on Docker or not 7 | /// 8 | public class IsDocker : Node 9 | { 10 | /// 11 | public override int Inputs => 1; 12 | /// 13 | public override int Outputs => 2; 14 | /// 15 | public override FlowElementType Type => FlowElementType.Logic; 16 | /// 17 | public override string Group => "System"; 18 | /// 19 | public override string Icon => "fab fa-docker"; 20 | /// 21 | public override string HelpUrl => "https://fileflows.com/docs/plugins/basic-nodes/system/is-docker"; 22 | /// 23 | public override string CustomColor => "#2481e4"; 24 | 25 | /// 26 | public override int Execute(NodeParameters args) 27 | => args.IsDocker ? 1 : 2; 28 | } -------------------------------------------------------------------------------- /BasicNodes/SystemElements/IsLinux.cs: -------------------------------------------------------------------------------- 1 | using FileFlows.Plugin; 2 | 3 | namespace FileFlows.BasicNodes.SystemElements; 4 | 5 | /// 6 | /// A flow to determine if this is running on Linux or not 7 | /// 8 | public class IsLinux : Node 9 | { 10 | /// 11 | public override int Inputs => 1; 12 | /// 13 | public override int Outputs => 2; 14 | /// 15 | public override FlowElementType Type => FlowElementType.Logic; 16 | /// 17 | public override string Group => "System"; 18 | /// 19 | public override string Icon => "fab fa-linux"; 20 | /// 21 | public override string HelpUrl => "https://fileflows.com/docs/plugins/basic-nodes/system/is-linux"; 22 | /// 23 | public override string CustomColor => "#2481e4"; 24 | 25 | /// 26 | public override int Execute(NodeParameters args) 27 | => args.IsDocker == false && OperatingSystem.IsLinux() ? 1 : 2; 28 | } -------------------------------------------------------------------------------- /BasicNodes/SystemElements/IsMacOS.cs: -------------------------------------------------------------------------------- 1 | using FileFlows.Plugin; 2 | 3 | namespace FileFlows.BasicNodes.SystemElements; 4 | 5 | /// 6 | /// A flow to determine if this is running on MacOS or not 7 | /// 8 | public class IsMacOS : Node 9 | { 10 | /// 11 | public override int Inputs => 1; 12 | /// 13 | public override int Outputs => 2; 14 | /// 15 | public override FlowElementType Type => FlowElementType.Logic; 16 | /// 17 | public override string Group => "System"; 18 | /// 19 | public override string Icon => "fab fa-apple"; 20 | /// 21 | public override string HelpUrl => "https://fileflows.com/docs/plugins/basic-nodes/system/is-mac-os"; 22 | /// 23 | public override string CustomColor => "#2481e4"; 24 | 25 | /// 26 | public override int Execute(NodeParameters args) 27 | => OperatingSystem.IsMacOS() ? 1 : 2; 28 | } -------------------------------------------------------------------------------- /BasicNodes/SystemElements/IsWindows.cs: -------------------------------------------------------------------------------- 1 | using FileFlows.Plugin; 2 | 3 | namespace FileFlows.BasicNodes.SystemElements; 4 | 5 | /// 6 | /// A flow to determine if this is running on windows or not 7 | /// 8 | public class IsWindows : Node 9 | { 10 | /// 11 | public override int Inputs => 1; 12 | /// 13 | public override int Outputs => 2; 14 | /// 15 | public override FlowElementType Type => FlowElementType.Logic; 16 | /// 17 | public override string Group => "System"; 18 | /// 19 | public override string Icon => "fab fa-windows"; 20 | /// 21 | public override string HelpUrl => "https://fileflows.com/docs/plugins/basic-nodes/system/is-windows"; 22 | /// 23 | public override string CustomColor => "#2481e4"; 24 | 25 | /// 26 | public override int Execute(NodeParameters args) 27 | => OperatingSystem.IsWindows() ? 1 : 2; 28 | } -------------------------------------------------------------------------------- /BasicNodes/Templating/TemplatingNode.cs: -------------------------------------------------------------------------------- 1 | using FileFlows.Plugin; 2 | 3 | namespace FileFlows.BasicNodes.Templating; 4 | 5 | /// 6 | /// Flow element used for templating 7 | /// 8 | public abstract class TemplatingNode : Node 9 | { 10 | 11 | } -------------------------------------------------------------------------------- /BasicNodes/Tests/FileExistsTests.cs: -------------------------------------------------------------------------------- 1 | #if(DEBUG) 2 | 3 | using FileFlows.BasicNodes.File; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace BasicNodes.Tests; 7 | 8 | [TestClass] 9 | public class FileExistsTests 10 | { 11 | [TestMethod] 12 | public void BasicTest() 13 | { 14 | var logger = new TestLogger(); 15 | var args = new FileFlows.Plugin.NodeParameters(@"c:\test\testfile.mkv", logger, false, string.Empty, null); 16 | 17 | var element = new FileExists(); 18 | element.FileName = "{folder.Orig.FullName}/{file.Orig.FileNameNoExtension}.en.srt"; 19 | element.Execute(args); 20 | } 21 | } 22 | 23 | #endif -------------------------------------------------------------------------------- /BasicNodes/Tests/MoveTests2.cs: -------------------------------------------------------------------------------- 1 |  2 | #if(DEBUG) 3 | 4 | namespace BasicNodes.Tests; 5 | 6 | using FileFlows.Plugin; 7 | using FileFlows.BasicNodes.File; 8 | using Microsoft.VisualStudio.TestTools.UnitTesting; 9 | using System.Text.RegularExpressions; 10 | 11 | [TestClass] 12 | public class MoveTests2 : TestBase 13 | { 14 | 15 | /// 16 | /// Tests that confirms additional files are moved 17 | /// 18 | [TestMethod] 19 | public void MoveTests_AdditionalFiles() 20 | { 21 | var additionalFile = System.IO.Path.Combine(TempPath, Guid.NewGuid() + ".srt"); 22 | System.IO.File.WriteAllText(additionalFile, Guid.NewGuid().ToString()); 23 | var destPath = System.IO.Path.Combine(TempPath, Guid.NewGuid().ToString()); 24 | 25 | var args = new NodeParameters(TempFile, Logger, false, string.Empty, new LocalFileService()); 26 | args.InitFile(TempFile); 27 | 28 | var ele = new MoveFile(); 29 | ele.AdditionalFiles = ["*.srt"]; 30 | ele.DestinationPath = destPath; 31 | var result = ele.Execute(args); 32 | Assert.AreEqual(1, result); 33 | } 34 | } 35 | 36 | #endif -------------------------------------------------------------------------------- /BasicNodes/Tests/TouchTests.cs: -------------------------------------------------------------------------------- 1 | #if(DEBUG) 2 | 3 | namespace BasicNodes.Tests; 4 | 5 | using FileFlows.BasicNodes.File; 6 | using Microsoft.VisualStudio.TestTools.UnitTesting; 7 | 8 | [TestClass] 9 | public class TouchTests : TestBase 10 | { 11 | FileFlows.Plugin.NodeParameters Args; 12 | 13 | protected override void TestStarting() 14 | { 15 | Args = new FileFlows.Plugin.NodeParameters(TempFile, Logger, false, string.Empty, new LocalFileService()); 16 | } 17 | 18 | [TestMethod] 19 | public void Touch_File() 20 | { 21 | Touch node = new (); 22 | node.FileName = TempFile; 23 | var result = node.Execute(Args); 24 | Assert.AreEqual(1, result); 25 | } 26 | 27 | [TestMethod] 28 | public void Touch_Folder() 29 | { 30 | Touch node = new(); 31 | node.FileName = TempPath; 32 | var result = node.Execute(Args); 33 | Assert.AreEqual(1, result); 34 | } 35 | } 36 | 37 | #endif -------------------------------------------------------------------------------- /ChecksumNodes/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Collections.Generic; 3 | global using System.Linq; 4 | global using System.Text; 5 | global using System.Threading.Tasks; 6 | global using FileFlows.Plugin; -------------------------------------------------------------------------------- /ChecksumNodes/Plugin.cs: -------------------------------------------------------------------------------- 1 | namespace ChecksumNodes; 2 | 3 | public class Plugin : IPlugin 4 | { 5 | /// 6 | public Guid Uid => new Guid("5ce1524c-5e7b-40ee-9fc1-2152181490f1"); 7 | /// 8 | public string Name => "Checksum"; 9 | /// 10 | public string MinimumVersion => "1.0.4.2019"; 11 | /// 12 | public string Icon => "fas fa-file-contract"; 13 | 14 | /// 15 | public void Init() 16 | { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ChecksumNodes/i18n/ja.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "MD5Checksum": { 5 | "Description": "作業ファイルのMD5チェックサムを計算し、変数「MD5」と「Checksum」に保存します。", 6 | "Label": "MD5チェックサム", 7 | "Outputs": { 8 | "1": "チェックサムが変数「MD5」と「Checksum」に保存されました" 9 | } 10 | }, 11 | "SHA1Checksum": { 12 | "Description": "作業ファイルのSHA1チェックサムを計算し、変数「SHA1」と「Checksum」に保存します。", 13 | "Label": "SHA1チェックサム", 14 | "Outputs": { 15 | "1": "チェックサムが変数「SHA1」と「Checksum」に保存されました" 16 | } 17 | }, 18 | "SHA256Checksum": { 19 | "Description": "作業ファイルのSHA256チェックサムを計算し、変数「SHA256」と「Checksum」に保存します。", 20 | "Label": "SHA256チェックサム", 21 | "Outputs": { 22 | "1": "チェックサムが変数「SHA256」と「Checksum」に保存されました" 23 | } 24 | }, 25 | "SHA512Checksum": { 26 | "Description": "作業ファイルのSHA512チェックサムを計算し、変数「SHA512」と「Checksum」に保存します。", 27 | "Label": "SHA512チェックサム", 28 | "Outputs": { 29 | "1": "チェックサムが変数「SHA512」と「Checksum」に保存されました" 30 | } 31 | } 32 | } 33 | }, 34 | "Plugins": { 35 | "ChecksumNodes": { 36 | "Description": "ファイルに対してチェックサムを実行する機能を提供するフロー要素。", 37 | "Label": "チェックサム" 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /ChecksumNodes/i18n/ko.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "MD5Checksum": { 5 | "Description": "작업 파일의 MD5 체크섬을 계산하고 이를 변수 \"MD5\"와 \"Checksum\"에 저장합니다.", 6 | "Label": "MD5 체크섬", 7 | "Outputs": { 8 | "1": "체크섬이 변수 \"MD5\"와 \"Checksum\"에 저장됨" 9 | } 10 | }, 11 | "SHA1Checksum": { 12 | "Description": "작업 파일의 SHA1 체크섬을 계산하고 이를 변수 \"SHA1\"와 \"Checksum\"에 저장합니다.", 13 | "Label": "SHA1 체크섬", 14 | "Outputs": { 15 | "1": "체크섬이 변수 \"SHA1\"과 \"Checksum\"에 저장됨" 16 | } 17 | }, 18 | "SHA256Checksum": { 19 | "Description": "작업 파일의 SHA256 체크섬을 계산하고 이를 변수 \"SHA256\"와 \"Checksum\"에 저장합니다.", 20 | "Label": "SHA256 체크섬", 21 | "Outputs": { 22 | "1": "체크섬이 변수 \"SHA256\"과 \"Checksum\"에 저장됨" 23 | } 24 | }, 25 | "SHA512Checksum": { 26 | "Description": "작업 파일의 SHA512 체크섬을 계산하고 이를 변수 \"SHA512\"와 \"Checksum\"에 저장합니다.", 27 | "Label": "SHA512 체크섬", 28 | "Outputs": { 29 | "1": "체크섬이 변수 \"SHA512\"과 \"Checksum\"에 저장됨" 30 | } 31 | } 32 | } 33 | }, 34 | "Plugins": { 35 | "ChecksumNodes": { 36 | "Description": "파일에 대해 체크섬을 실행할 수 있는 흐름 요소입니다.", 37 | "Label": "체크섬" 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /ChecksumNodes/i18n/zh.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "MD5Checksum": { 5 | "Description": "计算工作文件的MD5校验和,并将其存储在变量\"MD5\"和\"Checksum\"中。", 6 | "Label": "MD5校验和", 7 | "Outputs": { 8 | "1": "校验和已存储在变量\"MD5\"和\"Checksum\"中" 9 | } 10 | }, 11 | "SHA1Checksum": { 12 | "Description": "计算工作文件的SHA1校验和,并将其存储在变量\"SHA1\"和\"Checksum\"中。", 13 | "Label": "SHA1校验和", 14 | "Outputs": { 15 | "1": "校验和已存储在变量\"SHA1\"和\"Checksum\"中" 16 | } 17 | }, 18 | "SHA256Checksum": { 19 | "Description": "计算工作文件的SHA256校验和,并将其存储在变量\"SHA256\"和\"Checksum\"中。", 20 | "Label": "SHA256校验和", 21 | "Outputs": { 22 | "1": "校验和已存储在变量\"SHA256\"和\"Checksum\"中" 23 | } 24 | }, 25 | "SHA512Checksum": { 26 | "Description": "计算工作文件的SHA512校验和,并将其存储在变量\"SHA512\"和\"Checksum\"中。", 27 | "Label": "SHA512校验和", 28 | "Outputs": { 29 | "1": "校验和已存储在变量\"SHA512\"和\"Checksum\"中" 30 | } 31 | } 32 | } 33 | }, 34 | "Plugins": { 35 | "ChecksumNodes": { 36 | "Description": "提供针对文件运行校验和的流程元素。", 37 | "Label": "校验和" 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /ChecksumNodes/i18n/zht.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "MD5Checksum": { 5 | "Description": "計算工作文件的MD5校驗和,並將其存儲在變量\"MD5\"和\"Checksum\"中。", 6 | "Label": "MD5 校驗和", 7 | "Outputs": { 8 | "1": "校驗和存儲在變量\"MD5\"和\"Checksum\"中" 9 | } 10 | }, 11 | "SHA1Checksum": { 12 | "Description": "計算工作文件的SHA1校驗和,並將其存儲在變量\"SHA1\"和\"Checksum\"中。", 13 | "Label": "SHA1 校驗和", 14 | "Outputs": { 15 | "1": "校驗和存儲在變量\"SHA1\"和\"Checksum\"中" 16 | } 17 | }, 18 | "SHA256Checksum": { 19 | "Description": "計算工作文件的SHA256校驗和,並將其存儲在變量\"SHA256\"和\"Checksum\"中。", 20 | "Label": "SHA256 校驗和", 21 | "Outputs": { 22 | "1": "校驗和存儲在變量\"SHA256\"和\"Checksum\"中" 23 | } 24 | }, 25 | "SHA512Checksum": { 26 | "Description": "計算工作文件的SHA512校驗和,並將其存儲在變量\"SHA512\"和\"Checksum\"中。", 27 | "Label": "SHA512 校驗和", 28 | "Outputs": { 29 | "1": "校驗和存儲在變量\"SHA512\"和\"Checksum\"中" 30 | } 31 | } 32 | } 33 | }, 34 | "Plugins": { 35 | "ChecksumNodes": { 36 | "Description": "提供對文件運行校驗和的能力的流程元素。", 37 | "Label": "校驗和" 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /ComicNodes/ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Comic; 2 | 3 | internal static class ExtensionMethods 4 | { 5 | public static string? EmptyAsNull(this string str) => str == string.Empty ? null : str; 6 | } 7 | -------------------------------------------------------------------------------- /ComicNodes/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Text; 3 | global using System.ComponentModel.DataAnnotations; 4 | global using FileFlows.Plugin; 5 | global using FileFlows.Plugin.Attributes; 6 | global using FileFlows.Comic; 7 | global using System.Text.RegularExpressions; 8 | global using FileFlows.Common; -------------------------------------------------------------------------------- /ComicNodes/Helpers/PageNameHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | 3 | namespace FileFlows.ComicNodes.Helpers; 4 | 5 | internal class PageNameHelper 6 | { 7 | internal static void FixPageNames(string directory) 8 | { 9 | 10 | var files = new DirectoryInfo(directory).GetFiles(); 11 | foreach (var file in files) 12 | { 13 | var numMatch = Regex.Match(file.Name, @"[\d]+"); 14 | if (numMatch.Success == false) 15 | continue; 16 | // ensure any file that is stupidly name eg page1.jpg, page2.jpg, page10.jpg, page11.jpg etc is ordered correctly 17 | file.MoveTo(Path.Combine(directory, file.Name.Replace(numMatch.Value, int.Parse(numMatch.Value).ToString(new string('0', files.Length.ToString().Length))))); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ComicNodes/Plugin.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Comic; 2 | 3 | public class Plugin : FileFlows.Plugin.IPlugin 4 | { 5 | /// 6 | public Guid Uid => new Guid("3664da0a-b531-47b9-bdc8-e8368d9746ce"); 7 | /// 8 | public string Name => "Comic"; 9 | /// 10 | public string MinimumVersion => "1.0.4.2019"; 11 | /// 12 | public string Icon => "svg:comic"; 13 | 14 | /// 15 | public void Init() 16 | { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /DiscordNodes/ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.DiscordNodes; 2 | 3 | /// 4 | /// Extension methods 5 | /// 6 | internal static class ExtensionMethods 7 | { 8 | /// 9 | /// Treats an empty string as if it was null 10 | /// 11 | /// the input string 12 | /// the string unless it was empty then null 13 | public static string? EmptyAsNull(this string str) => str == string.Empty ? null : str; 14 | } 15 | -------------------------------------------------------------------------------- /DiscordNodes/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Collections.Generic; 3 | global using System.Linq; 4 | global using System.Text; 5 | global using System.Threading.Tasks; 6 | global using System.ComponentModel.DataAnnotations; 7 | global using FileFlows.Plugin; 8 | global using FileFlows.Plugin.Attributes; 9 | global using FileFlows.Common; -------------------------------------------------------------------------------- /DiscordNodes/Plugin.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.DiscordNodes; 2 | 3 | /// 4 | /// The plugin information 5 | /// 6 | public class Plugin : FileFlows.Plugin.IPlugin 7 | { 8 | /// 9 | /// Gets the UID of this plugin 10 | /// 11 | public Guid Uid => new Guid("ebaea108-8783-46b2-a889-be0d79bc8ad6"); 12 | /// 13 | /// Gets the name of this plugin 14 | /// 15 | public string Name => "Discord"; 16 | /// 17 | /// Gets the minimum version this plugin supports 18 | /// 19 | public string MinimumVersion => "1.0.4.2019"; 20 | 21 | /// 22 | public string Icon => "fab fa-discord:#5865F2"; 23 | 24 | /// 25 | /// Initializes this plugin 26 | /// 27 | public void Init() 28 | { 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /DiscordNodes/PluginSettings.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.DiscordNodes; 2 | 3 | /// 4 | /// THe Plugin settings 5 | /// 6 | public class PluginSettings:IPluginSettings 7 | { 8 | /// 9 | /// Gets or sets the webhook id for this plugin 10 | /// 11 | [Text(1)] 12 | [Required] 13 | public string WebhookId { get; set; } = string.Empty; 14 | 15 | /// 16 | /// Gets or sets the webhook token for this plugin 17 | /// 18 | [Text(2)] 19 | [Required] 20 | public string WebhookToken { get; set; } = string.Empty; 21 | } 22 | -------------------------------------------------------------------------------- /DiscordNodes/i18n/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Discord": { 5 | "Description": "Sendet eine Nachricht an einen Discord-Server.", 6 | "Fields": { 7 | "Message": "Nachricht", 8 | "Message-Help": "Die Nachricht, die an den Discord-Server gesendet werden soll.", 9 | "MessageType": "Typ", 10 | "MessageType-Help": "Die Art der zu sendenden Nachricht. Der Grundtyp ist nur die Nachricht ohne Titel und ohne Farbgebung.", 11 | "Title": "Titel", 12 | "Title-Help": "Hier kann ein optionaler Titel festgelegt werden." 13 | }, 14 | "Outputs": { 15 | "1": "Discord-Nachricht gesendet!", 16 | "2": "Die Discord-Nachricht konnte nicht gesendet werden!" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "DiscordNodes": { 23 | "Description": "Mit diesem Plugin können Sie Nachrichten an Discord senden.\n\nDie Webhook Id und der Token kann aus der URL entnommen werden.\n https://discord.com/api/webhooks/[webhookid]/[webhooktoken]", 24 | "Label": "Discord", 25 | "Fields": { 26 | "WebhookId": "Webhook-ID", 27 | "WebhookToken": "Webhook-Token" 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /DiscordNodes/i18n/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Discord": { 5 | "Description": "Sends a message to a Discord server.", 6 | "Fields": { 7 | "Message": "Message", 8 | "Message-Help": "The message to send to the Discord server", 9 | "MessageType": "Type", 10 | "MessageType-Help": "The type of message to send. Basic type will be just the message without a title and no colouring.", 11 | "Title": "Title", 12 | "Title-Help": "An optional title to send, if not set the message type will be the title." 13 | }, 14 | "Outputs": { 15 | "1": "Discord message sent", 16 | "2": "Discord message failed to send" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "DiscordNodes": { 23 | "Description": "A plugin that allows you to send messages to a Discord server.\n\nThe Webhook Id and token can be obtained from the webhook URL\n https://discord.com/api/webhooks/[webhookid]/[webhooktoken]", 24 | "Label": "Discord", 25 | "Fields": { 26 | "WebhookId": "Webhook Id", 27 | "WebhookToken": "Webhook Token" 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /DiscordNodes/i18n/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Discord": { 5 | "Description": "Envía un mensaje a un servidor de Discord.", 6 | "Fields": { 7 | "Message": "Mensaje", 8 | "Message-Help": "El mensaje a enviar al servidor de Discord", 9 | "MessageType": "Tipo", 10 | "MessageType-Help": "El tipo de mensaje a enviar. El tipo básico será solo el mensaje sin título y sin color.", 11 | "Title": "Título", 12 | "Title-Help": "Un título opcional para enviar; si no se establece, el tipo de mensaje será el título." 13 | }, 14 | "Outputs": { 15 | "1": "Mensaje de Discord enviado", 16 | "2": "Error al enviar el mensaje de Discord" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "DiscordNodes": { 23 | "Description": "Un plugin que te permite enviar mensajes a un servidor de Discord.\n\nEl ID del Webhook y el token se pueden obtener de la URL del webhook\n https://discord.com/api/webhooks/[webhookid]/[webhooktoken]", 24 | "Label": "Discord", 25 | "Fields": { 26 | "WebhookId": "ID del Webhook", 27 | "WebhookToken": "Token del Webhook" 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /DiscordNodes/i18n/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Discord": { 5 | "Description": "Envoie un message à un serveur Discord.", 6 | "Fields": { 7 | "Message": "Message", 8 | "Message-Help": "Le message à envoyer au serveur Discord", 9 | "MessageType": "Type", 10 | "MessageType-Help": "Le type de message à envoyer. Le type de base sera juste le message sans titre et sans couleur.", 11 | "Title": "Titre", 12 | "Title-Help": "Un titre optionnel à envoyer; s'il n'est pas défini, le type de message sera le titre." 13 | }, 14 | "Outputs": { 15 | "1": "Message Discord envoyé", 16 | "2": "Échec de l'envoi du message Discord" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "DiscordNodes": { 23 | "Description": "Un plugin qui vous permet d'envoyer des messages à un serveur Discord.\n\nL'ID du Webhook et le token peuvent être obtenus à partir de l'URL du webhook\n https://discord.com/api/webhooks/[webhookid]/[webhooktoken]", 24 | "Label": "Discord", 25 | "Fields": { 26 | "WebhookId": "ID du Webhook", 27 | "WebhookToken": "Token du Webhook" 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /DiscordNodes/i18n/it.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Discord": { 5 | "Description": "Invia un messaggio a un server Discord.", 6 | "Fields": { 7 | "Message": "Messaggio", 8 | "Message-Help": "Il messaggio da inviare al server Discord", 9 | "MessageType": "Tipo", 10 | "MessageType-Help": "Il tipo di messaggio da inviare. Il tipo base sarà solo il messaggio senza un titolo e senza colori.", 11 | "Title": "Titolo", 12 | "Title-Help": "Un titolo opzionale da inviare; se non impostato, il tipo di messaggio sarà il titolo." 13 | }, 14 | "Outputs": { 15 | "1": "Messaggio Discord inviato", 16 | "2": "Invio del messaggio Discord non riuscito" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "DiscordNodes": { 23 | "Description": "Un plugin che ti consente di inviare messaggi a un server Discord.\n\nL'ID del Webhook e il token possono essere ottenuti dall'URL del webhook\n https://discord.com/api/webhooks/[webhookid]/[webhooktoken]", 24 | "Label": "Discord", 25 | "Fields": { 26 | "WebhookId": "ID del Webhook", 27 | "WebhookToken": "Token del Webhook" 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /DiscordNodes/i18n/ja.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Discord": { 5 | "Description": "Discordサーバーにメッセージを送信します。", 6 | "Fields": { 7 | "Message": "メッセージ", 8 | "Message-Help": "Discordサーバーに送信するメッセージ", 9 | "MessageType": "タイプ", 10 | "MessageType-Help": "送信するメッセージのタイプ。基本タイプはタイトルなし、色なしのメッセージになります。", 11 | "Title": "タイトル", 12 | "Title-Help": "送信するオプションのタイトル。設定されていない場合、メッセージタイプはタイトルになります。" 13 | }, 14 | "Outputs": { 15 | "1": "Discordメッセージが送信されました", 16 | "2": "Discordメッセージの送信に失敗しました" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "DiscordNodes": { 23 | "Description": "Discordサーバーにメッセージを送信できるプラグインです。\n\nWebhook IDとトークンは、Webhook URLから取得できます\n https://discord.com/api/webhooks/[webhookid]/[webhooktoken]", 24 | "Label": "Discord", 25 | "Fields": { 26 | "WebhookId": "Webhook ID", 27 | "WebhookToken": "Webhookトークン" 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /DiscordNodes/i18n/ko.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Discord": { 5 | "Description": "디스코드 서버에 메시지를 보냅니다.", 6 | "Fields": { 7 | "Message": "메시지", 8 | "Message-Help": "디스코드 서버에 보낼 메시지", 9 | "MessageType": "유형", 10 | "MessageType-Help": "보낼 메시지의 유형. 기본 유형은 제목이 없고 색상이 없는 메시지입니다.", 11 | "Title": "제목", 12 | "Title-Help": "보낼 선택적 제목입니다. 설정하지 않으면 메시지 유형이 제목으로 설정됩니다." 13 | }, 14 | "Outputs": { 15 | "1": "디스코드 메시지가 전송됨", 16 | "2": "디스코드 메시지 전송 실패" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "DiscordNodes": { 23 | "Description": "디스코드 서버에 메시지를 보낼 수 있는 플러그인입니다.\n\nWebhook ID와 토큰은 웹훅 URL에서 얻을 수 있습니다.\n https://discord.com/api/webhooks/[webhookid]/[webhooktoken]", 24 | "Label": "디스코드", 25 | "Fields": { 26 | "WebhookId": "Webhook ID", 27 | "WebhookToken": "Webhook 토큰" 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /DiscordNodes/i18n/pt.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Discord": { 5 | "Description": "Envia uma mensagem para um servidor Discord.", 6 | "Fields": { 7 | "Message": "Mensagem", 8 | "Message-Help": "A mensagem a ser enviada para o servidor Discord", 9 | "MessageType": "Tipo", 10 | "MessageType-Help": "O tipo de mensagem a ser enviada. O tipo básico será apenas a mensagem sem um título e sem coloração.", 11 | "Title": "Título", 12 | "Title-Help": "Um título opcional a ser enviado; se não definido, o tipo de mensagem será o título." 13 | }, 14 | "Outputs": { 15 | "1": "Mensagem do Discord enviada", 16 | "2": "Falha ao enviar a mensagem do Discord" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "DiscordNodes": { 23 | "Description": "Um plugin que permite enviar mensagens para um servidor Discord.\n\nO ID do Webhook e o token podem ser obtidos a partir da URL do webhook\n https://discord.com/api/webhooks/[webhookid]/[webhooktoken]", 24 | "Label": "Discord", 25 | "Fields": { 26 | "WebhookId": "ID do Webhook", 27 | "WebhookToken": "Token do Webhook" 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /DiscordNodes/i18n/ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Discord": { 5 | "Description": "Отправляет сообщение на сервер Discord.", 6 | "Fields": { 7 | "Message": "Сообщение", 8 | "Message-Help": "Сообщение, которое нужно отправить на сервер Discord", 9 | "MessageType": "Тип", 10 | "MessageType-Help": "Тип сообщения для отправки. Базовый тип — это просто сообщение без заголовка и без цветовой оформления.", 11 | "Title": "Заголовок", 12 | "Title-Help": "Необязательный заголовок для отправки; если не установлен, тип сообщения будет заголовком." 13 | }, 14 | "Outputs": { 15 | "1": "Сообщение Discord отправлено", 16 | "2": "Не удалось отправить сообщение Discord" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "DiscordNodes": { 23 | "Description": "Плагин, который позволяет вам отправлять сообщения на сервер Discord.\n\nWebhook ID и токен можно получить из URL вебхука\n https://discord.com/api/webhooks/[webhookid]/[webhooktoken]", 24 | "Label": "Discord", 25 | "Fields": { 26 | "WebhookId": "Webhook ID", 27 | "WebhookToken": "Webhook Token" 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /DiscordNodes/i18n/sv.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Discord": { 5 | "Description": "Skickar ett meddelande till en Discord-server.", 6 | "Fields": { 7 | "Message": "Meddelande", 8 | "Message-Help": "Meddelandet som ska skickas till Discord-servern", 9 | "MessageType": "Typ", 10 | "MessageType-Help": "Typen av meddelande som ska skickas. Grundtypen är bara meddelandet utan en titel och utan färgning.", 11 | "Title": "Titel", 12 | "Title-Help": "En valfri titel att skicka; om den inte är inställd kommer meddelandetypen att vara titeln." 13 | }, 14 | "Outputs": { 15 | "1": "Discord-meddelandet skickades", 16 | "2": "Misslyckades med att skicka Discord-meddelandet" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "DiscordNodes": { 23 | "Description": "Ett plugin som gör att du kan skicka meddelanden till en Discord-server.\n\nWebhook-ID och token kan erhållas från webhook-URL\n https://discord.com/api/webhooks/[webhookid]/[webhooktoken]", 24 | "Label": "Discord", 25 | "Fields": { 26 | "WebhookId": "Webhook-ID", 27 | "WebhookToken": "Webhook-token" 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /DiscordNodes/i18n/zh.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Discord": { 5 | "Description": "向Discord服务器发送消息。", 6 | "Fields": { 7 | "Message": "消息", 8 | "Message-Help": "要发送到Discord服务器的消息", 9 | "MessageType": "类型", 10 | "MessageType-Help": "要发送的消息类型。基本类型只是消息,没有标题和颜色。", 11 | "Title": "标题", 12 | "Title-Help": "要发送的可选标题;如果未设置,消息类型将为标题。" 13 | }, 14 | "Outputs": { 15 | "1": "Discord消息已发送", 16 | "2": "Discord消息发送失败" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "DiscordNodes": { 23 | "Description": "一个插件,允许您向Discord服务器发送消息。\n\nWebhook ID和令牌可以从Webhook URL中获取\n https://discord.com/api/webhooks/[webhookid]/[webhooktoken]", 24 | "Label": "Discord", 25 | "Fields": { 26 | "WebhookId": "Webhook ID", 27 | "WebhookToken": "Webhook令牌" 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /DiscordNodes/i18n/zht.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Discord": { 5 | "Description": "向Discord伺服器發送消息。", 6 | "Fields": { 7 | "Message": "消息", 8 | "Message-Help": "要發送到Discord伺服器的消息", 9 | "MessageType": "類型", 10 | "MessageType-Help": "要發送的消息類型。基本類型將僅是消息,沒有標題且沒有顏色。", 11 | "Title": "標題", 12 | "Title-Help": "要發送的可選標題,如果未設置,消息類型將成為標題。" 13 | }, 14 | "Outputs": { 15 | "1": "Discord消息已發送", 16 | "2": "Discord消息發送失敗" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "DiscordNodes": { 23 | "Description": "一個允許您向Discord伺服器發送消息的插件。\n\nWebhook Id和令牌可以從Webhook URL獲取\n https://discord.com/api/webhooks/[webhookid]/[webhooktoken]", 24 | "Label": "Discord", 25 | "Fields": { 26 | "WebhookId": "Webhook Id", 27 | "WebhookToken": "Webhook令牌" 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /Docker/Plugin.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Docker; 2 | 3 | /// 4 | /// Plugin Info 5 | /// 6 | public class Plugin : FileFlows.Plugin.IPlugin 7 | { 8 | /// 9 | public Guid Uid => new Guid("1df9239a-3ce5-44b1-9113-3cdcae980a69"); 10 | /// 11 | public string Name => "Docker"; 12 | /// 13 | public string MinimumVersion => "1.0.4.2019"; 14 | 15 | /// 16 | public string Icon => "svg:docker"; 17 | 18 | /// 19 | public void Init() { } 20 | } -------------------------------------------------------------------------------- /Docker/i18n/zh.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "DockerExecute": { 5 | "Description": "从镜像启动Docker容器,并在其中执行指定的命令。", 6 | "Label": "执行 Docker", 7 | "Fields": { 8 | "AdditionalOutputs": "额外输出", 9 | "AdditionalOutputs-Help": "命令执行的额外预期输出的索引列表,以便与输出匹配以进行自定义处理。", 10 | "Command": "命令", 11 | "Command-Help": "要在Docker容器内运行的具体命令。", 12 | "Image-Help": "要执行的Docker镜像的名称。如果该镜像在本地不存在,将自动下载该镜像。", 13 | "Message": "Docker镜像", 14 | "Volumes": "卷映射", 15 | "Volumes-Help": "指定要映射到容器的卷。临时路径将自动映射到容器内部的`/temp`。", 16 | "VolumesKey": "主机", 17 | "VolumesValue": "容器" 18 | }, 19 | "Outputs": { 20 | "1": "命令成功执行", 21 | "10": "自定义输出 9", 22 | "2": "自定义输出 1", 23 | "3": "自定义输出 2", 24 | "4": "自定义输出 3", 25 | "5": "自定义输出 4", 26 | "6": "自定义输出 5", 27 | "7": "自定义输出 6", 28 | "8": "自定义输出 7", 29 | "9": "自定义输出 8" 30 | } 31 | } 32 | } 33 | }, 34 | "Plugins": { 35 | "Docker": { 36 | "Description": "一个与Docker交互的插件,以实现Docker资源和过程的灵活集成、交互和管理。", 37 | "Label": "Docker" 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /Docker/i18n/zht.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "DockerExecute": { 5 | "Description": "從映像啟動Docker容器,並在其中執行指定的命令。", 6 | "Label": "執行 Docker", 7 | "Fields": { 8 | "AdditionalOutputs": "額外輸出", 9 | "AdditionalOutputs-Help": "命令執行的額外預期輸出的索引列表,以便與輸出匹配以進行自定義處理。", 10 | "Command": "命令", 11 | "Command-Help": "要在Docker容器內運行的具體命令。", 12 | "Image-Help": "要執行的Docker映像的名稱。如果該映像在本地不存在,將自動下載該映像。", 13 | "Message": "Docker映像", 14 | "Volumes": "卷映射", 15 | "Volumes-Help": "指定要映射到容器的卷。臨時路徑將自動映射到容器內部的`/temp`。", 16 | "VolumesKey": "主機", 17 | "VolumesValue": "容器" 18 | }, 19 | "Outputs": { 20 | "1": "命令成功執行", 21 | "10": "自定義輸出 9", 22 | "2": "自定義輸出 1", 23 | "3": "自定義輸出 2", 24 | "4": "自定義輸出 3", 25 | "5": "自定義輸出 4", 26 | "6": "自定義輸出 5", 27 | "7": "自定義輸出 6", 28 | "8": "自定義輸出 7", 29 | "9": "自定義輸出 8" 30 | } 31 | } 32 | } 33 | }, 34 | "Plugins": { 35 | "Docker": { 36 | "Description": "一個與Docker互動的插件,以實現Docker資源和過程的靈活整合、互動和管理。", 37 | "Label": "Docker" 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /EmailNodes/ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.EmailNodes 2 | { 3 | internal static class ExtensionMethods 4 | { 5 | public static void AddOrUpdate(this Dictionary dict, string key, object value) { 6 | if (dict.ContainsKey(key)) 7 | dict[key] = value; 8 | else 9 | dict.Add(key, value); 10 | } 11 | public static string? EmptyAsNull(this string str) 12 | { 13 | return str == string.Empty ? null : str; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /EmailNodes/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using FileFlows.Plugin; 2 | global using FileFlows.EmailNodes; 3 | global using FileFlows.Plugin.Attributes; -------------------------------------------------------------------------------- /EmailNodes/Plugin.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.EmailNodes; 2 | 3 | public class Plugin : IPlugin 4 | { 5 | /// 6 | public Guid Uid => new Guid("b5077522-4a31-4faa-b9a7-b409ecb9c01e"); 7 | /// 8 | public string Name => "Email"; 9 | /// 10 | public string MinimumVersion => "1.0.4.2019"; 11 | /// 12 | public string Icon => "fas fa-envelope"; 13 | 14 | /// 15 | public void Init() 16 | { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /EmailNodes/PluginSettings.cs: -------------------------------------------------------------------------------- 1 | // namespace FileFlows.EmailNodes 2 | // { 3 | // using FileFlows.Plugin; 4 | // using FileFlows.Plugin.Attributes; 5 | // using System; 6 | // using System.ComponentModel.DataAnnotations; 7 | // 8 | // public class PluginSettings:IPluginSettings 9 | // { 10 | // [Required] 11 | // [Text(1)] 12 | // public string SmtpServer { get; set; } = string.Empty; 13 | // 14 | // [Range(1, 6555)] 15 | // [NumberInt(1)] 16 | // public int SmtpPort { get; set; } 17 | // 18 | // [Text(2)] 19 | // public string SmtpUsername { get; set; } = string.Empty; 20 | // 21 | // [Password(3)] 22 | // public string SmtpPassword { get; set; } = string.Empty; 23 | // 24 | // [Text(4)] 25 | // [Required] 26 | // [System.ComponentModel.DataAnnotations.RegularExpression(@"^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$")] 27 | // public string Sender { get; set; } = string.Empty; 28 | // } 29 | // } 30 | -------------------------------------------------------------------------------- /EmailNodes/i18n/zh.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "SendEmail": { 5 | "Description": "使用配置的SMTP服务器发送电子邮件", 6 | "Label": "发送电子邮件", 7 | "Fields": { 8 | "Body": "正文", 9 | "Body-Help": "要发送的电子邮件内容。使用 [scriban](https://github.com/scriban/scriban) 模板语言", 10 | "Recipients": "收件人", 11 | "Recipients-Help": "要发送消息的电子邮件地址列表", 12 | "Subject": "主题", 13 | "Subject-Help": "正在设置的电子邮件主题" 14 | }, 15 | "Outputs": { 16 | "1": "电子邮件 ''{Subject}'' 已发送", 17 | "2": "电子邮件发送失败" 18 | } 19 | } 20 | } 21 | }, 22 | "Plugins": { 23 | "EmailNodes": { 24 | "Description": "此插件允许您在执行流程时发送电子邮件。", 25 | "Label": "电子邮件", 26 | "Fields": { 27 | "Sender": "发件人", 28 | "Sender-Help": "发送邮件的电子邮件地址", 29 | "SmtpPassword": "SMTP 密码", 30 | "SmtpPassword-Help": "用于对 SMTP 服务器进行身份验证的密码", 31 | "SmtpPort": "SMTP 端口", 32 | "SmtpPort-Help": "用于发送电子邮件的 SMTP 服务器端口,默认为 25", 33 | "SmtpServer": "SMTP 服务器", 34 | "SmtpServer-Help": "用于发送电子邮件的 SMTP 服务器地址", 35 | "SmtpUsername": "SMTP 用户名", 36 | "SmtpUsername-Help": "用于对 SMTP 服务器进行身份验证的用户名" 37 | } 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /EmailNodes/i18n/zht.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "SendEmail": { 5 | "Description": "使用配置的SMTP伺服器發送電子郵件", 6 | "Label": "發送電子郵件", 7 | "Fields": { 8 | "Body": "內容", 9 | "Body-Help": "要發送的電子郵件消息內容。使用[scriban](https://github.com/scriban/scriban)模板語言", 10 | "Recipients": "收件人", 11 | "Recipients-Help": "要發送消息的電子郵件地址列表", 12 | "Subject": "主題", 13 | "Subject-Help": "設置的電子郵件主題" 14 | }, 15 | "Outputs": { 16 | "1": "電子郵件 ''{Subject}'' 已發送", 17 | "2": "電子郵件發送失敗" 18 | } 19 | } 20 | } 21 | }, 22 | "Plugins": { 23 | "EmailNodes": { 24 | "Description": "此插件允許您在執行流程時發送電子郵件。", 25 | "Label": "電子郵件", 26 | "Fields": { 27 | "Sender": "發件人", 28 | "Sender-Help": "發送電子郵件的電子郵件地址", 29 | "SmtpPassword": "SMTP密碼", 30 | "SmtpPassword-Help": "用於對SMTP伺服器進行身份驗證的密碼", 31 | "SmtpPort": "SMTP端口", 32 | "SmtpPort-Help": "用於發送電子郵件的SMTP伺服器端口,默認為25", 33 | "SmtpServer": "SMTP伺服器", 34 | "SmtpServer-Help": "用於發送電子郵件的SMTP伺服器地址", 35 | "SmtpUsername": "SMTP用戶名", 36 | "SmtpUsername-Help": "用於對SMTP伺服器進行身份驗證的用戶名" 37 | } 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /Emby/ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Emby; 2 | 3 | internal static class ExtensionMethods 4 | { 5 | public static string? EmptyAsNull(this string str) => str == string.Empty ? null : str; 6 | } 7 | -------------------------------------------------------------------------------- /Emby/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Text; 3 | global using FileFlows.Plugin; -------------------------------------------------------------------------------- /Emby/Plugin.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Emby; 2 | 3 | public class Plugin : FileFlows.Plugin.IPlugin 4 | { 5 | /// 6 | public Guid Uid => new Guid("51bdd442-6630-4c8c-b3a4-70a2d1c60309"); 7 | /// 8 | public string Name => "Emby"; 9 | /// 10 | public string MinimumVersion => "1.0.4.2019"; 11 | /// 12 | public string Icon => "svg:emby"; 13 | 14 | /// 15 | public void Init() 16 | { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Emby/PluginSettings.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Emby 2 | { 3 | using FileFlows.Plugin; 4 | using FileFlows.Plugin.Attributes; 5 | using System.ComponentModel.DataAnnotations; 6 | 7 | public class PluginSettings:IPluginSettings 8 | { 9 | [Text(1)] 10 | [Required] 11 | public string ServerUrl { get; set; } = string.Empty; 12 | 13 | [Text(2)] 14 | [Required] 15 | public string AccessToken { get; set; } = string.Empty; 16 | 17 | [KeyValue(3, null)] 18 | public List>? Mapping { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /FileDrop/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Text; 3 | global using System.ComponentModel.DataAnnotations; 4 | global using FileFlows.Plugin; 5 | global using FileFlows.Plugin.Attributes; 6 | global using FileFlows.Common; -------------------------------------------------------------------------------- /FileDrop/Plugin.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.FileDropPlugin; 2 | 3 | /// 4 | /// Plugin Information 5 | /// 6 | public class Plugin : FileFlows.Plugin.IPlugin 7 | { 8 | /// 9 | public Guid Uid => new Guid("ba8cfaa3-4ac0-4a39-9e1b-a48def94eb3d"); 10 | /// 11 | public string Name => "File Drop"; 12 | /// 13 | public string MinimumVersion => "24.12.4.4168"; 14 | /// 15 | public string Icon => "fas fa-tint"; 16 | 17 | /// 18 | public void Init() 19 | { 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /FileDrop/i18n/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "MoveToUserFolder": { 5 | "Description": "Verschiebt die Arbeitsdatei in das Ausgabeverzeichnis des Benutzers.", 6 | "Label": "In Benutzerordner verschieben", 7 | "Outputs": { 8 | "1": "Datei wurde erfolgreich verschoben" 9 | } 10 | }, 11 | "SetFileDropDisplayName": { 12 | "Description": "Legt den Anzeigenamen für eine Datei basierend auf den Dateiinformationen des File Drops fest, um Klarheit über die Dateiidentität und den Besitz zu gewährleisten.", 13 | "Label": "Datei Drop Anzeigenamen festlegen", 14 | "Outputs": { 15 | "1": "Anzeigename festgelegt" 16 | } 17 | } 18 | } 19 | }, 20 | "Plugins": { 21 | "FileDrop": { 22 | "Description": "Plugin, das File Drop bezogene Flow-Elemente bereitstellt", 23 | "Label": "File Drop" 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /FileDrop/i18n/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "MoveToUserFolder": { 5 | "Description": "Moves the working file into the users output directory.", 6 | "Label": "Move To User Folder", 7 | "Outputs": { 8 | "1": "File was was successfully moved" 9 | } 10 | }, 11 | "SetFileDropDisplayName": { 12 | "Description": "Sets the display name for a file based on file drop file information, ensuring clarity on file identity and ownership.", 13 | "Label": "Set File Drop Display Name", 14 | "Outputs": { 15 | "1": "Display name set" 16 | } 17 | } 18 | } 19 | }, 20 | "Plugins": { 21 | "FileDrop": { 22 | "Description": "Plugin that provides File Drop related flow elements", 23 | "Label": "File Drop" 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /FileDrop/i18n/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "MoveToUserFolder": { 5 | "Description": "Mueve el archivo de trabajo al directorio de salida del usuario.", 6 | "Label": "Mover a la Carpeta del Usuario", 7 | "Outputs": { 8 | "1": "El archivo se movió con éxito" 9 | } 10 | }, 11 | "SetFileDropDisplayName": { 12 | "Description": "Establece el nombre para mostrar de un archivo según la información del archivo de File Drop, asegurando claridad sobre la identidad y propiedad del archivo.", 13 | "Label": "Establecer nombre para mostrar del archivo de File Drop", 14 | "Outputs": { 15 | "1": "Nombre para mostrar establecido" 16 | } 17 | } 18 | } 19 | }, 20 | "Plugins": { 21 | "FileDrop": { 22 | "Description": "Complemento que proporciona elementos de flujo relacionados con File Drop", 23 | "Label": "File Drop" 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /FileDrop/i18n/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "MoveToUserFolder": { 5 | "Description": "Déplace le fichier de travail dans le répertoire de sortie de l'utilisateur.", 6 | "Label": "Déplacer vers le Dossier Utilisateur", 7 | "Outputs": { 8 | "1": "Le fichier a été déplacé avec succès" 9 | } 10 | }, 11 | "SetFileDropDisplayName": { 12 | "Description": "Définit le nom affiché d'un fichier en fonction des informations du fichier de File Drop, garantissant la clarté de l'identité et de la propriété du fichier.", 13 | "Label": "Définir le nom affiché du fichier de File Drop", 14 | "Outputs": { 15 | "1": "Nom affiché défini" 16 | } 17 | } 18 | } 19 | }, 20 | "Plugins": { 21 | "FileDrop": { 22 | "Description": "Plugin qui fournit des éléments de flux liés à File Drop", 23 | "Label": "File Drop" 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /FileDrop/i18n/it.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "MoveToUserFolder": { 5 | "Description": "Sposta il file di lavoro nella directory di output dell'utente.", 6 | "Label": "Sposta nella Cartella dell'Utente", 7 | "Outputs": { 8 | "1": "Il file è stato spostato con successo" 9 | } 10 | }, 11 | "SetFileDropDisplayName": { 12 | "Description": "Imposta il nome visualizzato di un file in base alle informazioni del file di File Drop, garantendo chiarezza sull'identità e la proprietà del file.", 13 | "Label": "Imposta il nome visualizzato del file di File Drop", 14 | "Outputs": { 15 | "1": "Nome visualizzato impostato" 16 | } 17 | } 18 | } 19 | }, 20 | "Plugins": { 21 | "FileDrop": { 22 | "Description": "Plugin che fornisce elementi di flusso relativi a File Drop", 23 | "Label": "File Drop" 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /FileDrop/i18n/ja.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "MoveToUserFolder": { 5 | "Description": "作業ファイルをユーザーの出力ディレクトリに移動します。", 6 | "Label": "ユーザーフォルダーに移動", 7 | "Outputs": { 8 | "1": "ファイルは正常に移動されました" 9 | } 10 | }, 11 | "SetFileDropDisplayName": { 12 | "Description": "ファイルのドロップ情報に基づいてファイルの表示名を設定し、ファイルの識別と所有権が明確であることを保証します。", 13 | "Label": "ファイルドロップ表示名を設定", 14 | "Outputs": { 15 | "1": "表示名が設定されました" 16 | } 17 | } 18 | } 19 | }, 20 | "Plugins": { 21 | "FileDrop": { 22 | "Description": "File Drop 関連のフロー要素を提供するプラグイン", 23 | "Label": "File Drop" 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /FileDrop/i18n/ko.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "MoveToUserFolder": { 5 | "Description": "작업 파일을 사용자의 출력 디렉토리로 이동합니다.", 6 | "Label": "사용자 폴더로 이동", 7 | "Outputs": { 8 | "1": "파일이 성공적으로 이동되었습니다" 9 | } 10 | }, 11 | "SetFileDropDisplayName": { 12 | "Description": "파일 드롭 파일 정보를 기반으로 파일의 표시 이름을 설정하여 파일의 정체성과 소유권을 명확하게 보장합니다.", 13 | "Label": "파일 드롭 표시 이름 설정", 14 | "Outputs": { 15 | "1": "표시 이름이 설정됨" 16 | } 17 | } 18 | } 19 | }, 20 | "Plugins": { 21 | "FileDrop": { 22 | "Description": "File Drop 관련 흐름 요소를 제공하는 플러그인", 23 | "Label": "File Drop" 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /FileDrop/i18n/nl.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "MoveToUserFolder": { 5 | "Description": "Verplaatst het werkbestand naar de uitvoermap van de gebruiker.", 6 | "Label": "Verplaats naar Gebruikersmap", 7 | "Outputs": { 8 | "1": "Bestand is succesvol verplaatst" 9 | } 10 | }, 11 | "SetFileDropDisplayName": { 12 | "Description": "Stelt de weergavenaam van een bestand in op basis van de bestandsinformatie van File Drop, om duidelijkheid te waarborgen over de identiteit en eigendom van het bestand.", 13 | "Label": "Weergavenaam voor File Drop instellen", 14 | "Outputs": { 15 | "1": "Weergavenaam ingesteld" 16 | } 17 | } 18 | } 19 | }, 20 | "Plugins": { 21 | "FileDrop": { 22 | "Description": "Plugin die File Drop gerelateerde flow-elementen biedt", 23 | "Label": "File Drop" 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /FileDrop/i18n/pt.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "MoveToUserFolder": { 5 | "Description": "Move o arquivo de trabalho para o diretório de saída do usuário.", 6 | "Label": "Mover para a Pasta do Usuário", 7 | "Outputs": { 8 | "1": "O arquivo foi movido com sucesso" 9 | } 10 | }, 11 | "SetFileDropDisplayName": { 12 | "Description": "Define o nome exibido de um arquivo com base nas informações do arquivo de File Drop, garantindo clareza sobre a identidade e a propriedade do arquivo.", 13 | "Label": "Definir nome exibido do arquivo de File Drop", 14 | "Outputs": { 15 | "1": "Nome exibido definido" 16 | } 17 | } 18 | } 19 | }, 20 | "Plugins": { 21 | "FileDrop": { 22 | "Description": "Plugin que fornece elementos de fluxo relacionados ao File Drop", 23 | "Label": "File Drop" 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /FileDrop/i18n/ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "MoveToUserFolder": { 5 | "Description": "Перемещает рабочий файл в выходной каталог пользователя.", 6 | "Label": "Переместить в Папку Пользователя", 7 | "Outputs": { 8 | "1": "Файл успешно перемещен" 9 | } 10 | }, 11 | "SetFileDropDisplayName": { 12 | "Description": "Устанавливает отображаемое имя файла на основе информации о файле в File Drop, обеспечивая ясность относительно идентичности и собственности файла.", 13 | "Label": "Установить отображаемое имя файла для File Drop", 14 | "Outputs": { 15 | "1": "Отображаемое имя установлено" 16 | } 17 | } 18 | } 19 | }, 20 | "Plugins": { 21 | "FileDrop": { 22 | "Description": "Плагин, который предоставляет элементы потока, связанные с File Drop", 23 | "Label": "File Drop" 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /FileDrop/i18n/sv.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "MoveToUserFolder": { 5 | "Description": "Flyttar arbetsfilen till användarens utmatningskatalog.", 6 | "Label": "Flytta till Användarmapp", 7 | "Outputs": { 8 | "1": "Filen flyttades framgångsrikt" 9 | } 10 | }, 11 | "SetFileDropDisplayName": { 12 | "Description": "Sätter visningsnamnet för en fil baserat på filinformation från File Drop, vilket säkerställer tydlighet om filens identitet och ägande.", 13 | "Label": "Ställ in visningsnamn för File Drop", 14 | "Outputs": { 15 | "1": "Visningsnamn inställt" 16 | } 17 | } 18 | } 19 | }, 20 | "Plugins": { 21 | "FileDrop": { 22 | "Description": "Plugin som tillhandahåller flow-element relaterade till File Drop", 23 | "Label": "File Drop" 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /FileDrop/i18n/zh.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "MoveToUserFolder": { 5 | "Description": "将工作文件移动到用户的输出目录。", 6 | "Label": "移动到用户文件夹", 7 | "Outputs": { 8 | "1": "文件已成功移动" 9 | } 10 | }, 11 | "SetFileDropDisplayName": { 12 | "Description": "根据文件上传信息设置文件的显示名称,确保文件的身份和所有权清晰可见。", 13 | "Label": "设置文件上传显示名称", 14 | "Outputs": { 15 | "1": "显示名称已设置" 16 | } 17 | } 18 | } 19 | }, 20 | "Plugins": { 21 | "FileDrop": { 22 | "Description": "提供与文件上传相关的流程元素的插件", 23 | "Label": "文件上传" 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /FileDrop/i18n/zht.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "MoveToUserFolder": { 5 | "Description": "將工作檔案移動到使用者的輸出目錄。", 6 | "Label": "移動到使用者資料夾", 7 | "Outputs": { 8 | "1": "檔案已成功移動" 9 | } 10 | }, 11 | "SetFileDropDisplayName": { 12 | "Description": "根據文件上傳資訊設定文件的顯示名稱,確保文件的身份和所有權清晰可見。", 13 | "Label": "設定文件上傳顯示名稱", 14 | "Outputs": { 15 | "1": "顯示名稱已設定" 16 | } 17 | } 18 | } 19 | }, 20 | "Plugins": { 21 | "FileDrop": { 22 | "Description": "提供與文件上傳相關的流程元素的插件", 23 | "Label": "文件上傳" 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /FileFlows.Common.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revenz/FileFlowsPlugins/9a42dee278704c4d0e74a9e4cb546d5acdf2d28f/FileFlows.Common.dll -------------------------------------------------------------------------------- /FileFlows.Common.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revenz/FileFlowsPlugins/9a42dee278704c4d0e74a9e4cb546d5acdf2d28f/FileFlows.Common.pdb -------------------------------------------------------------------------------- /FileFlows.Plugin.deps.json: -------------------------------------------------------------------------------- 1 | { 2 | "runtimeTarget": { 3 | "name": ".NETCoreApp,Version=v7.0", 4 | "signature": "" 5 | }, 6 | "compilationOptions": {}, 7 | "targets": { 8 | ".NETCoreApp,Version=v7.0": { 9 | "FileFlows.Plugin/1.0.0": { 10 | "runtime": { 11 | "FileFlows.Plugin.dll": {} 12 | } 13 | } 14 | } 15 | }, 16 | "libraries": { 17 | "FileFlows.Plugin/1.0.0": { 18 | "type": "project", 19 | "serviceable": false, 20 | "sha512": "" 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /FileFlows.Plugin.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revenz/FileFlowsPlugins/9a42dee278704c4d0e74a9e4cb546d5acdf2d28f/FileFlows.Plugin.dll -------------------------------------------------------------------------------- /FileFlows.Plugin.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revenz/FileFlowsPlugins/9a42dee278704c4d0e74a9e4cb546d5acdf2d28f/FileFlows.Plugin.pdb -------------------------------------------------------------------------------- /Gotify/ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Gotify; 2 | 3 | /// 4 | /// Extension methods 5 | /// 6 | internal static class ExtensionMethods 7 | { 8 | /// 9 | /// Returns an empty string as null, otherwise returns the original string 10 | /// 11 | /// the input string 12 | /// the string or null if empty 13 | public static string? EmptyAsNull(this string str) => str == string.Empty ? null : str; 14 | } 15 | -------------------------------------------------------------------------------- /Gotify/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Text; 3 | global using System.ComponentModel.DataAnnotations; 4 | global using FileFlows.Plugin; 5 | global using FileFlows.Plugin.Attributes; -------------------------------------------------------------------------------- /Gotify/Plugin.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Gotify; 2 | 3 | /// 4 | /// A Gotify Plugin 5 | /// 6 | public class Plugin : FileFlows.Plugin.IPlugin 7 | { 8 | /// 9 | /// Gets the UID for this plugin 10 | /// 11 | public Guid Uid => new Guid("3d8e13f2-819f-437f-b177-be40147c6e2b"); 12 | 13 | /// 14 | /// Gets the name of this plugin 15 | /// 16 | public string Name => "Gotify"; 17 | 18 | /// 19 | /// Gets the minimum version of FileFlows required for this plugin 20 | /// 21 | public string MinimumVersion => "1.0.4.2019"; 22 | /// 23 | public string Icon => "svg:gotify"; 24 | 25 | /// 26 | /// Initializes this plugin 27 | /// 28 | public void Init() 29 | { 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Gotify/PluginSettings.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Gotify; 2 | 3 | /// 4 | /// The plugin settings for this plugin 5 | /// 6 | public class PluginSettings : IPluginSettings 7 | { 8 | /// 9 | /// Gets or sets the URL to the server to send messages to 10 | /// 11 | [Text(1)] 12 | [Required] 13 | public string ServerUrl { get; set; } = string.Empty; 14 | 15 | /// 16 | /// Gets or sets the Access Token for the server 17 | /// 18 | [Text(2)] 19 | [Required] 20 | public string AccessToken { get; set; } = string.Empty; 21 | } 22 | -------------------------------------------------------------------------------- /Gotify/i18n/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Gotify": { 5 | "Description": "Sendet eine Nachricht an einen Gotify-Server.", 6 | "Fields": { 7 | "Message": "Nachricht", 8 | "Message-Help": "Die Nachricht, die an den Gotify-Server gesendet werden soll", 9 | "Priority": "Priorität", 10 | "Priority-Help": "Die Priorität der gesendeten Nachricht", 11 | "Title": "Titel", 12 | "Title-Help": "Ein optionaler Titel zum Senden." 13 | }, 14 | "Outputs": { 15 | "1": "Gotify-Nachricht gesendet", 16 | "2": "Gotify-Nachricht konnte nicht gesendet werden" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "Gotify": { 23 | "Description": "Ein Plugin, das es Ihnen ermöglicht, Nachrichten an einen Gotify-Server zu senden.", 24 | "Label": "Gotify", 25 | "Fields": { 26 | "AccessToken": "Zugriffstoken", 27 | "AccessToken-Help": "Das Zugriffstoken, das verwendet wird, um mit dem Gotify-Server zu kommunizieren.", 28 | "ServerUrl": "Server", 29 | "ServerUrl-Help": "Die URL des Gotify-Servers", 30 | "ServerUrl-Placeholder": "http://gotify.lan" 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Gotify/i18n/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Gotify": { 5 | "Description": "Sends a message to a Gotify server.", 6 | "Fields": { 7 | "Message": "Message", 8 | "Message-Help": "The message to send to the Gotify server", 9 | "Priority": "Priority", 10 | "Priority-Help": "The priority of the message being sent", 11 | "Title": "Title", 12 | "Title-Help": "An optional title to send." 13 | }, 14 | "Outputs": { 15 | "1": "Gotify message sent", 16 | "2": "Gotify message failed to send" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "Gotify": { 23 | "Description": "A plugin that allows you to send messages to a Gotify server.", 24 | "Label": "Gotify", 25 | "Fields": { 26 | "AccessToken": "Access Token", 27 | "AccessToken-Help": "The access token used to communicate with the Gotify server.", 28 | "ServerUrl": "Server", 29 | "ServerUrl-Help": "The URL of the Gotify server", 30 | "ServerUrl-Placeholder": "http://gotify.lan" 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Gotify/i18n/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Gotify": { 5 | "Description": "Envía un mensaje a un servidor Gotify.", 6 | "Fields": { 7 | "Message": "Mensaje", 8 | "Message-Help": "El mensaje a enviar al servidor Gotify", 9 | "Priority": "Prioridad", 10 | "Priority-Help": "La prioridad del mensaje que se envía", 11 | "Title": "Título", 12 | "Title-Help": "Un título opcional para enviar." 13 | }, 14 | "Outputs": { 15 | "1": "Mensaje de Gotify enviado", 16 | "2": "Error al enviar el mensaje de Gotify" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "Gotify": { 23 | "Description": "Un plugin que permite enviar mensajes a un servidor Gotify.", 24 | "Label": "Gotify", 25 | "Fields": { 26 | "AccessToken": "Token de acceso", 27 | "AccessToken-Help": "El token de acceso utilizado para comunicarse con el servidor Gotify.", 28 | "ServerUrl": "Servidor", 29 | "ServerUrl-Help": "La URL del servidor Gotify", 30 | "ServerUrl-Placeholder": "http://gotify.lan" 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Gotify/i18n/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Gotify": { 5 | "Description": "Envoie un message à un serveur Gotify.", 6 | "Fields": { 7 | "Message": "Message", 8 | "Message-Help": "Le message à envoyer au serveur Gotify", 9 | "Priority": "Priorité", 10 | "Priority-Help": "La priorité du message envoyé", 11 | "Title": "Titre", 12 | "Title-Help": "Un titre optionnel à envoyer." 13 | }, 14 | "Outputs": { 15 | "1": "Message Gotify envoyé", 16 | "2": "Échec de l'envoi du message Gotify" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "Gotify": { 23 | "Description": "Un plugin qui vous permet d'envoyer des messages à un serveur Gotify.", 24 | "Label": "Gotify", 25 | "Fields": { 26 | "AccessToken": "Token d'accès", 27 | "AccessToken-Help": "Le token d'accès utilisé pour communiquer avec le serveur Gotify.", 28 | "ServerUrl": "Serveur", 29 | "ServerUrl-Help": "L'URL du serveur Gotify", 30 | "ServerUrl-Placeholder": "http://gotify.lan" 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Gotify/i18n/it.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Gotify": { 5 | "Description": "Invia un messaggio a un server Gotify.", 6 | "Fields": { 7 | "Message": "Messaggio", 8 | "Message-Help": "Il messaggio da inviare al server Gotify", 9 | "Priority": "Priorità", 10 | "Priority-Help": "La priorità del messaggio in fase di invio", 11 | "Title": "Titolo", 12 | "Title-Help": "Un titolo opzionale da inviare." 13 | }, 14 | "Outputs": { 15 | "1": "Messaggio Gotify inviato", 16 | "2": "Invio del messaggio Gotify fallito" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "Gotify": { 23 | "Description": "Un plugin che ti consente di inviare messaggi a un server Gotify.", 24 | "Label": "Gotify", 25 | "Fields": { 26 | "AccessToken": "Token di accesso", 27 | "AccessToken-Help": "Il token di accesso utilizzato per comunicare con il server Gotify.", 28 | "ServerUrl": "Server", 29 | "ServerUrl-Help": "L'URL del server Gotify", 30 | "ServerUrl-Placeholder": "http://gotify.lan" 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Gotify/i18n/ja.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Gotify": { 5 | "Description": "Gotifyサーバーにメッセージを送信します。", 6 | "Fields": { 7 | "Message": "メッセージ", 8 | "Message-Help": "Gotifyサーバーに送信するメッセージ", 9 | "Priority": "優先度", 10 | "Priority-Help": "送信されるメッセージの優先度", 11 | "Title": "タイトル", 12 | "Title-Help": "送信するオプションのタイトル。" 13 | }, 14 | "Outputs": { 15 | "1": "Gotifyメッセージが送信されました", 16 | "2": "Gotifyメッセージの送信に失敗しました" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "Gotify": { 23 | "Description": "Gotifyサーバーにメッセージを送信するためのプラグイン。", 24 | "Label": "Gotify", 25 | "Fields": { 26 | "AccessToken": "アクセス・トークン", 27 | "AccessToken-Help": "Gotifyサーバーとの通信に使用されるアクセス・トークン。", 28 | "ServerUrl": "サーバー", 29 | "ServerUrl-Help": "GotifyサーバーのURL", 30 | "ServerUrl-Placeholder": "http://gotify.lan" 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Gotify/i18n/ko.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Gotify": { 5 | "Description": "Gotify 서버에 메시지를 보냅니다.", 6 | "Fields": { 7 | "Message": "메시지", 8 | "Message-Help": "Gotify 서버에 보낼 메시지입니다.", 9 | "Priority": "우선 순위", 10 | "Priority-Help": "전송되는 메시지의 우선 순위입니다.", 11 | "Title": "제목", 12 | "Title-Help": "전송할 선택적 제목입니다." 13 | }, 14 | "Outputs": { 15 | "1": "Gotify 메시지가 전송되었습니다.", 16 | "2": "Gotify 메시지 전송 실패" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "Gotify": { 23 | "Description": "Gotify 서버에 메시지를 보낼 수 있는 플러그인입니다.", 24 | "Label": "Gotify", 25 | "Fields": { 26 | "AccessToken": "액세스 토큰", 27 | "AccessToken-Help": "Gotify 서버와 통신하는 데 사용되는 액세스 토큰입니다.", 28 | "ServerUrl": "서버", 29 | "ServerUrl-Help": "Gotify 서버의 URL입니다.", 30 | "ServerUrl-Placeholder": "http://gotify.lan" 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Gotify/i18n/nl.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Gotify": { 5 | "Description": "Stuurt een bericht naar een Gotify-server.", 6 | "Fields": { 7 | "Message": "Bericht", 8 | "Message-Help": "Het bericht dat naar de Gotify-server moet worden gestuurd", 9 | "Priority": "Prioriteit", 10 | "Priority-Help": "De prioriteit van het te verzenden bericht", 11 | "Title": "Titel", 12 | "Title-Help": "Een optionele titel om te verzenden." 13 | }, 14 | "Outputs": { 15 | "1": "Gotify-bericht verzonden", 16 | "2": "Verzenden van Gotify-bericht mislukt" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "Gotify": { 23 | "Description": "Een plugin waarmee je berichten naar een Gotify-server kunt sturen.", 24 | "Label": "Gotify", 25 | "Fields": { 26 | "AccessToken": "Toegangstoken", 27 | "AccessToken-Help": "Het toegangstoken dat wordt gebruikt om te communiceren met de Gotify-server.", 28 | "ServerUrl": "Server", 29 | "ServerUrl-Help": "De URL van de Gotify-server", 30 | "ServerUrl-Placeholder": "http://gotify.lan" 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Gotify/i18n/pt.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Gotify": { 5 | "Description": "Envia uma mensagem para um servidor Gotify.", 6 | "Fields": { 7 | "Message": "Mensagem", 8 | "Message-Help": "A mensagem a ser enviada para o servidor Gotify", 9 | "Priority": "Prioridade", 10 | "Priority-Help": "A prioridade da mensagem sendo enviada", 11 | "Title": "Título", 12 | "Title-Help": "Um título opcional a ser enviado." 13 | }, 14 | "Outputs": { 15 | "1": "Mensagem Gotify enviada", 16 | "2": "Falha ao enviar a mensagem Gotify" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "Gotify": { 23 | "Description": "Um plugin que permite enviar mensagens para um servidor Gotify.", 24 | "Label": "Gotify", 25 | "Fields": { 26 | "AccessToken": "Token de Acesso", 27 | "AccessToken-Help": "O token de acesso usado para se comunicar com o servidor Gotify.", 28 | "ServerUrl": "Servidor", 29 | "ServerUrl-Help": "A URL do servidor Gotify", 30 | "ServerUrl-Placeholder": "http://gotify.lan" 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Gotify/i18n/ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Gotify": { 5 | "Description": "Отправляет сообщение на сервер Gotify.", 6 | "Fields": { 7 | "Message": "Сообщение", 8 | "Message-Help": "Сообщение для отправки на сервер Gotify", 9 | "Priority": "Приоритет", 10 | "Priority-Help": "Приоритет отправляемого сообщения", 11 | "Title": "Заголовок", 12 | "Title-Help": "Необязательный заголовок для отправки." 13 | }, 14 | "Outputs": { 15 | "1": "Сообщение Gotify отправлено", 16 | "2": "Не удалось отправить сообщение Gotify" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "Gotify": { 23 | "Description": "Плагин, который позволяет отправлять сообщения на сервер Gotify.", 24 | "Label": "Gotify", 25 | "Fields": { 26 | "AccessToken": "Токен доступа", 27 | "AccessToken-Help": "Токен доступа, используемый для связи с сервером Gotify.", 28 | "ServerUrl": "Сервер", 29 | "ServerUrl-Help": "URL-адрес сервера Gotify", 30 | "ServerUrl-Placeholder": "http://gotify.lan" 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Gotify/i18n/sv.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Gotify": { 5 | "Description": "Skickar ett meddelande till en Gotify-server.", 6 | "Fields": { 7 | "Message": "Meddelande", 8 | "Message-Help": "Meddelandet som ska skickas till Gotify-servern", 9 | "Priority": "Prioritet", 10 | "Priority-Help": "Prioriteten för det skickade meddelandet", 11 | "Title": "Titel", 12 | "Title-Help": "En valfri titel att skicka." 13 | }, 14 | "Outputs": { 15 | "1": "Gotify-meddelandet har skickats", 16 | "2": "Misslyckades med att skicka Gotify-meddelandet" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "Gotify": { 23 | "Description": "Ett plugin som låter dig skicka meddelanden till en Gotify-server.", 24 | "Label": "Gotify", 25 | "Fields": { 26 | "AccessToken": "Åtkomsttoken", 27 | "AccessToken-Help": "Åtkomsttoken som används för att kommunicera med Gotify-servern.", 28 | "ServerUrl": "Server", 29 | "ServerUrl-Help": "URL-adressen till Gotify-servern", 30 | "ServerUrl-Placeholder": "http://gotify.lan" 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Gotify/i18n/zh.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Gotify": { 5 | "Description": "向Gotify服务器发送消息。", 6 | "Fields": { 7 | "Message": "消息", 8 | "Message-Help": "要发送到Gotify服务器的消息", 9 | "Priority": "优先级", 10 | "Priority-Help": "发送消息的优先级", 11 | "Title": "标题", 12 | "Title-Help": "要发送的可选标题。" 13 | }, 14 | "Outputs": { 15 | "1": "Gotify消息已发送", 16 | "2": "Gotify消息发送失败" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "Gotify": { 23 | "Description": "一个插件,可以向Gotify服务器发送消息。", 24 | "Label": "Gotify", 25 | "Fields": { 26 | "AccessToken": "访问令牌", 27 | "AccessToken-Help": "用于与Gotify服务器通信的访问令牌。", 28 | "ServerUrl": "服务器", 29 | "ServerUrl-Help": "Gotify服务器的URL", 30 | "ServerUrl-Placeholder": "http://gotify.lan" 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Gotify/i18n/zht.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Gotify": { 5 | "Description": "將消息發送到Gotify伺服器。", 6 | "Fields": { 7 | "Message": "消息", 8 | "Message-Help": "要發送到Gotify伺服器的消息", 9 | "Priority": "優先級", 10 | "Priority-Help": "發送消息的優先級", 11 | "Title": "標題", 12 | "Title-Help": "要發送的可選標題。" 13 | }, 14 | "Outputs": { 15 | "1": "Gotify消息已發送", 16 | "2": "Gotify消息發送失敗" 17 | } 18 | } 19 | } 20 | }, 21 | "Plugins": { 22 | "Gotify": { 23 | "Description": "一個允許您將消息發送到Gotify伺服器的插件。", 24 | "Label": "Gotify", 25 | "Fields": { 26 | "AccessToken": "訪問令牌", 27 | "AccessToken-Help": "用於與Gotify伺服器通信的訪問令牌。", 28 | "ServerUrl": "伺服器", 29 | "ServerUrl-Help": "Gotify伺服器的URL", 30 | "ServerUrl-Placeholder": "http://gotify.lan" 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /ImageNodes/ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.ImageNodes; 2 | 3 | internal static class ExtensionMethods 4 | { 5 | public static string? EmptyAsNull(this string str) => str == string.Empty ? null : str; 6 | public static void AddOrUpdate(this Dictionary dict, string key, object value) 7 | { 8 | if (dict.ContainsKey(key)) 9 | dict[key] = value; 10 | else 11 | dict.Add(key, value); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ImageNodes/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.ComponentModel.DataAnnotations; 3 | global using System.Collections.Generic; 4 | global using FileFlows.Plugin; 5 | global using FileFlows.Plugin.Attributes; 6 | global using FileHelper = FileFlows.Plugin.Helpers.FileHelper; 7 | global using static FileFlows.ImageNodes.Images.Constants; 8 | global using FileFlows.Common; -------------------------------------------------------------------------------- /ImageNodes/Images/AutoCropImage.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace FileFlows.ImageNodes.Images; 4 | 5 | /// 6 | /// Flow element that crops an image of any white/black borders 7 | /// 8 | public class AutoCropImage : ImageNode 9 | { 10 | /// 11 | public override int Inputs => 1; 12 | /// 13 | public override int Outputs => 2; 14 | /// 15 | public override FlowElementType Type => FlowElementType.Process; 16 | /// 17 | public override string HelpUrl => "https://fileflows.com/docs/plugins/image-nodes/auto-crop-image"; 18 | /// 19 | public override string Icon => "fas fa-crop"; 20 | 21 | /// 22 | /// Gets or sets the crop threshold 23 | /// 24 | [Slider(1)] 25 | [Range(1, 100)] 26 | [DefaultValue(50)] 27 | public int Threshold { get; set; } 28 | 29 | /// 30 | protected override Result PerformAction(NodeParameters args, string localFile, string destination) 31 | => args.ImageHelper.Trim(localFile, destination, Threshold, GetImageTypeFromFormat(), Quality); 32 | } 33 | -------------------------------------------------------------------------------- /ImageNodes/Images/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.ImageNodes.Images; 2 | 3 | public class Constants 4 | { 5 | internal const string IMAGE_FORMAT_BMP = "Bmp"; 6 | internal const string IMAGE_FORMAT_GIF = "Gif"; 7 | internal const string IMAGE_FORMAT_JPEG = "Jpeg"; 8 | internal const string IMAGE_FORMAT_PBM = "Pbm"; 9 | internal const string IMAGE_FORMAT_PNG = "Png"; 10 | internal const string IMAGE_FORMAT_TIFF = "Tiff"; 11 | internal const string IMAGE_FORMAT_TGA = "Tga"; 12 | internal const string IMAGE_FORMAT_WEBP = "WebP"; 13 | internal const string IMAGE_FORMAT_HEIC = "Heic"; 14 | } -------------------------------------------------------------------------------- /ImageNodes/Images/ImageFlip.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.ImageNodes.Images; 2 | 3 | /// 4 | /// Flow element to flip an image 5 | /// 6 | public class ImageFlip : ImageNode 7 | { 8 | /// 9 | public override int Inputs => 1; 10 | 11 | /// 12 | public override int Outputs => 1; 13 | 14 | /// 15 | public override FlowElementType Type => FlowElementType.Process; 16 | 17 | /// 18 | public override string HelpUrl => "https://fileflows.com/docs/plugins/image-nodes/image-flip"; 19 | 20 | /// 21 | public override string Icon => "fas fa-sync-alt"; 22 | 23 | /// 24 | /// Gets or sets if the image should be flipped vertically, otherwise its flipped horizontally 25 | /// 26 | [Boolean(2)] 27 | public bool Vertical { get; set; } 28 | 29 | /// 30 | protected override Result PerformAction(NodeParameters args, string localFile, string destination) 31 | => Vertical 32 | ? args.ImageHelper.FlipVertically(localFile, destination, GetImageTypeFromFormat(), Quality) 33 | : args.ImageHelper.FlipHorizontally(localFile, destination, GetImageTypeFromFormat(), Quality); 34 | } 35 | -------------------------------------------------------------------------------- /ImageNodes/Images/ImageIsLandscape.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.ImageNodes.Images; 2 | 3 | /// 4 | /// Checks if an image is landascape 5 | /// 6 | public class ImageIsLandscape: ImageBaseNode 7 | { 8 | /// 9 | public override int Inputs => 1; 10 | /// 11 | public override int Outputs => 2; 12 | /// 13 | public override FlowElementType Type => FlowElementType.Logic; 14 | /// 15 | public override string Icon => "fas fa-image"; 16 | /// 17 | public override string HelpUrl => "https://fileflows.com/docs/plugins/image-nodes/image-is-landscape"; 18 | 19 | 20 | /// 21 | public override int Execute(NodeParameters args) 22 | { 23 | var img = GetImageInfo(args); 24 | if (img == null) 25 | return -1; 26 | return img.IsLandscape ? 1 : 2; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ImageNodes/Images/ImageIsPortrait.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.ImageNodes.Images; 2 | 3 | /// 4 | /// Checks if an image is portrait 5 | /// 6 | public class ImageIsPortrait : ImageBaseNode 7 | { 8 | /// 9 | public override int Inputs => 1; 10 | /// 11 | public override int Outputs => 2; 12 | /// 13 | public override FlowElementType Type => FlowElementType.Logic; 14 | /// 15 | public override string Icon => "fas fa-portrait"; 16 | 17 | 18 | /// 19 | public override int Execute(NodeParameters args) 20 | { 21 | var img = GetImageInfo(args); 22 | if (img == null) 23 | return -1; 24 | return img.IsPortrait ? 1 : 2; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ImageNodes/Plugin.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.ImageNodes; 2 | 3 | public class Plugin : FileFlows.Plugin.IPlugin 4 | { 5 | /// 6 | public Guid Uid => new Guid("a6ddeee5-4c5a-46c5-80d5-e48552dd6a9b"); 7 | /// 8 | public string Name => "Image"; 9 | /// 10 | public string MinimumVersion => "1.0.4.2019"; 11 | /// 12 | public string Icon => "svg:image"; 13 | 14 | /// 15 | public void Init() 16 | { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /MetaNodes/AniList/AniListDateData.cs: -------------------------------------------------------------------------------- 1 | namespace MetaNodes.AniList; 2 | 3 | /// 4 | /// Represents the date information of the media. 5 | /// 6 | public class AniListDateData 7 | { 8 | /// 9 | /// Gets or sets the year of the media's release. 10 | /// 11 | public int Year { get; set; } 12 | } 13 | -------------------------------------------------------------------------------- /MetaNodes/AniList/AniListEpisodeData.cs: -------------------------------------------------------------------------------- 1 | namespace MetaNodes.AniList; 2 | 3 | 4 | /// 5 | /// Represents information about a season episode from AniList API. 6 | /// 7 | public class AniListEpisodeData 8 | { 9 | /// 10 | /// Gets or sets the title of the episode. 11 | /// 12 | public string Title { get; set; } 13 | 14 | /// 15 | /// Gets or sets the episode number. 16 | /// 17 | public int Episode { get; set; } 18 | 19 | /// 20 | /// Gets or sets the description of the episode. 21 | /// 22 | public string Description { get; set; } 23 | 24 | /// 25 | /// Gets or sets the air date of the episode. 26 | /// 27 | public string AirDate { get; set; } 28 | 29 | /// 30 | /// Gets or sets the episode number for filtering. 31 | /// 32 | public int Number { get; set; } 33 | } -------------------------------------------------------------------------------- /MetaNodes/AniList/AniListEpisodeInfo.cs: -------------------------------------------------------------------------------- 1 | namespace MetaNodes.AniList; 2 | 3 | /// 4 | /// Represents information about an anime episode. 5 | /// 6 | public class AniListEpisodeInfo 7 | { 8 | /// 9 | /// Gets or sets the title of the episode. 10 | /// 11 | public string Title { get; set; } 12 | 13 | /// 14 | /// Gets or sets the episode number. 15 | /// 16 | public int EpisodeNumber { get; set; } 17 | 18 | /// 19 | /// Gets or sets the description of the episode. 20 | /// 21 | public string Description { get; set; } 22 | 23 | /// 24 | /// Gets or sets the air date of the episode. 25 | /// 26 | public string AirDate { get; set; } 27 | } 28 | -------------------------------------------------------------------------------- /MetaNodes/AniList/AniListEpisodeResponse.cs: -------------------------------------------------------------------------------- 1 | namespace MetaNodes.AniList; 2 | 3 | /// 4 | /// Represents the response from AniList API for episode queries. 5 | /// 6 | public class AniListEpisodeResponse 7 | { 8 | /// 9 | /// Gets or sets the data contained in the response. 10 | /// 11 | public AniListEpisodeData Data { get; set; } 12 | /// 13 | /// Gets or sets the media information. 14 | /// 15 | public AniListMediaEpisodeData Media { get; set; } 16 | } 17 | -------------------------------------------------------------------------------- /MetaNodes/AniList/AniListMediaData.cs: -------------------------------------------------------------------------------- 1 | namespace MetaNodes.AniList; 2 | 3 | /// 4 | /// Represents the media data of an anime show from AniList API. 5 | /// 6 | public class AniListShowInfoResponse 7 | { 8 | /// 9 | /// Gets or sets the title information of the media. 10 | /// 11 | public AniListTitle Title { get; set; } 12 | 13 | /// 14 | /// Gets or sets the description of the media. 15 | /// 16 | public string Description { get; set; } 17 | 18 | /// 19 | /// Gets or sets the start date information of the media. 20 | /// 21 | public AniListDateData StartDate { get; set; } 22 | 23 | /// 24 | /// Gets or sets the average score of the media. 25 | /// 26 | public int? AverageScore { get; set; } 27 | } 28 | -------------------------------------------------------------------------------- /MetaNodes/AniList/AniListMediaEpisodeData.cs: -------------------------------------------------------------------------------- 1 | namespace MetaNodes.AniList; 2 | 3 | /// 4 | /// Represents the media data of an anime show with episode information from AniList API. 5 | /// 6 | public class AniListMediaEpisodeData 7 | { 8 | /// 9 | /// Gets or sets the season number of the media. 10 | /// 11 | public int Season { get; set; } 12 | 13 | /// 14 | /// Gets or sets the total number of episodes in the media. 15 | /// 16 | public int Episodes { get; set; } 17 | 18 | /// 19 | /// Gets or sets the season episodes. 20 | /// 21 | public List SeasonEpisodes { get; set; } 22 | } -------------------------------------------------------------------------------- /MetaNodes/AniList/AniListTitle.cs: -------------------------------------------------------------------------------- 1 | namespace MetaNodes.AniList; 2 | 3 | /// 4 | /// Represents the title information of an anime show in multiple languages. 5 | /// 6 | public class AniListTitle 7 | { 8 | /// 9 | /// Gets or sets the romaji title of the media. 10 | /// 11 | public string Romaji { get; set; } 12 | 13 | /// 14 | /// Gets or sets the English title of the media. 15 | /// 16 | public string English { get; set; } 17 | 18 | /// 19 | /// Gets or sets the native title of the media. 20 | /// 21 | public string Native { get; set; } 22 | } 23 | 24 | 25 | -------------------------------------------------------------------------------- /MetaNodes/Plugin.cs: -------------------------------------------------------------------------------- 1 | namespace MetaNodes; 2 | 3 | using System.ComponentModel.DataAnnotations; 4 | 5 | public class Plugin : FileFlows.Plugin.IPlugin 6 | { 7 | /// 8 | public Guid Uid => new Guid("ed1e2547-6f92-4bc8-ae49-fcd7c74e7e9c"); 9 | /// 10 | public string Name => "Meta"; 11 | /// 12 | public string MinimumVersion => "1.0.4.2019"; 13 | /// 14 | public string Icon => "svg:database"; 15 | 16 | /// 17 | public void Init() { } 18 | } 19 | -------------------------------------------------------------------------------- /MetaNodes/ThirdParty/TheMovieDbWrapper/ApiRequest/IApiRequest.cs: -------------------------------------------------------------------------------- 1 | namespace DM.MovieApi.ApiRequest; 2 | 3 | /// 4 | /// Interface to provide a constraint for all MovieDb Api Request interfaces/classes. 5 | /// 6 | public interface IApiRequest 7 | { } 8 | -------------------------------------------------------------------------------- /MetaNodes/ThirdParty/TheMovieDbWrapper/ApiResponse/ApiError.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace DM.MovieApi.ApiResponse; 4 | 5 | [DataContract] 6 | public class ApiError 7 | { 8 | private int _statusCode; 9 | 10 | [DataMember( Name = "status_code" )] 11 | public int StatusCode 12 | { 13 | get => _statusCode; 14 | private set 15 | { 16 | _statusCode = value; 17 | 18 | TmdbStatusCode = Enum.IsDefined( typeof( TmdbStatusCode ), _statusCode ) 19 | ? ( TmdbStatusCode )_statusCode 20 | : TmdbStatusCode.Unknown; 21 | } 22 | } 23 | 24 | [DataMember( Name = "status_message" )] 25 | public string Message { get; private set; } 26 | 27 | public TmdbStatusCode TmdbStatusCode { get; private set; } 28 | 29 | public override string ToString() 30 | => $"Status: {StatusCode}: {Message}"; 31 | } 32 | -------------------------------------------------------------------------------- /MetaNodes/ThirdParty/TheMovieDbWrapper/ApiResponse/ApiQueryResponse.cs: -------------------------------------------------------------------------------- 1 | namespace DM.MovieApi.ApiResponse; 2 | 3 | /// 4 | /// Standard response from an API call returning a single specific result. 5 | /// Multiple item based based results (i.e., searches) are returned with an . 6 | /// 7 | public class ApiQueryResponse : ApiResponseBase 8 | { 9 | /// 10 | /// The item returned from the API call. 11 | /// 12 | public T Item { get; internal set; } 13 | 14 | public override string ToString() 15 | => Item.ToString(); 16 | } 17 | -------------------------------------------------------------------------------- /MetaNodes/ThirdParty/TheMovieDbWrapper/ApiResponse/ApiResponseBase.cs: -------------------------------------------------------------------------------- 1 | namespace DM.MovieApi.ApiResponse; 2 | 3 | /// 4 | /// Base class for all API responses from themoviedb.org. 5 | /// 6 | public abstract class ApiResponseBase 7 | { 8 | /// 9 | /// Contains specific error information if an error was encountered during the API call to themoviedb.org. 10 | /// 11 | public ApiError Error { get; internal set; } 12 | 13 | /// 14 | /// The API command text used for the API call to themoviedb.org. 15 | /// 16 | public string CommandText { get; internal set; } 17 | 18 | /// 19 | /// The JSON returned from themoviedb.org based on the query. 20 | /// 21 | public string Json { get; internal set; } 22 | 23 | public override string ToString() 24 | => CommandText; 25 | } 26 | -------------------------------------------------------------------------------- /MetaNodes/ThirdParty/TheMovieDbWrapper/IApiSettings.cs: -------------------------------------------------------------------------------- 1 | namespace DM.MovieApi; 2 | 3 | internal interface IApiSettings 4 | { 5 | string ApiUrl { get; } 6 | 7 | string BearerToken { get; } 8 | } 9 | -------------------------------------------------------------------------------- /MetaNodes/ThirdParty/TheMovieDbWrapper/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 kindler chase 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MetaNodes/ThirdParty/TheMovieDbWrapper/MovieDb/Certifications/IApiMovieRatingRequest.cs: -------------------------------------------------------------------------------- 1 | using DM.MovieApi.ApiRequest; 2 | using DM.MovieApi.ApiResponse; 3 | 4 | namespace DM.MovieApi.MovieDb.Certifications; 5 | 6 | /// 7 | /// Interface for retrieving movie rating information. 8 | /// 9 | public interface IApiMovieRatingRequest : IApiRequest 10 | { 11 | /// 12 | /// Gets the list of supported certifications (movie ratings) for movies. 13 | /// 14 | Task> GetMovieRatingsAsync(); 15 | } 16 | -------------------------------------------------------------------------------- /MetaNodes/ThirdParty/TheMovieDbWrapper/MovieDb/Collections/CollectionInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace DM.MovieApi.MovieDb.Collections; 4 | 5 | [DataContract] 6 | public class CollectionInfo 7 | { 8 | [DataMember( Name = "id" )] 9 | public int Id { get; set; } 10 | 11 | [DataMember( Name = "name" )] 12 | public string Name { get; set; } 13 | 14 | [DataMember( Name = "poster_path" )] 15 | public string PosterPath { get; set; } 16 | 17 | [DataMember( Name = "backdrop_path" )] 18 | public string BackdropPath { get; set; } 19 | 20 | public override string ToString() 21 | { 22 | if( string.IsNullOrWhiteSpace( Name ) ) 23 | { 24 | return "n/a"; 25 | } 26 | 27 | return $"{Name} ({Id})"; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /MetaNodes/ThirdParty/TheMovieDbWrapper/MovieDb/Companies/ParentCompany.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace DM.MovieApi.MovieDb.Companies; 4 | 5 | [DataContract] 6 | public class ParentCompany 7 | { 8 | [DataMember( Name = "id" )] 9 | public int Id { get; set; } 10 | 11 | [DataMember( Name = "name" )] 12 | public string Name { get; set; } 13 | 14 | [DataMember( Name = "logo_path" )] 15 | public string LogoPath { get; set; } 16 | 17 | public override string ToString() 18 | { 19 | if( string.IsNullOrWhiteSpace( Name ) ) 20 | { 21 | return "n/a"; 22 | } 23 | 24 | return $"{Name} ({Id})"; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /MetaNodes/ThirdParty/TheMovieDbWrapper/MovieDb/Companies/ProductionCompany.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace DM.MovieApi.MovieDb.Companies; 4 | 5 | [DataContract] 6 | public class ProductionCompany 7 | { 8 | [DataMember( Name = "id" )] 9 | public int Id { get; set; } 10 | 11 | [DataMember( Name = "name" )] 12 | public string Name { get; set; } 13 | 14 | [DataMember( Name = "description" )] 15 | public string Description { get; set; } 16 | 17 | [DataMember( Name = "headquarters" )] 18 | public string Headquarters { get; set; } 19 | 20 | [DataMember( Name = "homepage" )] 21 | public string Homepage { get; set; } 22 | 23 | [DataMember( Name = "logo_path" )] 24 | public string LogoPath { get; set; } 25 | 26 | [DataMember( Name = "parent_company" )] 27 | public ParentCompany ParentCompany { get; set; } 28 | 29 | public override string ToString() 30 | => $"{Name} ({Id})"; 31 | } 32 | -------------------------------------------------------------------------------- /MetaNodes/ThirdParty/TheMovieDbWrapper/MovieDb/Configuration/ApiConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace DM.MovieApi.MovieDb.Configuration; 4 | 5 | [DataContract] 6 | public class ApiConfiguration 7 | { 8 | [DataMember( Name = "images" )] 9 | public ImageConfiguration Images { get; private set; } 10 | 11 | [DataMember( Name = "change_keys" )] 12 | public IReadOnlyList ChangeKeys { get; private set; } 13 | 14 | public override string ToString() 15 | { 16 | if( !string.IsNullOrWhiteSpace( Images?.RootUrl ) ) 17 | { 18 | return Images.RootUrl; 19 | } 20 | 21 | return "not set"; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /MetaNodes/ThirdParty/TheMovieDbWrapper/MovieDb/Configuration/ApiConfigurationRequest.cs: -------------------------------------------------------------------------------- 1 | using DM.MovieApi.ApiRequest; 2 | using DM.MovieApi.ApiResponse; 3 | using DM.MovieApi.Shims; 4 | 5 | namespace DM.MovieApi.MovieDb.Configuration; 6 | 7 | internal class ApiConfigurationRequest : ApiRequestBase, IApiConfigurationRequest 8 | { 9 | [ImportingConstructor] 10 | public ApiConfigurationRequest( IApiSettings settings ) 11 | : base( settings ) 12 | { } 13 | 14 | public async Task> GetAsync() 15 | { 16 | ApiQueryResponse response = await base.QueryAsync( "configuration" ); 17 | 18 | return response; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /MetaNodes/ThirdParty/TheMovieDbWrapper/MovieDb/Configuration/IApiConfigurationRequest.cs: -------------------------------------------------------------------------------- 1 | using DM.MovieApi.ApiRequest; 2 | using DM.MovieApi.ApiResponse; 3 | 4 | namespace DM.MovieApi.MovieDb.Configuration; 5 | 6 | /// 7 | /// Interface for retrieving themoviedb.org configuration information. 8 | /// 9 | public interface IApiConfigurationRequest : IApiRequest 10 | { 11 | /// 12 | /// Get themoviedb.org system wide configuration information. Some elements of themoviedb.org 13 | /// API require knowledge of the configuration data. The purpose of the 14 | /// is to try and keep the actual API responses as light as possible. 15 | /// It is recommended you cache this data within your application and check for updates every few days. 16 | /// This method currently holds the data relevant to building image URLs as well as the change key map. 17 | /// 18 | Task> GetAsync(); 19 | } 20 | -------------------------------------------------------------------------------- /MetaNodes/ThirdParty/TheMovieDbWrapper/MovieDb/Configuration/ImageConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace DM.MovieApi.MovieDb.Configuration; 4 | 5 | [DataContract] 6 | public class ImageConfiguration 7 | { 8 | [DataMember( Name = "base_url" )] 9 | public string RootUrl { get; private set; } 10 | 11 | [DataMember( Name = "secure_base_url" )] 12 | public string SecureRootUrl { get; private set; } 13 | 14 | [DataMember( Name = "backdrop_sizes" )] 15 | public IReadOnlyList BackDrops { get; private set; } 16 | 17 | [DataMember( Name = "logo_sizes" )] 18 | public IReadOnlyList Logos { get; private set; } 19 | 20 | [DataMember( Name = "poster_sizes" )] 21 | public IReadOnlyList Posters { get; private set; } 22 | 23 | [DataMember( Name = "profile_sizes" )] 24 | public IReadOnlyList Profiles { get; private set; } 25 | 26 | [DataMember( Name = "still_sizes" )] 27 | public IReadOnlyList Stills { get; private set; } 28 | 29 | public override string ToString() 30 | { 31 | if( !string.IsNullOrWhiteSpace( RootUrl ) ) 32 | { 33 | return RootUrl; 34 | } 35 | 36 | return "not set"; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /MetaNodes/ThirdParty/TheMovieDbWrapper/MovieDb/Discover/DiscoverEnums.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace DM.MovieApi.MovieDb.Discover; 4 | 5 | public enum DiscoverSortBy 6 | { 7 | [Description( "popularity" )] 8 | Popularity, 9 | 10 | [Description( "release_date" )] 11 | ReleaseDate, 12 | 13 | [Description( "revenue" )] 14 | Revenue, 15 | 16 | [Description( "primary_release_date" )] 17 | PrimaryReleaseDate, 18 | 19 | [Description( "original_title" )] 20 | OriginalTitle, 21 | 22 | [Description( "vote_average" )] 23 | VoteAverage, 24 | 25 | [Description( "vote_count" )] 26 | VoteCount 27 | } 28 | 29 | public enum SortDirection 30 | { 31 | [Description( "asc" )] 32 | Asc, 33 | 34 | [Description( "desc" )] 35 | Desc 36 | } 37 | 38 | public enum FilterExp 39 | { 40 | [Description( "gte" )] 41 | GreaterThanOrEqual, 42 | 43 | [Description( "lte" )] 44 | LessThanOrEqual 45 | } 46 | -------------------------------------------------------------------------------- /MetaNodes/ThirdParty/TheMovieDbWrapper/MovieDb/Discover/IApiDiscoverRequest.cs: -------------------------------------------------------------------------------- 1 | using DM.MovieApi.ApiRequest; 2 | using DM.MovieApi.ApiResponse; 3 | using DM.MovieApi.MovieDb.Movies; 4 | 5 | namespace DM.MovieApi.MovieDb.Discover; 6 | 7 | /// 8 | /// Interface for discovering movies based on the filter provided by the parameter builder. 9 | /// 10 | public interface IApiDiscoverRequest : IApiRequest 11 | { 12 | /// 13 | /// Allows for the discovery of movies by various types of data provided to the parameter. 14 | /// 15 | /// Provides a method of adding several types of parameters to filter the query. 16 | /// Default is page 1. The page number to retrieve; the will contain the current page returned and the total number of pages available. 17 | /// Default is English. The ISO 639-1 language code to retrieve the result from. 18 | Task> DiscoverMoviesAsync( DiscoverMovieParameterBuilder builder, int pageNumber = 1, string language = "en" ); 19 | } 20 | -------------------------------------------------------------------------------- /MetaNodes/ThirdParty/TheMovieDbWrapper/MovieDb/IndustryProfessions/ApiProfessionRequest.cs: -------------------------------------------------------------------------------- 1 | using DM.MovieApi.ApiRequest; 2 | using DM.MovieApi.ApiResponse; 3 | using DM.MovieApi.Shims; 4 | using Newtonsoft.Json.Linq; 5 | 6 | namespace DM.MovieApi.MovieDb.IndustryProfessions; 7 | 8 | internal class ApiProfessionRequest : ApiRequestBase, IApiProfessionRequest 9 | { 10 | [ImportingConstructor] 11 | public ApiProfessionRequest( IApiSettings settings ) 12 | : base( settings ) 13 | { } 14 | 15 | public Task>> GetAllAsync() 16 | { 17 | const string command = "job/list"; 18 | 19 | Task>> response = base.QueryAsync( command, ProfessionDeserializer ); 20 | 21 | return response; 22 | } 23 | 24 | private IReadOnlyList ProfessionDeserializer( string json ) 25 | { 26 | var obj = JObject.Parse( json ); 27 | 28 | var arr = ( JArray )obj["jobs"]; 29 | 30 | // ReSharper disable once PossibleNullReferenceException 31 | var professions = arr.ToObject>(); 32 | 33 | return professions; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /MetaNodes/ThirdParty/TheMovieDbWrapper/MovieDb/IndustryProfessions/IApiProfessionRequest.cs: -------------------------------------------------------------------------------- 1 | using DM.MovieApi.ApiRequest; 2 | using DM.MovieApi.ApiResponse; 3 | 4 | namespace DM.MovieApi.MovieDb.IndustryProfessions; 5 | 6 | /// 7 | /// Interface for retrieving information about Movie/TV industry specific professions. 8 | /// 9 | public interface IApiProfessionRequest : IApiRequest 10 | { 11 | /// 12 | /// Gets all the Movie/TV industry specific professions. 13 | /// 14 | Task>> GetAllAsync(); 15 | } 16 | -------------------------------------------------------------------------------- /MetaNodes/ThirdParty/TheMovieDbWrapper/MovieDb/IndustryProfessions/Profession.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace DM.MovieApi.MovieDb.IndustryProfessions; 4 | 5 | [DataContract] 6 | public class Profession 7 | { 8 | [DataMember( Name = "department" )] 9 | public string Department { get; set; } 10 | 11 | [DataMember( Name = "jobs" )] 12 | public IReadOnlyList Jobs { get; set; } 13 | 14 | public override string ToString() 15 | => $"{Department} {Jobs.Count} jobs"; 16 | } 17 | -------------------------------------------------------------------------------- /MetaNodes/ThirdParty/TheMovieDbWrapper/MovieDb/People/Gender.cs: -------------------------------------------------------------------------------- 1 | namespace DM.MovieApi.MovieDb.People; 2 | 3 | public enum Gender 4 | { 5 | Unknown = 0, 6 | Female = 1, 7 | Male = 2, 8 | } 9 | -------------------------------------------------------------------------------- /MetaNodes/ThirdParty/TheMovieDbWrapper/MovieDb/TV/SeasonInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace DM.MovieApi.MovieDb.TV; 4 | 5 | [DataContract] 6 | public class SeasonInfo 7 | { 8 | [DataMember( Name = "id" )] 9 | public int Id { get; set; } 10 | 11 | [DataMember( Name = "air_date" )] 12 | public DateTime AirDate { get; set; } 13 | 14 | [DataMember( Name = "overview" )] 15 | public string Overview { get; set; } 16 | 17 | [DataMember( Name = "name" )] 18 | public string Name { get; set; } 19 | 20 | [DataMember( Name = "poster_path" )] 21 | public string PosterPath { get; set; } 22 | 23 | [DataMember( Name = "season_number" )] 24 | public int SeasonNumber { get; set; } 25 | 26 | [DataMember( Name = "episodes" )] 27 | public IReadOnlyList Episodes { get; set; } 28 | 29 | public SeasonInfo() 30 | { 31 | Episodes = Array.Empty(); 32 | } 33 | 34 | public override string ToString() 35 | => $"{Name} - {AirDate:yyyy-MM-dd}"; 36 | } 37 | -------------------------------------------------------------------------------- /MetaNodes/ThirdParty/TheMovieDbWrapper/Shims/CollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | 3 | namespace DM.MovieApi.Shims; 4 | 5 | public static class CollectionExtensions 6 | { 7 | public static IReadOnlyList AsReadOnly( this List list ) 8 | { 9 | return new ReadOnlyCollection( list ); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /MetaNodes/ThirdParty/TheMovieDbWrapper/Shims/EnumExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Reflection; 3 | 4 | namespace DM.MovieApi.Shims; 5 | 6 | internal static class EnumExtensions 7 | { 8 | public static string GetDescription( this Enum e ) 9 | { 10 | DescriptionAttribute attr = e.GetType() 11 | .GetMember( e.ToString() ) 12 | .First() 13 | .GetCustomAttribute(); 14 | 15 | return attr?.Description ?? e.ToString(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /MetaNodes/ThirdParty/TheMovieDbWrapper/Shims/ImportingConstructorAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace DM.MovieApi.Shims; 2 | 3 | [AttributeUsage( AttributeTargets.Constructor )] 4 | internal sealed class ImportingConstructorAttribute : Attribute 5 | { } 6 | -------------------------------------------------------------------------------- /MetaNodes/ThirdParty/TheMovieDbWrapper/info: -------------------------------------------------------------------------------- 1 | This code is written by nCubed and was taken from 2 | https://github.com/nCubed/TheMovieDbWrapper/ 3 | On November 22nd 2021 4 | 5 | No changes have been done to the code, its just easier to include the code in this DLL instead of having to load it separately. 6 | 7 | See the license for this for more information. -------------------------------------------------------------------------------- /Nextcloud/ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Nextcloud; 2 | 3 | /// 4 | /// Extension methods 5 | /// 6 | internal static class ExtensionMethods 7 | { 8 | /// 9 | /// Returns an empty string as null, otherwise returns the original string 10 | /// 11 | /// the input string 12 | /// the string or null if empty 13 | public static string? EmptyAsNull(this string str) 14 | { 15 | return str == string.Empty ? null : str; 16 | } 17 | } -------------------------------------------------------------------------------- /Nextcloud/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Text; 3 | global using System.ComponentModel.DataAnnotations; 4 | global using FileFlows.Plugin; 5 | global using FileFlows.Plugin.Attributes; 6 | global using FileFlows.Common; -------------------------------------------------------------------------------- /Nextcloud/Plugin.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Nextcloud; 2 | 3 | /// 4 | /// Nextcloud plugin 5 | /// 6 | public class Plugin : FileFlows.Plugin.IPlugin 7 | { 8 | /// 9 | public Guid Uid => new Guid("c9aff033-ae5b-45ad-81b0-8691c850242a"); 10 | /// 11 | public string Name => "Nextcloud"; 12 | /// 13 | public string MinimumVersion => "24.8.1.3444"; 14 | /// 15 | public string Icon => "svg:nextcloud"; 16 | 17 | /// 18 | public void Init() 19 | { 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Nextcloud/PluginSettings.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Nextcloud; 2 | 3 | /// 4 | /// The plugin settings for this plugin 5 | /// 6 | public class PluginSettings : IPluginSettings 7 | { 8 | /// 9 | /// Gets or sets the next cloud URL 10 | /// 11 | [Text(1)] 12 | [Required] 13 | public string Url { get; set; } = string.Empty; 14 | 15 | /// 16 | /// Gets or sets the username 17 | /// 18 | [Text(2)] 19 | [Required] 20 | public string Username { get; set; } = string.Empty; 21 | 22 | /// 23 | /// Gets or sets the password 24 | /// 25 | [Text(3)] 26 | [Required] 27 | public string Password { get; set; } = string.Empty; 28 | } 29 | -------------------------------------------------------------------------------- /Nextcloud/Tests/UploadTest.cs: -------------------------------------------------------------------------------- 1 | #if(DEBUG) 2 | 3 | using FileFlows.Nextcloud.FlowElements; 4 | using FileFlows.Nextcloud.Helpers; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | using Moq; 7 | using PluginTestLibrary; 8 | 9 | namespace FileFlows.Nextcloud.Tests; 10 | 11 | [TestClass] 12 | public class UploadTest : TestBase 13 | { 14 | [TestMethod] 15 | public void Test() 16 | { 17 | var args = GetNodeParameters(TempFile); 18 | args.GetPluginSettingsJson = _ => """{"Username": "user", "Password": "password", "Url": "http://nextcloud.test" }"""; 19 | string destPath = "ff-test/" + Guid.NewGuid() + ".heic"; 20 | Mock mockUploader = new(); 21 | mockUploader.Setup(x => x.UploadFile( 22 | It.Is(y => y == TempFile), 23 | It.Is(y => y == destPath))) 24 | .Returns(true); 25 | 26 | var element = new UploadToNextcloud(); 27 | element.GetUploader = (_, _, _, _) => mockUploader.Object; 28 | element.DestinationPath = destPath; 29 | Assert.AreEqual(1, element.Execute(args)); 30 | } 31 | } 32 | #endif -------------------------------------------------------------------------------- /Nextcloud/i18n/ja.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "UploadToNextcloud": { 5 | "Description": "Nextcloudにファイルをアップロードします", 6 | "Label": "Nextcloudにアップロード", 7 | "Fields": { 8 | "DestinationPath": "宛先", 9 | "DestinationPath-Help": "Nextcloud内のファイルの場所とファイル名、すなわち完全なパス。", 10 | "File": "ファイル", 11 | "File-Help": "アップロードするファイル。現在の作業ファイルを使用するには空白のままにします。", 12 | "SetWorkingFile": "作業ファイルを設定", 13 | "SetWorkingFile-Help": "アップロードされたNextcloudパスを作業ファイルとして設定します。これをフローの最後にのみ使用してください。このファイルは最終出力としてマークされ、以降のフロー要素で作業ファイルが必要な場合、期待通りに動作しません。" 14 | }, 15 | "Outputs": { 16 | "1": "Nextcloudに正常にアップロードされました", 17 | "2": "Nextcloudへのアップロードに失敗しました" 18 | } 19 | } 20 | } 21 | }, 22 | "Plugins": { 23 | "Nextcloud": { 24 | "Description": "Nextcloudにファイルをアップロードするためのプラグインです。", 25 | "Label": "Nextcloud", 26 | "Fields": { 27 | "Password": "パスワード", 28 | "Password-Help": "このNextcloudユーザーのためにFileFlows用に作成したアプリケーション固有のパスワード。", 29 | "Url": "URL", 30 | "Url-Help": "NextcloudサーバーのURL", 31 | "Username": "ユーザー名", 32 | "Username-Help": "アップロードするユーザーのユーザー名。" 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /Nextcloud/i18n/ko.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "UploadToNextcloud": { 5 | "Description": "Nextcloud에 파일을 업로드합니다.", 6 | "Label": "Nextcloud에 업로드", 7 | "Fields": { 8 | "DestinationPath": "대상", 9 | "DestinationPath-Help": "Nextcloud에서 파일의 전체 경로와 파일 이름입니다.", 10 | "File": "파일", 11 | "File-Help": "업로드할 파일로, 현재 작업 파일을 사용하려면 비워 두세요.", 12 | "SetWorkingFile": "작업 파일 설정", 13 | "SetWorkingFile-Help": "업로드된 넥스트클라우드 경로를 작업 파일로 설정합니다. 이 파일은 최종 출력으로 표시되므로 흐름의 끝에서만 사용하십시오. 작업 파일이 필요한 후속 흐름 요소는 예상대로 작동하지 않습니다." 14 | }, 15 | "Outputs": { 16 | "1": "Nextcloud에 성공적으로 업로드되었습니다.", 17 | "2": "Nextcloud에 업로드 실패" 18 | } 19 | } 20 | } 21 | }, 22 | "Plugins": { 23 | "Nextcloud": { 24 | "Description": "Nextcloud에 파일을 업로드할 수 있는 플러그인입니다.", 25 | "Label": "Nextcloud", 26 | "Fields": { 27 | "Password": "비밀번호", 28 | "Password-Help": "이 Nextcloud 사용자에 대한 FileFlows를 위해 생성한 애플리케이션 전용 비밀번호입니다.", 29 | "Url": "URL", 30 | "Url-Help": "Nextcloud 서버의 URL입니다.", 31 | "Username": "사용자 이름", 32 | "Username-Help": "업로드할 사용자의 사용자 이름입니다." 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /Nextcloud/i18n/zh.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "UploadToNextcloud": { 5 | "Description": "将文件上传到Nextcloud", 6 | "Label": "上传到Nextcloud", 7 | "Fields": { 8 | "DestinationPath": "目的地", 9 | "DestinationPath-Help": "文件在Nextcloud中的位置和文件名,即完整路径。", 10 | "File": "文件", 11 | "File-Help": "要上传的文件,留空以使用当前工作文件。", 12 | "SetWorkingFile": "设置工作文件", 13 | "SetWorkingFile-Help": "将上传的Nextcloud路径设置为工作文件。仅在流程结束时使用此设置,因为该文件将被标记为最终输出。任何需要工作文件的后续流程元素将无法正常运行。" 14 | }, 15 | "Outputs": { 16 | "1": "已成功上传到Nextcloud", 17 | "2": "上传到Nextcloud失败" 18 | } 19 | } 20 | } 21 | }, 22 | "Plugins": { 23 | "Nextcloud": { 24 | "Description": "一个允许您将文件上传到Nextcloud的插件。", 25 | "Label": "Nextcloud", 26 | "Fields": { 27 | "Password": "密码", 28 | "Password-Help": "您为FileFlows为该Nextcloud用户创建的应用程序特定密码。", 29 | "Url": "URL", 30 | "Url-Help": "Nextcloud服务器的URL", 31 | "Username": "用户名", 32 | "Username-Help": "要上传的用户的用户名。" 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /Nextcloud/i18n/zht.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "UploadToNextcloud": { 5 | "Description": "將文件上傳到Nextcloud", 6 | "Label": "上傳到Nextcloud", 7 | "Fields": { 8 | "DestinationPath": "目的地", 9 | "DestinationPath-Help": "文件在Nextcloud中的位置和文件名,即完整路徑。", 10 | "File": "文件", 11 | "File-Help": "要上傳的文件,留空以使用當前工作文件。", 12 | "SetWorkingFile": "設定工作檔案", 13 | "SetWorkingFile-Help": "將上傳的Nextcloud路徑設為工作檔案。僅在流程結束時使用此設置,因為該檔案將被標記為最終輸出。需要工作檔案的後續流程元素將無法正常運行。" 14 | }, 15 | "Outputs": { 16 | "1": "已成功上傳到Nextcloud", 17 | "2": "上傳到Nextcloud失敗" 18 | } 19 | } 20 | } 21 | }, 22 | "Plugins": { 23 | "Nextcloud": { 24 | "Description": "一個允許您將文件上傳到Nextcloud的插件。", 25 | "Label": "Nextcloud", 26 | "Fields": { 27 | "Password": "密碼", 28 | "Password-Help": "您為此Nextcloud用戶為FileFlows創建的應用專用密碼。", 29 | "Url": "URL", 30 | "Url-Help": "Nextcloud伺服器的URL", 31 | "Username": "用戶名", 32 | "Username-Help": "要上傳的用戶的用戶名。" 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /Pdf/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Text; 3 | global using System.ComponentModel.DataAnnotations; 4 | global using FileFlows.Plugin; 5 | global using FileFlows.Plugin.Attributes; 6 | global using FileFlows.Common; -------------------------------------------------------------------------------- /Pdf/Plugin.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Pdf; 2 | 3 | /// 4 | /// Plugin Information 5 | /// 6 | public class Plugin : FileFlows.Plugin.IPlugin 7 | { 8 | /// 9 | public Guid Uid => new Guid("af8fa0a9-53ad-457d-9729-14f1f996d477"); 10 | /// 11 | public string Name => "PDF"; 12 | /// 13 | public string MinimumVersion => "25.01.1.4200"; 14 | /// 15 | public string Icon => "fas fa-file-pdf"; 16 | 17 | /// 18 | public void Init() 19 | { 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Plex/ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Plex; 2 | 3 | /// 4 | /// Extension methods 5 | /// 6 | internal static class ExtensionMethods 7 | { 8 | /// 9 | /// Returns an empty string as null, otherwise returns the original string 10 | /// 11 | /// the input string 12 | /// the string or null if empty 13 | public static string? EmptyAsNull(this string str) 14 | { 15 | return str == string.Empty ? null : str; 16 | } 17 | } -------------------------------------------------------------------------------- /Plex/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Text; 3 | global using System.ComponentModel.DataAnnotations; 4 | global using FileFlows.Plugin; 5 | global using FileFlows.Plugin.Attributes; -------------------------------------------------------------------------------- /Plex/MediaManagement/PlexUpdater.cs: -------------------------------------------------------------------------------- 1 | using FileFlows.Plex.Models; 2 | 3 | namespace FileFlows.Plex.MediaManagement; 4 | 5 | public class PlexUpdater: PlexNode 6 | { 7 | protected override int ExecuteActual(NodeParameters args, PlexDirectory directory, string url, string mappedPath, string accessToken) 8 | { 9 | args.Logger?.ILog("Executing Actual in Plex Updater"); 10 | url += $"library/sections/{directory.Key}/refresh?path={Uri.EscapeDataString(mappedPath)}&X-Plex-Token=" + accessToken; 11 | 12 | using var httpClient = new HttpClient(); 13 | var updateResponse = GetWebRequest(httpClient, url); 14 | if (updateResponse.success == false) 15 | { 16 | if(string.IsNullOrWhiteSpace(updateResponse.body) == false) 17 | args.Logger?.WLog("Failed to update Plex:" + updateResponse.body); 18 | return 2; 19 | } 20 | return 1; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Plex/Models/PlexDirectory.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Plex.Models; 2 | 3 | public class PlexDirectory 4 | { 5 | public string? Key { get; set; } 6 | public PlexDirectoryLocation[]? Location { get; set; } 7 | } 8 | 9 | public class PlexDirectoryLocation 10 | { 11 | public int Id { get; set; } 12 | public string? Path { get; set; } 13 | } -------------------------------------------------------------------------------- /Plex/Models/PlexMedia.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Plex.Models; 2 | 3 | internal class PlexMedia 4 | { 5 | public string? RatingKey { get; set; } 6 | public int Id { get; set; } 7 | public PlexPart[]? Part { get; set; } 8 | } 9 | 10 | internal class PlexPart 11 | { 12 | public int Id { get; set; } 13 | public string? Key { get; set; } 14 | public string? File { get; set; } 15 | } 16 | -------------------------------------------------------------------------------- /Plex/Models/PlexMetadata.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Plex.Models; 2 | 3 | internal class PlexMetadata 4 | { 5 | public string? RatingKey { get; set; } 6 | public string? Key { get; set; } 7 | public PlexMedia[]? Media { get; set; } 8 | } 9 | -------------------------------------------------------------------------------- /Plex/Models/PlexSections.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Plex.Models; 2 | 3 | internal class PlexSections 4 | { 5 | public PlexSection? MediaContainer { get; set; } 6 | } 7 | 8 | internal class PlexSection 9 | { 10 | public int Size { get; set; } 11 | public PlexDirectory[]? Directory { get; set; } 12 | public PlexMetadata[]? Metadata { get; set; } 13 | } 14 | -------------------------------------------------------------------------------- /Plex/Plugin.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Plex; 2 | 3 | public class Plugin : FileFlows.Plugin.IPlugin 4 | { 5 | /// 6 | public Guid Uid => new Guid("5be72267-7574-4ba9-a958-f3dda0d6c2dc"); 7 | /// 8 | public string Name => "Plex"; 9 | /// 10 | public string MinimumVersion => "1.0.4.2019"; 11 | /// 12 | public string Icon => "svg:plex"; 13 | 14 | /// 15 | public void Init() 16 | { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Plex/PluginSettings.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Plex; 2 | 3 | /// 4 | /// The Plex plugin settings 5 | /// 6 | public class PluginSettings:IPluginSettings 7 | { 8 | /// 9 | /// Gets or sets the server URL 10 | /// 11 | [Text(1)] 12 | [Required] 13 | public string ServerUrl { get; set; } = string.Empty; 14 | 15 | /// 16 | /// Gets or sets the access token 17 | /// 18 | [Text(2)] 19 | [Required] 20 | public string AccessToken { get; set; } = string.Empty; 21 | 22 | /// 23 | /// Gets or sets if certificate errors should be ignored 24 | /// 25 | [Boolean(3)] 26 | public bool IgnoreCertificateErrors { get; set; } = false; 27 | 28 | /// 29 | /// Gets or sets the mappings 30 | /// 31 | [KeyValue(4, null)] 32 | public List> Mapping { get; set; } = new(); 33 | } -------------------------------------------------------------------------------- /PluginTestLibrary/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using FileFlows.Common; -------------------------------------------------------------------------------- /PluginTestLibrary/PluginTestLibrary.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | ..\FileFlows.Plugin.dll 16 | 17 | 18 | ..\FileFlows.Common.dll 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Pushbullet/ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Pushbullet; 2 | 3 | /// 4 | /// Extension methods 5 | /// 6 | internal static class ExtensionMethods 7 | { 8 | /// 9 | /// Returns an empty string as null, otherwise returns the original string 10 | /// 11 | /// the input string 12 | /// the string or null if empty 13 | public static string? EmptyAsNull(this string str) => str == string.Empty ? null : str; 14 | } 15 | -------------------------------------------------------------------------------- /Pushbullet/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Text; 3 | global using System.ComponentModel.DataAnnotations; 4 | global using FileFlows.Plugin; 5 | global using FileFlows.Plugin.Attributes; -------------------------------------------------------------------------------- /Pushbullet/Plugin.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Pushbullet; 2 | 3 | /// 4 | /// A Pushbullet Plugin 5 | /// 6 | public class Plugin : FileFlows.Plugin.IPlugin 7 | { 8 | /// 9 | /// Gets the UID for this plugin 10 | /// 11 | public Guid Uid => new Guid("3016f2b9-41cb-45cf-b4f9-a8d9a30dc385"); 12 | 13 | /// 14 | /// Gets the name of this plugin 15 | /// 16 | public string Name => "Pushbullet"; 17 | 18 | /// 19 | /// Gets the minimum version of FileFlows required for this plugin 20 | /// 21 | public string MinimumVersion => "1.0.4.2019"; 22 | /// 23 | public string Icon => "svg:pushbullet"; 24 | 25 | /// 26 | /// Initializes this plugin 27 | /// 28 | public void Init() 29 | { 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Pushbullet/PluginSettings.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Pushbullet; 2 | 3 | /// 4 | /// The plugin settings for this plugin 5 | /// 6 | public class PluginSettings : IPluginSettings 7 | { 8 | /// 9 | /// Gets or sets the API Token 10 | /// 11 | [Text(2)] 12 | [Required] 13 | public string ApiToken { get; set; } = string.Empty; 14 | } 15 | -------------------------------------------------------------------------------- /Pushbullet/i18n/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Pushbullet": { 5 | "Description": "Sendet eine Nachricht über Pushbullet.", 6 | "Fields": { 7 | "Message": "Nachricht", 8 | "Message-Help": "Die Nachricht, die an den Pushbullet-Server gesendet werden soll", 9 | "Priority": "Priorität", 10 | "Priority-Help": "Die Priorität der gesendeten Nachricht", 11 | "Title": "Titel" 12 | }, 13 | "Outputs": { 14 | "1": "Pushbullet-Nachricht gesendet", 15 | "2": "Pushbullet-Nachricht konnte nicht gesendet werden" 16 | } 17 | } 18 | } 19 | }, 20 | "Plugins": { 21 | "Pushbullet": { 22 | "Description": "Ein Plugin, das es Ihnen ermöglicht, Nachrichten an einen Pushbullet-Server zu senden.", 23 | "Label": "Pushbullet", 24 | "Fields": { 25 | "ApiToken": "API-Token", 26 | "ApiToken-Help": "Das Anwendungs-API-Token, das zum Senden von Benachrichtigungen verwendet wird.", 27 | "UserKey": "Benutzer Schlüssel", 28 | "UserKey-Help": "Ihr persönlicher Benutzerschlüssel zum Empfangen von Benachrichtigungen." 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Pushbullet/i18n/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Pushbullet": { 5 | "Description": "Sends a message via Pushbullet.", 6 | "Fields": { 7 | "Message": "Message", 8 | "Message-Help": "The message to send to the Pushbullet server", 9 | "Priority": "Priority", 10 | "Priority-Help": "The priority of the message being sent", 11 | "Title": "Title" 12 | }, 13 | "Outputs": { 14 | "1": "Pushbullet message sent", 15 | "2": "Pushbullet message failed to send" 16 | } 17 | } 18 | } 19 | }, 20 | "Plugins": { 21 | "Pushbullet": { 22 | "Description": "A plugin that allows you to send messages to a Pushbullet server.", 23 | "Label": "Pushbullet", 24 | "Fields": { 25 | "ApiToken": "API Token", 26 | "ApiToken-Help": "The application API token use for sending notifications.", 27 | "UserKey": "User Key", 28 | "UserKey-Help": "Your personal user key for receiving notifications." 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Pushbullet/i18n/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Pushbullet": { 5 | "Description": "Envía un mensaje a través de Pushbullet.", 6 | "Fields": { 7 | "Message": "Mensaje", 8 | "Message-Help": "El mensaje a enviar al servidor Pushbullet", 9 | "Priority": "Prioridad", 10 | "Priority-Help": "La prioridad del mensaje que se está enviando", 11 | "Title": "Título" 12 | }, 13 | "Outputs": { 14 | "1": "Mensaje de Pushbullet enviado", 15 | "2": "Error al enviar el mensaje de Pushbullet" 16 | } 17 | } 18 | } 19 | }, 20 | "Plugins": { 21 | "Pushbullet": { 22 | "Description": "Un plugin que te permite enviar mensajes a un servidor Pushbullet.", 23 | "Label": "Pushbullet", 24 | "Fields": { 25 | "ApiToken": "Token de API", 26 | "ApiToken-Help": "El token de API de la aplicación utilizado para enviar notificaciones.", 27 | "UserKey": "Clave de usuario", 28 | "UserKey-Help": "Tu clave de usuario personal para recibir notificaciones." 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Pushbullet/i18n/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Pushbullet": { 5 | "Description": "Envoie un message via Pushbullet.", 6 | "Fields": { 7 | "Message": "Message", 8 | "Message-Help": "Le message à envoyer au serveur Pushbullet", 9 | "Priority": "Priorité", 10 | "Priority-Help": "La priorité du message envoyé", 11 | "Title": "Titre" 12 | }, 13 | "Outputs": { 14 | "1": "Message Pushbullet envoyé", 15 | "2": "Échec de l'envoi du message Pushbullet" 16 | } 17 | } 18 | } 19 | }, 20 | "Plugins": { 21 | "Pushbullet": { 22 | "Description": "Un plugin qui vous permet d'envoyer des messages à un serveur Pushbullet.", 23 | "Label": "Pushbullet", 24 | "Fields": { 25 | "ApiToken": "Token API", 26 | "ApiToken-Help": "Le token API de l'application utilisé pour envoyer des notifications.", 27 | "UserKey": "Clé utilisateur", 28 | "UserKey-Help": "Votre clé utilisateur personnelle pour recevoir des notifications." 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Pushbullet/i18n/it.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Pushbullet": { 5 | "Description": "Invia un messaggio tramite Pushbullet.", 6 | "Fields": { 7 | "Message": "Messaggio", 8 | "Message-Help": "Il messaggio da inviare al server Pushbullet", 9 | "Priority": "Priorità", 10 | "Priority-Help": "La priorità del messaggio in fase di invio", 11 | "Title": "Titolo" 12 | }, 13 | "Outputs": { 14 | "1": "Messaggio Pushbullet inviato", 15 | "2": "Invio del messaggio Pushbullet non riuscito" 16 | } 17 | } 18 | } 19 | }, 20 | "Plugins": { 21 | "Pushbullet": { 22 | "Description": "Un plugin che ti consente di inviare messaggi a un server Pushbullet.", 23 | "Label": "Pushbullet", 24 | "Fields": { 25 | "ApiToken": "Token API", 26 | "ApiToken-Help": "Il token API dell'applicazione utilizzato per inviare notifiche.", 27 | "UserKey": "Chiave utente", 28 | "UserKey-Help": "La tua chiave utente personale per ricevere notifiche." 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Pushbullet/i18n/ja.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Pushbullet": { 5 | "Description": "Pushbullet経由でメッセージを送信します。", 6 | "Fields": { 7 | "Message": "メッセージ", 8 | "Message-Help": "Pushbulletサーバーに送信するメッセージ", 9 | "Priority": "優先度", 10 | "Priority-Help": "送信中のメッセージの優先度", 11 | "Title": "タイトル" 12 | }, 13 | "Outputs": { 14 | "1": "Pushbulletメッセージを送信しました", 15 | "2": "Pushbulletメッセージの送信に失敗しました" 16 | } 17 | } 18 | } 19 | }, 20 | "Plugins": { 21 | "Pushbullet": { 22 | "Description": "Pushbulletサーバーにメッセージを送信するためのプラグインです。", 23 | "Label": "Pushbullet", 24 | "Fields": { 25 | "ApiToken": "APIトークン", 26 | "ApiToken-Help": "通知を送信するために使用するアプリケーションAPIトークン。", 27 | "UserKey": "ユーザーキー", 28 | "UserKey-Help": "通知を受信するためのあなたの個人ユーザーキー。" 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Pushbullet/i18n/ko.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Pushbullet": { 5 | "Description": "Pushbullet을 통해 메시지를 전송합니다.", 6 | "Fields": { 7 | "Message": "메시지", 8 | "Message-Help": "Pushbullet 서버로 전송할 메시지", 9 | "Priority": "우선 순위", 10 | "Priority-Help": "전송되는 메시지의 우선 순위", 11 | "Title": "제목" 12 | }, 13 | "Outputs": { 14 | "1": "Pushbullet 메시지가 전송되었습니다.", 15 | "2": "Pushbullet 메시지 전송에 실패했습니다." 16 | } 17 | } 18 | } 19 | }, 20 | "Plugins": { 21 | "Pushbullet": { 22 | "Description": "Pushbullet 서버에 메시지를 전송할 수 있는 플러그인입니다.", 23 | "Label": "Pushbullet", 24 | "Fields": { 25 | "ApiToken": "API 토큰", 26 | "ApiToken-Help": "알림 전송에 사용되는 애플리케이션 API 토큰입니다.", 27 | "UserKey": "사용자 키", 28 | "UserKey-Help": "알림 수신을 위한 개인 사용자 키입니다." 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Pushbullet/i18n/nl.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Pushbullet": { 5 | "Description": "Verstuur een bericht via Pushbullet.", 6 | "Fields": { 7 | "Message": "Bericht", 8 | "Message-Help": "Het bericht dat naar de Pushbullet-server moet worden verzonden", 9 | "Priority": "Prioriteit", 10 | "Priority-Help": "De prioriteit van het verzonden bericht", 11 | "Title": "Titel" 12 | }, 13 | "Outputs": { 14 | "1": "Pushbullet-bericht verzonden", 15 | "2": "Verzenden van Pushbullet-bericht mislukt" 16 | } 17 | } 18 | } 19 | }, 20 | "Plugins": { 21 | "Pushbullet": { 22 | "Description": "Een plugin die je in staat stelt om berichten naar een Pushbullet-server te verzenden.", 23 | "Label": "Pushbullet", 24 | "Fields": { 25 | "ApiToken": "API-token", 26 | "ApiToken-Help": "De applicatie-API-token die wordt gebruikt om meldingen te verzenden.", 27 | "UserKey": "Gebruikerssleutel", 28 | "UserKey-Help": "Je persoonlijke gebruikerssleutel voor het ontvangen van meldingen." 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Pushbullet/i18n/pt.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Pushbullet": { 5 | "Description": "Envia uma mensagem via Pushbullet.", 6 | "Fields": { 7 | "Message": "Mensagem", 8 | "Message-Help": "A mensagem a ser enviada ao servidor Pushbullet", 9 | "Priority": "Prioridade", 10 | "Priority-Help": "A prioridade da mensagem sendo enviada", 11 | "Title": "Título" 12 | }, 13 | "Outputs": { 14 | "1": "Mensagem Pushbullet enviada", 15 | "2": "Falha ao enviar a mensagem Pushbullet" 16 | } 17 | } 18 | } 19 | }, 20 | "Plugins": { 21 | "Pushbullet": { 22 | "Description": "Um plugin que permite enviar mensagens para um servidor Pushbullet.", 23 | "Label": "Pushbullet", 24 | "Fields": { 25 | "ApiToken": "Token da API", 26 | "ApiToken-Help": "O token da API da aplicação usado para enviar notificações.", 27 | "UserKey": "Chave do usuário", 28 | "UserKey-Help": "Sua chave de usuário pessoal para receber notificações." 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Pushbullet/i18n/ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Pushbullet": { 5 | "Description": "Отправляет сообщение через Pushbullet.", 6 | "Fields": { 7 | "Message": "Сообщение", 8 | "Message-Help": "Сообщение, которое нужно отправить на сервер Pushbullet", 9 | "Priority": "Приоритет", 10 | "Priority-Help": "Приоритет отправляемого сообщения", 11 | "Title": "Заголовок" 12 | }, 13 | "Outputs": { 14 | "1": "Сообщение Pushbullet отправлено", 15 | "2": "Не удалось отправить сообщение Pushbullet" 16 | } 17 | } 18 | } 19 | }, 20 | "Plugins": { 21 | "Pushbullet": { 22 | "Description": "Плагин, который позволяет вам отправлять сообщения на сервер Pushbullet.", 23 | "Label": "Pushbullet", 24 | "Fields": { 25 | "ApiToken": "API-токен", 26 | "ApiToken-Help": "Токен API приложения, используемый для отправки уведомлений.", 27 | "UserKey": "Ключ пользователя", 28 | "UserKey-Help": "Ваш личный ключ пользователя для получения уведомлений." 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Pushbullet/i18n/sv.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Pushbullet": { 5 | "Description": "Skickar ett meddelande via Pushbullet.", 6 | "Fields": { 7 | "Message": "Meddelande", 8 | "Message-Help": "Meddelandet som ska skickas till Pushbullet-servern", 9 | "Priority": "Prioritet", 10 | "Priority-Help": "Prioriteten för det meddelande som skickas", 11 | "Title": "Titel" 12 | }, 13 | "Outputs": { 14 | "1": "Pushbullet-meddelande skickat", 15 | "2": "Misslyckades med att skicka Pushbullet-meddelande" 16 | } 17 | } 18 | } 19 | }, 20 | "Plugins": { 21 | "Pushbullet": { 22 | "Description": "Ett plugin som gör att du kan skicka meddelanden till en Pushbullet-server.", 23 | "Label": "Pushbullet", 24 | "Fields": { 25 | "ApiToken": "API-token", 26 | "ApiToken-Help": "API-token för appen som används för att skicka aviseringar.", 27 | "UserKey": "Användarnyckel", 28 | "UserKey-Help": "Din personliga användarnyckel för att ta emot aviseringar." 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Pushbullet/i18n/zh.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Pushbullet": { 5 | "Description": "通过Pushbullet发送消息。", 6 | "Fields": { 7 | "Message": "消息", 8 | "Message-Help": "要发送到Pushbullet服务器的消息", 9 | "Priority": "优先级", 10 | "Priority-Help": "发送消息的优先级", 11 | "Title": "标题" 12 | }, 13 | "Outputs": { 14 | "1": "Pushbullet消息已发送", 15 | "2": "Pushbullet消息发送失败" 16 | } 17 | } 18 | } 19 | }, 20 | "Plugins": { 21 | "Pushbullet": { 22 | "Description": "一个允许您向Pushbullet服务器发送消息的插件。", 23 | "Label": "Pushbullet", 24 | "Fields": { 25 | "ApiToken": "API令牌", 26 | "ApiToken-Help": "用于发送通知的应用程序API令牌。", 27 | "UserKey": "用户密钥", 28 | "UserKey-Help": "您用于接收通知的个人用户密钥。" 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Pushbullet/i18n/zht.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Pushbullet": { 5 | "Description": "透過Pushbullet發送消息。", 6 | "Fields": { 7 | "Message": "消息", 8 | "Message-Help": "要發送到Pushbullet伺服器的消息", 9 | "Priority": "優先級", 10 | "Priority-Help": "正在發送的消息的優先級", 11 | "Title": "標題" 12 | }, 13 | "Outputs": { 14 | "1": "Pushbullet消息已發送", 15 | "2": "Pushbullet消息發送失敗" 16 | } 17 | } 18 | } 19 | }, 20 | "Plugins": { 21 | "Pushbullet": { 22 | "Description": "允許您向Pushbullet伺服器發送消息的插件。", 23 | "Label": "Pushbullet", 24 | "Fields": { 25 | "ApiToken": "API令牌", 26 | "ApiToken-Help": "用於發送通知的應用程序API令牌。", 27 | "UserKey": "用戶密鑰", 28 | "UserKey-Help": "您個人的用戶密鑰,用於接收通知。" 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Pushover/ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Pushover; 2 | 3 | /// 4 | /// Extension methods 5 | /// 6 | internal static class ExtensionMethods 7 | { 8 | /// 9 | /// Returns an empty string as null, otherwise returns the original string 10 | /// 11 | /// the input string 12 | /// the string or null if empty 13 | public static string? EmptyAsNull(this string str) => str == string.Empty ? null : str; 14 | } 15 | -------------------------------------------------------------------------------- /Pushover/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Text; 3 | global using System.ComponentModel.DataAnnotations; 4 | global using FileFlows.Plugin; 5 | global using FileFlows.Plugin.Attributes; -------------------------------------------------------------------------------- /Pushover/Plugin.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Pushover; 2 | 3 | /// 4 | /// A Pushover Plugin 5 | /// 6 | public class Plugin : FileFlows.Plugin.IPlugin 7 | { 8 | /// 9 | /// Gets the UID for this plugin 10 | /// 11 | public Guid Uid => new Guid("99cc0b7e-e470-4829-9a3b-30e8cc2ee749"); 12 | 13 | /// 14 | /// Gets the name of this plugin 15 | /// 16 | public string Name => "Pushover"; 17 | 18 | /// 19 | /// Gets the minimum version of FileFlows required for this plugin 20 | /// 21 | public string MinimumVersion => "1.0.4.2019"; 22 | /// 23 | public string Icon => "svg:pushover"; 24 | 25 | /// 26 | /// Initializes this plugin 27 | /// 28 | public void Init() 29 | { 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Pushover/PluginSettings.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Pushover; 2 | 3 | /// 4 | /// The plugin settings for this plugin 5 | /// 6 | public class PluginSettings : IPluginSettings 7 | { 8 | /// 9 | /// Gets or sets the user key to send the push over notification to 10 | /// 11 | [Text(1)] 12 | [Required] 13 | public string UserKey { get; set; } = string.Empty; 14 | 15 | /// 16 | /// Gets or sets the API Token 17 | /// 18 | [Text(2)] 19 | [Required] 20 | public string ApiToken { get; set; } = string.Empty; 21 | } 22 | -------------------------------------------------------------------------------- /Pushover/i18n/ja.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Pushover": { 5 | "Description": "Pushoverを介してメッセージを送信します。", 6 | "Fields": { 7 | "Expire": "有効期限", 8 | "Expire-Help": "この緊急メッセージの有効期限が切れるまでの時間(秒)を設定し、配信の再試行を停止します。", 9 | "Expire-Suffix": "秒", 10 | "Message": "メッセージ", 11 | "Message-Help": "Pushoverサーバーに送信するメッセージ", 12 | "Priority": "優先度", 13 | "Priority-Help": "送信するメッセージの優先度", 14 | "Retry": "再試行", 15 | "Retry-Help": "配信失敗の場合にこの緊急メッセージの再試行間隔(秒)を指定します。", 16 | "Retry-Suffix": "秒" 17 | }, 18 | "Outputs": { 19 | "1": "Pushoverメッセージが送信されました", 20 | "2": "Pushoverメッセージの送信に失敗しました" 21 | } 22 | } 23 | } 24 | }, 25 | "Plugins": { 26 | "Pushover": { 27 | "Description": "Pushoverサーバーにメッセージを送信するためのプラグインです。", 28 | "Label": "Pushover", 29 | "Fields": { 30 | "ApiToken": "APIトークン", 31 | "ApiToken-Help": "通知を送信するために使用されるアプリケーションのAPIトークン。", 32 | "UserKey": "ユーザーキー", 33 | "UserKey-Help": "通知を受信するためのあなたの個人ユーザーキー。" 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Pushover/i18n/ko.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Pushover": { 5 | "Description": "Pushover을 통해 메시지를 전송합니다.", 6 | "Fields": { 7 | "Expire": "만료", 8 | "Expire-Help": "이 긴급 메시지가 만료되어 전송 재시도를 중단하기 전에 지속할 시간을 초 단위로 설정합니다.", 9 | "Expire-Suffix": "초", 10 | "Message": "메시지", 11 | "Message-Help": "Pushover 서버로 전송할 메시지", 12 | "Priority": "우선 순위", 13 | "Priority-Help": "전송되는 메시지의 우선 순위", 14 | "Retry": "재시도", 15 | "Retry-Help": "전송 실패 시 이 긴급 메시지에 대한 재시도 시도 간의 간격을 초 단위로 지정합니다.", 16 | "Retry-Suffix": "초" 17 | }, 18 | "Outputs": { 19 | "1": "Pushover 메시지가 전송되었습니다.", 20 | "2": "Pushover 메시지 전송에 실패했습니다." 21 | } 22 | } 23 | } 24 | }, 25 | "Plugins": { 26 | "Pushover": { 27 | "Description": "Pushover 서버에 메시지를 전송할 수 있는 플러그인입니다.", 28 | "Label": "Pushover", 29 | "Fields": { 30 | "ApiToken": "API 토큰", 31 | "ApiToken-Help": "알림 전송에 사용되는 애플리케이션 API 토큰입니다.", 32 | "UserKey": "사용자 키", 33 | "UserKey-Help": "알림 수신을 위한 개인 사용자 키입니다." 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Pushover/i18n/zh.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Pushover": { 5 | "Description": "通过 Pushover 发送消息。", 6 | "Fields": { 7 | "Expire": "过期", 8 | "Expire-Help": "设置此紧急消息过期前的持续时间(以秒为单位),并停止重试交付。", 9 | "Expire-Suffix": "秒", 10 | "Message": "消息", 11 | "Message-Help": "要发送到 Pushover 服务器的消息", 12 | "Priority": "优先级", 13 | "Priority-Help": "发送消息的优先级", 14 | "Retry": "重试", 15 | "Retry-Help": "指定此紧急消息在交付失败时的重试间隔(以秒为单位)。", 16 | "Retry-Suffix": "秒" 17 | }, 18 | "Outputs": { 19 | "1": "Pushover 消息已发送", 20 | "2": "Pushover 消息发送失败" 21 | } 22 | } 23 | } 24 | }, 25 | "Plugins": { 26 | "Pushover": { 27 | "Description": "一个允许你向 Pushover 服务器发送消息的插件。", 28 | "Label": "Pushover", 29 | "Fields": { 30 | "ApiToken": "API 令牌", 31 | "ApiToken-Help": "用于发送通知的应用程序 API 令牌。", 32 | "UserKey": "用户密钥", 33 | "UserKey-Help": "您用于接收通知的个人用户密钥。" 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Pushover/i18n/zht.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Pushover": { 5 | "Description": "透過Pushover發送消息。", 6 | "Fields": { 7 | "Expire": "過期", 8 | "Expire-Help": "設置此緊急消息過期並停止重試交付之前的持續時間(以秒為單位)。", 9 | "Expire-Suffix": "秒", 10 | "Message": "消息", 11 | "Message-Help": "要發送到Pushover伺服器的消息", 12 | "Priority": "優先級", 13 | "Priority-Help": "正在發送的消息的優先級", 14 | "Retry": "重試", 15 | "Retry-Help": "指定在交付失敗的情況下,此緊急消息的重試嘗試之間的間隔(以秒為單位)。", 16 | "Retry-Suffix": "秒" 17 | }, 18 | "Outputs": { 19 | "1": "Pushover消息已發送", 20 | "2": "Pushover消息發送失敗" 21 | } 22 | } 23 | } 24 | }, 25 | "Plugins": { 26 | "Pushover": { 27 | "Description": "允許您向Pushover伺服器發送消息的插件。", 28 | "Label": "Pushover", 29 | "Fields": { 30 | "ApiToken": "API令牌", 31 | "ApiToken-Help": "用於發送通知的應用程序API令牌。", 32 | "UserKey": "用戶密鑰", 33 | "UserKey-Help": "您個人的用戶密鑰,用於接收通知。" 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Telegram/ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Telegram; 2 | 3 | /// 4 | /// Extension methods 5 | /// 6 | internal static class ExtensionMethods 7 | { 8 | /// 9 | /// Returns an empty string as null, otherwise returns the original string 10 | /// 11 | /// the input string 12 | /// the string or null if empty 13 | public static string? EmptyAsNull(this string str) => str == string.Empty ? null : str; 14 | } 15 | -------------------------------------------------------------------------------- /Telegram/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Text; 3 | global using System.ComponentModel.DataAnnotations; 4 | global using FileFlows.Plugin; 5 | global using FileFlows.Plugin.Attributes; -------------------------------------------------------------------------------- /Telegram/Plugin.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Telegram; 2 | 3 | /// 4 | /// A Telegram Plugin 5 | /// 6 | public class Plugin : FileFlows.Plugin.IPlugin 7 | { 8 | /// 9 | /// Gets the UID for this plugin 10 | /// 11 | public Guid Uid => new Guid("a610837d-c6d6-438b-8470-33a407ea7c98"); 12 | 13 | /// 14 | /// Gets the name of this plugin 15 | /// 16 | public string Name => "Telegram"; 17 | 18 | /// 19 | /// Gets the minimum version of FileFlows required for this plugin 20 | /// 21 | public string MinimumVersion => "1.0.4.2019"; 22 | 23 | /// 24 | public string Icon => "fab fa-telegram-plane"; 25 | 26 | /// 27 | /// Initializes this plugin 28 | /// 29 | public void Init() 30 | { 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Telegram/PluginSettings.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Telegram; 2 | 3 | /// 4 | /// The plugin settings for this plugin 5 | /// 6 | public class PluginSettings : IPluginSettings 7 | { 8 | /// 9 | /// Gets or sets the bot token 10 | /// 11 | [Text(1)] 12 | [Required] 13 | public string BotToken { get; set; } = string.Empty; 14 | 15 | /// 16 | /// Gets or sets the chat ID 17 | /// 18 | [Text(1)] 19 | [Required] 20 | public string ChatId { get; set; } = string.Empty; 21 | } -------------------------------------------------------------------------------- /Telegram/i18n/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Telegram": { 5 | "Description": "Sendet eine Telegram-Nachricht.", 6 | "Fields": { 7 | "Message": "Nachricht", 8 | "Message-Help": "Die zu sendende Nachricht." 9 | }, 10 | "Outputs": { 11 | "1": "Telegram-Nachricht gesendet", 12 | "2": "Telegram-Nachricht konnte nicht gesendet werden" 13 | } 14 | } 15 | } 16 | }, 17 | "Plugins": { 18 | "Telegram": { 19 | "Description": "Ein Plugin, das es Ihnen ermöglicht, Telegram-Nachrichten zu senden.", 20 | "Label": "Telegram", 21 | "Fields": { 22 | "BotToken": "Bot-Token", 23 | "BotToken-Help": "Ihr Telegram-Bot-Token.", 24 | "ChatId": "Chat-ID", 25 | "ChatId-Help": "Die ID des Chats, an den Nachrichten gesendet werden sollen." 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Telegram/i18n/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Telegram": { 5 | "Description": "Sends a Telegram message.", 6 | "Fields": { 7 | "Message": "Message", 8 | "Message-Help": "The message to send." 9 | }, 10 | "Outputs": { 11 | "1": "Telegram message sent", 12 | "2": "Telegram message failed to send" 13 | } 14 | } 15 | } 16 | }, 17 | "Plugins": { 18 | "Telegram": { 19 | "Description": "A plugin that allows you to send Telegram messages.", 20 | "Label": "Telegram", 21 | "Fields": { 22 | "BotToken": "Bot Token", 23 | "BotToken-Help": "Your telegram bot token.", 24 | "ChatId": "Chat ID", 25 | "ChatId-Help": "The ID of the chat to send messages to." 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Telegram/i18n/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Telegram": { 5 | "Description": "Envía un mensaje de Telegram.", 6 | "Fields": { 7 | "Message": "Mensaje", 8 | "Message-Help": "El mensaje a enviar." 9 | }, 10 | "Outputs": { 11 | "1": "Mensaje de Telegram enviado", 12 | "2": "Error al enviar el mensaje de Telegram" 13 | } 14 | } 15 | } 16 | }, 17 | "Plugins": { 18 | "Telegram": { 19 | "Description": "Un plugin que te permite enviar mensajes de Telegram.", 20 | "Label": "Telegram", 21 | "Fields": { 22 | "BotToken": "Token del bot", 23 | "BotToken-Help": "Tu token de bot de Telegram.", 24 | "ChatId": "ID del chat", 25 | "ChatId-Help": "La ID del chat al que enviar mensajes." 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Telegram/i18n/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Telegram": { 5 | "Description": "Envoie un message Telegram.", 6 | "Fields": { 7 | "Message": "Message", 8 | "Message-Help": "Le message à envoyer." 9 | }, 10 | "Outputs": { 11 | "1": "Message Telegram envoyé", 12 | "2": "Échec de l'envoi du message Telegram" 13 | } 14 | } 15 | } 16 | }, 17 | "Plugins": { 18 | "Telegram": { 19 | "Description": "Un plugin qui vous permet d'envoyer des messages Telegram.", 20 | "Label": "Telegram", 21 | "Fields": { 22 | "BotToken": "Token du bot", 23 | "BotToken-Help": "Votre token de bot Telegram.", 24 | "ChatId": "ID de chat", 25 | "ChatId-Help": "L'ID du chat auquel envoyer des messages." 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Telegram/i18n/it.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Telegram": { 5 | "Description": "Invia un messaggio Telegram.", 6 | "Fields": { 7 | "Message": "Messaggio", 8 | "Message-Help": "Il messaggio da inviare." 9 | }, 10 | "Outputs": { 11 | "1": "Messaggio Telegram inviato", 12 | "2": "Invio del messaggio Telegram fallito" 13 | } 14 | } 15 | } 16 | }, 17 | "Plugins": { 18 | "Telegram": { 19 | "Description": "Un plugin che ti consente di inviare messaggi Telegram.", 20 | "Label": "Telegram", 21 | "Fields": { 22 | "BotToken": "Token Bot", 23 | "BotToken-Help": "Il tuo token bot di Telegram.", 24 | "ChatId": "ID Chat", 25 | "ChatId-Help": "L'ID della chat a cui inviare i messaggi." 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Telegram/i18n/ja.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Telegram": { 5 | "Description": "Telegramメッセージを送信します。", 6 | "Fields": { 7 | "Message": "メッセージ", 8 | "Message-Help": "送信するメッセージ。" 9 | }, 10 | "Outputs": { 11 | "1": "Telegramメッセージが送信されました", 12 | "2": "Telegramメッセージの送信に失敗しました" 13 | } 14 | } 15 | } 16 | }, 17 | "Plugins": { 18 | "Telegram": { 19 | "Description": "Telegramメッセージを送信するためのプラグインです。", 20 | "Label": "Telegram", 21 | "Fields": { 22 | "BotToken": "ボットトークン", 23 | "BotToken-Help": "あなたのTelegramボットトークン。", 24 | "ChatId": "チャットID", 25 | "ChatId-Help": "メッセージを送信するチャットのID。" 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Telegram/i18n/ko.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Telegram": { 5 | "Description": "텔레그램 메시지를 전송합니다.", 6 | "Fields": { 7 | "Message": "메시지", 8 | "Message-Help": "전송할 메시지." 9 | }, 10 | "Outputs": { 11 | "1": "텔레그램 메시지가 전송되었습니다.", 12 | "2": "텔레그램 메시지 전송에 실패했습니다." 13 | } 14 | } 15 | } 16 | }, 17 | "Plugins": { 18 | "Telegram": { 19 | "Description": "텔레그램 메시지를 전송할 수 있는 플러그인입니다.", 20 | "Label": "텔레그램", 21 | "Fields": { 22 | "BotToken": "봇 토큰", 23 | "BotToken-Help": "당신의 텔레그램 봇 토큰입니다.", 24 | "ChatId": "채팅 ID", 25 | "ChatId-Help": "메시지를 전송할 채팅의 ID입니다." 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Telegram/i18n/nl.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Telegram": { 5 | "Description": "Verzendt een Telegram-bericht.", 6 | "Fields": { 7 | "Message": "Bericht", 8 | "Message-Help": "Het te verzenden bericht." 9 | }, 10 | "Outputs": { 11 | "1": "Telegram-bericht verzonden", 12 | "2": "Verzending van Telegram-bericht mislukt" 13 | } 14 | } 15 | } 16 | }, 17 | "Plugins": { 18 | "Telegram": { 19 | "Description": "Een plugin die je in staat stelt om Telegram-berichten te verzenden.", 20 | "Label": "Telegram", 21 | "Fields": { 22 | "BotToken": "Bot-token", 23 | "BotToken-Help": "Je Telegram-bot-token.", 24 | "ChatId": "Chat-ID", 25 | "ChatId-Help": "De ID van de chat om berichten naar te verzenden." 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Telegram/i18n/pt.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Telegram": { 5 | "Description": "Envia uma mensagem do Telegram.", 6 | "Fields": { 7 | "Message": "Mensagem", 8 | "Message-Help": "A mensagem a ser enviada." 9 | }, 10 | "Outputs": { 11 | "1": "Mensagem do Telegram enviada", 12 | "2": "Falha ao enviar a mensagem do Telegram" 13 | } 14 | } 15 | } 16 | }, 17 | "Plugins": { 18 | "Telegram": { 19 | "Description": "Um plugin que permite enviar mensagens do Telegram.", 20 | "Label": "Telegram", 21 | "Fields": { 22 | "BotToken": "Token do bot", 23 | "BotToken-Help": "Seu token de bot do Telegram.", 24 | "ChatId": "ID do chat", 25 | "ChatId-Help": "A ID do chat para enviar mensagens." 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Telegram/i18n/ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Telegram": { 5 | "Description": "Отправляет сообщение Telegram.", 6 | "Fields": { 7 | "Message": "Сообщение", 8 | "Message-Help": "Сообщение для отправки." 9 | }, 10 | "Outputs": { 11 | "1": "Сообщение Telegram отправлено", 12 | "2": "Не удалось отправить сообщение Telegram" 13 | } 14 | } 15 | } 16 | }, 17 | "Plugins": { 18 | "Telegram": { 19 | "Description": "Плагин, который позволяет отправлять сообщения Telegram.", 20 | "Label": "Telegram", 21 | "Fields": { 22 | "BotToken": "Токен бота", 23 | "BotToken-Help": "Ваш токен бота Telegram.", 24 | "ChatId": "ID чата", 25 | "ChatId-Help": "ID чата, в который нужно отправить сообщения." 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Telegram/i18n/sv.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Telegram": { 5 | "Description": "Skickar ett Telegram-meddelande.", 6 | "Fields": { 7 | "Message": "Meddelande", 8 | "Message-Help": "Meddelandet som ska skickas." 9 | }, 10 | "Outputs": { 11 | "1": "Telegram-meddelandet skickat", 12 | "2": "Misslyckades med att skicka Telegram-meddelandet" 13 | } 14 | } 15 | } 16 | }, 17 | "Plugins": { 18 | "Telegram": { 19 | "Description": "Ett plugin som gör att du kan skicka Telegram-meddelanden.", 20 | "Label": "Telegram", 21 | "Fields": { 22 | "BotToken": "Bot-token", 23 | "BotToken-Help": "Din Telegram-bot-token.", 24 | "ChatId": "Chat-ID", 25 | "ChatId-Help": "ID för chatten där meddelanden ska skickas." 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Telegram/i18n/zh.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Telegram": { 5 | "Description": "发送Telegram消息。", 6 | "Fields": { 7 | "Message": "消息", 8 | "Message-Help": "要发送的消息。" 9 | }, 10 | "Outputs": { 11 | "1": "Telegram消息已发送", 12 | "2": "发送Telegram消息失败" 13 | } 14 | } 15 | } 16 | }, 17 | "Plugins": { 18 | "Telegram": { 19 | "Description": "一个允许您发送Telegram消息的插件。", 20 | "Label": "Telegram", 21 | "Fields": { 22 | "BotToken": "机器人令牌", 23 | "BotToken-Help": "您的Telegram机器人令牌。", 24 | "ChatId": "聊天ID", 25 | "ChatId-Help": "要发送消息的聊天ID。" 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /Telegram/i18n/zht.json: -------------------------------------------------------------------------------- 1 | { 2 | "Flow": { 3 | "Parts": { 4 | "Telegram": { 5 | "Description": "發送Telegram消息。", 6 | "Fields": { 7 | "Message": "消息", 8 | "Message-Help": "要發送的消息。" 9 | }, 10 | "Outputs": { 11 | "1": "Telegram消息已發送", 12 | "2": "Telegram消息發送失敗" 13 | } 14 | } 15 | } 16 | }, 17 | "Plugins": { 18 | "Telegram": { 19 | "Description": "允許您發送Telegram消息的插件。", 20 | "Label": "Telegram", 21 | "Fields": { 22 | "BotToken": "機器人令牌", 23 | "BotToken-Help": "您的Telegram機器人令牌。", 24 | "ChatId": "聊天ID", 25 | "ChatId-Help": "要發送消息的聊天ID。" 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /VideoNodes/FfmpegBuilderNodes/EncoderAdjustments/EncoderAdjustment.cs: -------------------------------------------------------------------------------- 1 | using FileFlows.VideoNodes.FfmpegBuilderNodes.Models; 2 | 3 | namespace FileFlows.VideoNodes.FfmpegBuilderNodes.EncoderAdjustments; 4 | 5 | /// 6 | /// Adjusts FFmpeg arguments depending on devices being used 7 | /// 8 | public class EncoderAdjustment 9 | { 10 | /// 11 | /// Run any adjustments that are needed to FFmpeg arguments 12 | /// 13 | /// a logger to log to 14 | /// the FFmpeg Builder model 15 | /// the FFmpeg args to adjust 16 | /// the adjusted FFMpeg args 17 | public static List Run(ILogger logger, FfmpegModel model, List args) 18 | { 19 | if (VaapiAdjustments.IsUsingVaapi(args)) 20 | return new VaapiAdjustments().Run(logger, model, args); 21 | 22 | return args; 23 | } 24 | } -------------------------------------------------------------------------------- /VideoNodes/FfmpegBuilderNodes/EncoderAdjustments/IEncoderAdjustment.cs: -------------------------------------------------------------------------------- 1 | using FileFlows.VideoNodes.FfmpegBuilderNodes.Models; 2 | 3 | namespace FileFlows.VideoNodes.FfmpegBuilderNodes.EncoderAdjustments; 4 | 5 | /// 6 | /// Adjusts the FFmpeg commands 7 | /// 8 | public interface IEncoderAdjustment 9 | { 10 | /// 11 | /// Runs the encoder adjustments 12 | /// 13 | /// the logger to use 14 | /// the FFmpeg model 15 | /// the FFmpeg arguments 16 | /// the updated FFmpeg arguments 17 | List Run(ILogger logger, FfmpegModel model, List args); 18 | } -------------------------------------------------------------------------------- /VideoNodes/FfmpegBuilderNodes/FfmpegBuilderParameterReplacer.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.VideoNodes.FfmpegBuilderNodes; 2 | 3 | /// 4 | /// Changes the parameters in the final FFmpeg parameters 5 | /// 6 | public class FfmpegBuilderParameterReplacer : FfmpegBuilderNode 7 | { 8 | /// 9 | public override string HelpUrl => "https://fileflows.com/docs/plugins/video-nodes/ffmpeg-builder/parameter-replacer"; 10 | 11 | /// 12 | public override int Outputs => 1; 13 | 14 | /// 15 | public override string Icon => "fas fa-exchange-alt"; 16 | 17 | /// 18 | /// Gets or sets replacements to replace 19 | /// 20 | [KeyValue(1, null)] 21 | [Required] 22 | public List> Replacements{ get; set; } 23 | 24 | /// 25 | public override int Execute(NodeParameters args) 26 | { 27 | Model.ParameterReplacements = Replacements ?? []; 28 | return 1; 29 | } 30 | } -------------------------------------------------------------------------------- /VideoNodes/FfmpegBuilderNodes/FfmpegBuilderSetDevice.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.VideoNodes.FfmpegBuilderNodes; 2 | 3 | /// 4 | /// Sets the device to use 5 | /// 6 | public class FfmpegBuilderSetDevice : FfmpegBuilderNode 7 | { 8 | /// 9 | public override string HelpUrl => "https://fileflows.com/docs/plugins/video-nodes/ffmpeg-builder/set-device"; 10 | 11 | /// 12 | public override int Outputs => 1; 13 | 14 | /// 15 | public override string Icon => "fas fa-plug"; 16 | 17 | /// 18 | /// Gets or sets the device 19 | /// 20 | [TextVariable(1)] 21 | public string Device { get; set; } 22 | 23 | /// 24 | public override int Execute(NodeParameters args) 25 | { 26 | string device = args.ReplaceVariables(Device ?? string.Empty, stripMissing: true); 27 | args.Logger?.ILog("Device: " + device); 28 | 29 | Model.Device = device?.EmptyAsNull() ?? "NONE"; 30 | return 1; 31 | } 32 | } -------------------------------------------------------------------------------- /VideoNodes/FfmpegBuilderNodes/Metadata/FfmpegBuilderRemoveAttachments.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.VideoNodes.FfmpegBuilderNodes; 2 | 3 | public class FfmpegBuilderRemoveAttachments : FfmpegBuilderNode 4 | { 5 | /// 6 | /// Gets the Help Url 7 | /// 8 | public override string HelpUrl => 9 | "https://fileflows.com/docs/plugins/video-nodes/ffmpeg-builder/remove-attachments"; 10 | 11 | /// 12 | /// Gets the number of outputs 13 | /// 14 | public override int Outputs => 1; 15 | /// 16 | /// Gets the icon 17 | /// 18 | public override string Icon => "fas fa-paperclip"; 19 | 20 | public override int Execute(NodeParameters args) 21 | { 22 | Model.RemoveAttachments = true; 23 | return 1; 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderHdrToSdr.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.VideoNodes.FfmpegBuilderNodes; 2 | 3 | public class FfmpegBuilderHdrToSdr : FfmpegBuilderNode 4 | { 5 | public override int Outputs => 2; 6 | 7 | /// 8 | /// Get the help URL 9 | /// 10 | public override string HelpUrl => "https://fileflows.com/docs/plugins/video-nodes/ffmpeg-builder/hdr-to-sdr"; 11 | 12 | public override int Execute(NodeParameters args) 13 | { 14 | var videoInfo = GetVideoInfo(args); 15 | if (videoInfo == null || videoInfo.VideoStreams?.Any() != true) 16 | return -1; 17 | 18 | var vidStream = Model.VideoStreams?.Where(x => x.Deleted == false && x.Stream?.HDR == true).FirstOrDefault(); 19 | if (vidStream == null) 20 | { 21 | args.Logger.ILog("No HDR video stream found"); 22 | return 2; 23 | } 24 | 25 | vidStream.Filter.Add("zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0,zscale=t=bt709:m=bt709:r=tv,format=yuv420p"); 26 | 27 | return 1; 28 | } 29 | } -------------------------------------------------------------------------------- /VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderRemuxToMkv.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.VideoNodes.FfmpegBuilderNodes; 2 | 3 | public class FfmpegBuilderRemuxToMkv : FfmpegBuilderNode 4 | { 5 | /// 6 | public override string Icon => "svg:mkv"; 7 | /// 8 | public override string HelpUrl => "https://fileflows.com/docs/plugins/video-nodes/ffmpeg-builder/remux-to-mkv"; 9 | 10 | /// 11 | public override int Execute(NodeParameters args) 12 | { 13 | if (this.Model.Extension?.ToLowerInvariant() != "mkv") 14 | { 15 | args.Logger?.ILog($"Needs remuxing from '{this.Model.Extension}' to 'mkv', forcing encode"); 16 | this.Model.ForceEncode = true; 17 | } 18 | 19 | this.Model.Extension = "mkv"; 20 | return 1; 21 | } 22 | } -------------------------------------------------------------------------------- /VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderRemuxToMov.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.VideoNodes.FfmpegBuilderNodes; 2 | 3 | public class FfmpegBuilderRemuxToMov : FfmpegBuilderNode 4 | { 5 | /// 6 | public override string Icon => "svg:mov"; 7 | /// 8 | public override string HelpUrl => "https://fileflows.com/docs/plugins/video-nodes/ffmpeg-builder/remux-to-mov"; 9 | 10 | /// 11 | public override int Execute(NodeParameters args) 12 | { 13 | if (this.Model.Extension?.ToLowerInvariant() != "mov") 14 | { 15 | args.Logger?.ILog($"Needs remuxing from '{this.Model.Extension}' to 'mov', forcing encode"); 16 | this.Model.ForceEncode = true; 17 | } 18 | this.Model.Extension = "mov"; 19 | return 1; 20 | } 21 | } -------------------------------------------------------------------------------- /VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderRemuxToMxf.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.VideoNodes.FfmpegBuilderNodes; 2 | 3 | /// 4 | /// Remuxes a file to the MXF container 5 | /// 6 | public class FfmpegBuilderRemuxToMxf : FfmpegBuilderNode 7 | { 8 | /// 9 | /// Gets or sets the URL to the helper page 10 | /// 11 | public override string HelpUrl => "https://fileflows.com/docs/plugins/video-nodes/ffmpeg-builder/remux-to-mxf"; 12 | 13 | /// 14 | /// Gets that this is an enterprise flow element 15 | /// 16 | public override LicenseLevel LicenseLevel => LicenseLevel.Enterprise; 17 | 18 | /// 19 | public override string Icon => "svg:mxf"; 20 | 21 | /// 22 | public override int Execute(NodeParameters args) 23 | { 24 | if (this.Model.Extension?.ToLowerInvariant() != "mxf") 25 | { 26 | args.Logger?.ILog($"Needs remuxing from '{this.Model.Extension}' to 'mxf', forcing encode"); 27 | this.Model.ForceEncode = true; 28 | } 29 | this.Model.Extension = "mxf"; 30 | return 1; 31 | } 32 | } -------------------------------------------------------------------------------- /VideoNodes/FfmpegBuilderNodes/Video/FfmpegBuilderRemuxToWebm.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.VideoNodes.FfmpegBuilderNodes; 2 | 3 | /// 4 | /// Remuxes a file to webm 5 | /// 6 | public class FfmpegBuilderRemuxToWebm : FfmpegBuilderNode 7 | { 8 | /// 9 | public override string Icon => "svg:webm"; 10 | /// 11 | public override string HelpUrl => "https://fileflows.com/docs/plugins/video-nodes/ffmpeg-builder/remux-to-webm"; 12 | 13 | /// 14 | public override int Execute(NodeParameters args) 15 | { 16 | if (this.Model.Extension?.ToLowerInvariant() != "webm") 17 | { 18 | args.Logger?.ILog($"Needs remuxing from '{this.Model.Extension}' to 'webm', forcing encode"); 19 | this.Model.ForceEncode = true; 20 | } 21 | this.Model.Extension = "webm"; 22 | return 1; 23 | } 24 | } -------------------------------------------------------------------------------- /VideoNodes/FfmpegBuilderNodes/Video/HardwareEncoders/DisableAmd.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.VideoNodes.FfmpegBuilderNodes; 2 | 3 | /// 4 | /// Disabled AMD encoding 5 | /// 6 | public class DisableAmd:DisableEncoder 7 | { 8 | /// 9 | /// Gets the encoder variable 10 | /// 11 | protected override string EncoderVariable => "NoAMD"; 12 | 13 | /// 14 | public override string Icon => "svg:amd"; 15 | 16 | /// 17 | /// Gets the help URL 18 | /// 19 | public override string HelpUrl => "https://fileflows.com/docs/plugins/video-nodes/hardware-encoders/disable-amd"; 20 | } -------------------------------------------------------------------------------- /VideoNodes/FfmpegBuilderNodes/Video/HardwareEncoders/DisableEncoder.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.VideoNodes.FfmpegBuilderNodes; 2 | 3 | /// 4 | /// Flow element that disables an encoder 5 | /// 6 | public abstract class DisableEncoder:Node 7 | { 8 | /// 9 | /// Gets the number of inputs 10 | /// 11 | public override int Inputs => 1; 12 | /// 13 | /// Gets the number of outputs 14 | /// 15 | public override int Outputs => 1; 16 | /// 17 | /// Gets the icon 18 | /// 19 | public override string Icon => "far fa-times-circle"; 20 | /// 21 | /// Gets the flow element type 22 | /// 23 | public override FlowElementType Type => FlowElementType.Logic; 24 | 25 | /// 26 | /// Gets the encoder variable to set to disabled 27 | /// 28 | protected abstract string EncoderVariable { get; } 29 | 30 | /// 31 | /// Executes the node 32 | /// 33 | /// 34 | /// 35 | public override int Execute(NodeParameters args) 36 | { 37 | args.Variables[EncoderVariable] = true; 38 | return 1; 39 | } 40 | } -------------------------------------------------------------------------------- /VideoNodes/FfmpegBuilderNodes/Video/HardwareEncoders/DisableIntelQsv.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.VideoNodes.FfmpegBuilderNodes; 2 | 3 | /// 4 | /// Disabled Intel QSV encoding 5 | /// 6 | public class DisableIntelQsv:DisableEncoder 7 | { 8 | /// 9 | /// Gets the encoder variable 10 | /// 11 | protected override string EncoderVariable => "NoQSV"; 12 | 13 | /// 14 | public override string Icon => "svg:intel"; 15 | 16 | /// 17 | /// Gets the help URL 18 | /// 19 | public override string HelpUrl => "https://fileflows.com/docs/plugins/video-nodes/hardware-encoders/disable-intel-qsv"; 20 | } -------------------------------------------------------------------------------- /VideoNodes/FfmpegBuilderNodes/Video/HardwareEncoders/DisableNvidia.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.VideoNodes.FfmpegBuilderNodes; 2 | 3 | /// 4 | /// Disabled NVIDIA encoding 5 | /// 6 | public class DisableNvidia:DisableEncoder 7 | { 8 | /// 9 | /// Gets the encoder variable 10 | /// 11 | protected override string EncoderVariable => "NoNvidia"; 12 | 13 | /// 14 | public override string Icon => "svg:nvidia"; 15 | 16 | /// 17 | /// Gets the help URL 18 | /// 19 | public override string HelpUrl => "https://fileflows.com/docs/plugins/video-nodes/hardware-encoders/disable-nvidia"; 20 | } -------------------------------------------------------------------------------- /VideoNodes/FfmpegBuilderNodes/Video/HardwareEncoders/DisableVaapi.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.VideoNodes.FfmpegBuilderNodes; 2 | 3 | /// 4 | /// Disabled VAAPI encoding 5 | /// 6 | public class DisableVaapi:DisableEncoder 7 | { 8 | /// 9 | /// Gets the encoder variable 10 | /// 11 | protected override string EncoderVariable => "NoVAAPI"; 12 | 13 | /// 14 | /// Gets the help URL 15 | /// 16 | public override string HelpUrl => "https://fileflows.com/docs/plugins/video-nodes/hardware-encoders/disable-vaapi"; 17 | } -------------------------------------------------------------------------------- /VideoNodes/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Linq; 3 | global using System.Collections.Generic; 4 | global using System.Text.RegularExpressions; 5 | global using System.ComponentModel.DataAnnotations; 6 | global using System.ComponentModel; 7 | global using System.Threading; 8 | global using System.Threading.Tasks; 9 | global using FileFlows.Plugin; 10 | global using FileFlows.Plugin.Attributes; 11 | global using FileFlows.Plugin.Helpers; 12 | global using FileFlows.Common; -------------------------------------------------------------------------------- /VideoNodes/Globals.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.VideoNodes; 2 | 3 | public class Globals 4 | { 5 | internal const string PIX_FORMAT = "#PXT_FORMAT"; 6 | } -------------------------------------------------------------------------------- /VideoNodes/Helpers/GeneralHelper.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.VideoNodes.Helpers; 2 | 3 | /// 4 | /// General helper 5 | /// 6 | public class GeneralHelper 7 | { 8 | /// 9 | /// Checks if the input string represents a regular expression. 10 | /// 11 | /// The input string to check. 12 | /// True if the input is a regular expression, otherwise false. 13 | public static bool IsRegex(string input) 14 | { 15 | return new[] { "?", "|", "^", "$", "*" }.Any(ch => input.Contains(ch)); 16 | } 17 | } -------------------------------------------------------------------------------- /VideoNodes/Helpers/VaapiHelper.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.VideoNodes.Helpers; 2 | 3 | /// 4 | /// Helper for Vaapi 5 | /// 6 | class VaapiHelper 7 | { 8 | internal static bool VaapiLinux => OperatingSystem.IsLinux() && System.IO.File.Exists(VaapiRenderDevice); 9 | 10 | internal const string VaapiRenderDevice = "/dev/dri/renderD128"; 11 | } -------------------------------------------------------------------------------- /VideoNodes/LogicalNodes/VideoIsAV1.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.VideoNodes; 2 | 3 | /// 4 | /// Video is AV1 5 | /// 6 | public class VideoIsAV1 : VideoIsCodec 7 | { 8 | /// 9 | public override string HelpUrl => "https://fileflows.com/docs/plugins/video-nodes/logical-nodes/video-is-av1"; 10 | 11 | /// 12 | protected override bool CodecMatches(string codec) 13 | => codec.Contains("av1", StringComparison.InvariantCultureIgnoreCase); 14 | } -------------------------------------------------------------------------------- /VideoNodes/LogicalNodes/VideoIsH264.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.VideoNodes; 2 | 3 | /// 4 | /// Video is H264 5 | /// 6 | public class VideoIsH264 : VideoIsCodec 7 | { 8 | /// 9 | public override string HelpUrl => "https://fileflows.com/docs/plugins/video-nodes/logical-nodes/video-is-h264"; 10 | 11 | /// 12 | protected override bool CodecMatches(string codec) 13 | => codec.ToLowerInvariant() is "h264" or "h.264" or "264"; 14 | } -------------------------------------------------------------------------------- /VideoNodes/LogicalNodes/VideoIsHevc.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.VideoNodes; 2 | 3 | /// 4 | /// Video is HEVC 5 | /// 6 | public class VideoIsHevc : VideoIsCodec 7 | { 8 | /// 9 | public override string HelpUrl => "https://fileflows.com/docs/plugins/video-nodes/logical-nodes/video-is-hevc"; 10 | 11 | /// 12 | protected override bool CodecMatches(string codec) 13 | => codec.ToLowerInvariant() is "hevc" or "h265" or "265" or "h.265"; 14 | } -------------------------------------------------------------------------------- /VideoNodes/Plugin.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.VideoNodes; 2 | 3 | using System.ComponentModel.DataAnnotations; 4 | using FileFlows.Plugin.Attributes; 5 | 6 | public class Plugin : FileFlows.Plugin.IPlugin 7 | { 8 | /// 9 | public Guid Uid => new Guid("881b486b-4b38-4e66-b39e-fbc0fc9deee1"); 10 | /// 11 | public string Name => "Video"; 12 | /// 13 | public string MinimumVersion => "1.0.4.2019"; 14 | /// 15 | public string Icon => "svg:video"; 16 | 17 | /// 18 | public void Init() 19 | { 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /VideoNodes/StaticMethods.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.VideoNodes; 2 | 3 | /// 4 | /// Special class that exposes static methods to the script executor 5 | /// 6 | public class StaticMethods 7 | { 8 | /// 9 | /// Gets the video info for a file 10 | /// 11 | /// the args 12 | /// the name of the file to read 13 | /// the video info 14 | public static VideoInfo GetVideoInfo(NodeParameters args, string filename) 15 | => VideoInfoHelper.ReadStatic(args.Process, args.Logger, args.GetToolPath("FFMpeg"), filename).ValueOrDefault; 16 | } -------------------------------------------------------------------------------- /VideoNodes/Tests/EndCreditsTests.cs: -------------------------------------------------------------------------------- 1 | // #if(DEBUG) 2 | // 3 | // namespace VideoNodes.Tests; 4 | // 5 | // using FileFlows.VideoNodes; 6 | // using FileFlows.VideoNodes.FfmpegBuilderNodes; 7 | // using FileFlows.VideoNodes.VideoNodes; 8 | // using Microsoft.VisualStudio.TestTools.UnitTesting; 9 | // 10 | // [TestClass] 11 | // public class EndCreditsTests : TestBase 12 | // { 13 | // [TestMethod] 14 | // public void EndCredits_Base() 15 | // { 16 | // var logger = new TestLogger(); 17 | // string file = @"D:\videos\testfiles\pgs.mkv"; 18 | // var vi = new VideoInfoHelper(FfmpegPath, logger); 19 | // var vii = vi.Read(file); 20 | // 21 | // var args = new NodeParameters(file, logger, false, string.Empty, null); 22 | // args.GetToolPathActual = (string tool) => FfmpegPath; 23 | // args.TempPath = TempPath; 24 | // 25 | // var node = new DetectEndCredits(); 26 | // node.PreExecute(args); 27 | // var result = node.Execute(args); 28 | // var log = logger.ToString(); 29 | // Assert.AreEqual(1, result); 30 | // } 31 | // } 32 | // 33 | // #endif -------------------------------------------------------------------------------- /VideoNodes/Tests/Resources/audio.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revenz/FileFlowsPlugins/9a42dee278704c4d0e74a9e4cb546d5acdf2d28f/VideoNodes/Tests/Resources/audio.mp3 -------------------------------------------------------------------------------- /VideoNodes/Tests/Resources/corrupt.mkv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revenz/FileFlowsPlugins/9a42dee278704c4d0e74a9e4cb546d5acdf2d28f/VideoNodes/Tests/Resources/corrupt.mkv -------------------------------------------------------------------------------- /VideoNodes/Tests/Resources/eng_ger_audio.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revenz/FileFlowsPlugins/9a42dee278704c4d0e74a9e4cb546d5acdf2d28f/VideoNodes/Tests/Resources/eng_ger_audio.mp4 -------------------------------------------------------------------------------- /VideoNodes/Tests/Resources/hevc.mkv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revenz/FileFlowsPlugins/9a42dee278704c4d0e74a9e4cb546d5acdf2d28f/VideoNodes/Tests/Resources/hevc.mkv -------------------------------------------------------------------------------- /VideoNodes/Tests/Resources/subtitles.mkv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revenz/FileFlowsPlugins/9a42dee278704c4d0e74a9e4cb546d5acdf2d28f/VideoNodes/Tests/Resources/subtitles.mkv -------------------------------------------------------------------------------- /VideoNodes/Tests/Resources/video.mkv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revenz/FileFlowsPlugins/9a42dee278704c4d0e74a9e4cb546d5acdf2d28f/VideoNodes/Tests/Resources/video.mkv -------------------------------------------------------------------------------- /VideoNodes/Tests/Resources/video.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revenz/FileFlowsPlugins/9a42dee278704c4d0e74a9e4cb546d5acdf2d28f/VideoNodes/Tests/Resources/video.mp4 -------------------------------------------------------------------------------- /VideoNodes/Tests/Resources/video4by3.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revenz/FileFlowsPlugins/9a42dee278704c4d0e74a9e4cb546d5acdf2d28f/VideoNodes/Tests/Resources/video4by3.mp4 -------------------------------------------------------------------------------- /VideoNodes/Tests/Resources/video4by7.mkv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/revenz/FileFlowsPlugins/9a42dee278704c4d0e74a9e4cb546d5acdf2d28f/VideoNodes/Tests/Resources/video4by7.mkv -------------------------------------------------------------------------------- /VideoNodes/Tests/VideoHasErrorsTests.cs: -------------------------------------------------------------------------------- 1 | #if(DEBUG) 2 | 3 | using FileFlows.VideoNodes; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace VideoNodes.Tests; 7 | 8 | [TestClass] 9 | public class VideoHasErrorsTests : VideoTestBase 10 | { 11 | [TestMethod] 12 | public void VideoHasErrors_Video() 13 | { 14 | var args = GetVideoNodeParameters(VideoCorrupt); 15 | 16 | VideoFile vf = new(); 17 | vf.PreExecute(args); 18 | vf.Execute(args); 19 | 20 | VideoHasErrors element = new(); 21 | element.PreExecute(args); 22 | int output = element.Execute(args); 23 | 24 | Assert.AreEqual(1, output); 25 | } 26 | } 27 | 28 | 29 | #endif -------------------------------------------------------------------------------- /Web/ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Web; 2 | 3 | /// 4 | /// Extension methods 5 | /// 6 | internal static class ExtensionMethods 7 | { 8 | /// 9 | /// Returns an empty string as null, otherwise returns the original string 10 | /// 11 | /// the input string 12 | /// the string or null if empty 13 | public static string? EmptyAsNull(this string str) 14 | { 15 | return str == string.Empty ? null : str; 16 | } 17 | } -------------------------------------------------------------------------------- /Web/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Text; 3 | global using System.ComponentModel.DataAnnotations; 4 | global using FileFlows.Plugin; 5 | global using FileFlows.Plugin.Attributes; 6 | global using FileFlows.Common; -------------------------------------------------------------------------------- /Web/Plugin.cs: -------------------------------------------------------------------------------- 1 | namespace FileFlows.Web; 2 | 3 | /// 4 | /// Plugin Information 5 | /// 6 | public class Plugin : FileFlows.Plugin.IPlugin 7 | { 8 | /// 9 | public Guid Uid => new Guid("162b4ed0-da61-42de-85e1-576b9d7a2f82"); 10 | /// 11 | public string Name => "Web"; 12 | /// 13 | public string MinimumVersion => "24.8.1.3444"; 14 | /// 15 | public string Icon => "fas fa-globe"; 16 | 17 | /// 18 | public void Init() 19 | { 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Web/Tests/DownloadUrlTests.cs: -------------------------------------------------------------------------------- 1 | #if(DEBUG) 2 | 3 | using FileFlows.Web.FlowElements; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | using PluginTestLibrary; 6 | 7 | namespace FileFlows.Web.Tests; 8 | 9 | [TestClass] 10 | public class DownloadUrlTests : TestBase 11 | { 12 | [TestMethod] 13 | public void Basic() 14 | { 15 | var args = new NodeParameters("https://www.reddit.com/r/FileFlows/", Logger, false, string.Empty, new LocalFileService()); 16 | 17 | var element = new Downloader(); 18 | var result = element.Execute(args); 19 | Assert.AreEqual(1, result); 20 | } 21 | 22 | [TestMethod] 23 | public void InputFile() 24 | { 25 | var args = new NodeParameters("https://images.pexels.com/photos/45201/kitty-cat-kitten-pet-45201.jpeg", Logger, false, string.Empty, new LocalFileService()); 26 | 27 | var element = new InputUrl(); 28 | element.Download = true; 29 | var result = element.Execute(args); 30 | Assert.AreEqual(1, result); 31 | } 32 | } 33 | 34 | #endif -------------------------------------------------------------------------------- /plugins.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Name": "BasicNodes", 4 | "Version": "0.0.1.40", 5 | "Package": "https://github.com/revenz/FileFlowsPlugins/blob/master/Builds/BasicNodes.zip?raw=true" 6 | }, 7 | { 8 | "Name": "MetaNodes", 9 | "Version": "0.0.1.40", 10 | "Package": "https://github.com/revenz/FileFlowsPlugins/blob/master/Builds/MetaNodes.zip?raw=true" 11 | }, 12 | { 13 | "Name": "VideoNodes", 14 | "Version": "0.0.1.40", 15 | "Package": "https://github.com/revenz/FileFlowsPlugins/blob/master/Builds/VideoNodes.zip?raw=true" 16 | } 17 | ] 18 | --------------------------------------------------------------------------------