├── .eslintrc.json ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── auto_build.yml │ └── lint_and_test.yml ├── .gitignore ├── Community ├── Tdarr_Plugin_00td_action_add_audio_stream_codec.js ├── Tdarr_Plugin_00td_action_handbrake_basic_options.js ├── Tdarr_Plugin_00td_action_handbrake_ffmpeg_custom.js ├── Tdarr_Plugin_00td_action_keep_one_audio_stream.js ├── Tdarr_Plugin_00td_action_re_order_all_streams_v2.js ├── Tdarr_Plugin_00td_action_remove_audio_by_channel_count.js ├── Tdarr_Plugin_00td_action_remove_stream_by_specified_property.js ├── Tdarr_Plugin_00td_action_remux_container.js ├── Tdarr_Plugin_00td_action_standardise_audio_stream_codecs.js ├── Tdarr_Plugin_00td_action_transcode.js ├── Tdarr_Plugin_00td_filter_bit_depth.js ├── Tdarr_Plugin_00td_filter_break_stack_if_processed.js ├── Tdarr_Plugin_00td_filter_by_bitrate.js ├── Tdarr_Plugin_00td_filter_by_codec.js ├── Tdarr_Plugin_00td_filter_by_codec_tag_string.js ├── Tdarr_Plugin_00td_filter_by_file_property.js ├── Tdarr_Plugin_00td_filter_by_resolution.js ├── Tdarr_Plugin_00td_filter_by_size.js ├── Tdarr_Plugin_00td_filter_by_stream_tag.js ├── Tdarr_Plugin_075a_FFMPEG_HEVC_Generic.js ├── Tdarr_Plugin_075a_Transcode_Customisable.js ├── Tdarr_Plugin_075b_FFMPEG_HEVC_Generic_Video_Audio_Only.js ├── Tdarr_Plugin_075c_FFMPEG_HEVC_Generic_Video_Audio_Only_CRF20.js ├── Tdarr_Plugin_075d_FFMPEG_HEVC_GPU_Generic_Video_Audio_Only_CRF20.js ├── Tdarr_Plugin_076a_re_order_audio_streams.js ├── Tdarr_Plugin_076b_re_order_subtitle_streams.js ├── Tdarr_Plugin_077b_HandBrake_NVENC_264_Configurable.js ├── Tdarr_Plugin_078d_Output_embedded_subs_to_SRT_and_remove.js ├── Tdarr_Plugin_43az_add_to_radarr.js ├── Tdarr_Plugin_A47j_FFMPEG_NVENC_HEVC_Video_Only.js ├── Tdarr_Plugin_DOOM_NVENC_Tiered_MKV_CleanAll.js ├── Tdarr_Plugin_ER01_Transcode audio and video with HW (PC and Mac).js ├── Tdarr_Plugin_Greg_MP3_FFMPEG_CPU.js ├── Tdarr_Plugin_JB69_JBHEVCQSV_MinimalFile.js ├── Tdarr_Plugin_JB69_JBHEVCQSZ_PostFix.js ├── Tdarr_Plugin_MC93_Migz1FFMPEG.js ├── Tdarr_Plugin_MC93_Migz1FFMPEG_CPU.js ├── Tdarr_Plugin_MC93_Migz1Remux.js ├── Tdarr_Plugin_MC93_Migz2CleanTitle.js ├── Tdarr_Plugin_MC93_Migz3CleanAudio.js ├── Tdarr_Plugin_MC93_Migz4CleanSubs.js ├── Tdarr_Plugin_MC93_Migz5ConvertAudio.js ├── Tdarr_Plugin_MC93_Migz6OrderStreams.js ├── Tdarr_Plugin_MC93_MigzImageRemoval.js ├── Tdarr_Plugin_MC93_MigzPlex_Autoscan.js ├── Tdarr_Plugin_MP01_MichPasCleanSubsAndAudioCodecs.js ├── Tdarr_Plugin_Mthr_VaapiHEVCTranscode.js ├── Tdarr_Plugin_NIfPZuCLU_2_Pass_Loudnorm_Audio_Normalisation.js ├── Tdarr_Plugin_O8O0dCTlb_Set_File_Permissions_For_UnRaid.js ├── Tdarr_Plugin_SV6x_Smoove1FFMPEG_NVENC_H264.js ├── Tdarr_Plugin_TD01_TOAD_Autoscan.js ├── Tdarr_Plugin_VP92_VP9_Match_Bitrate_One_Pass.js ├── Tdarr_Plugin_a37x_Drawmonster_MP4_No_Title_Meta.js ├── Tdarr_Plugin_a8hc_HaveAGitGat_HandBrake_H264_VeryFast1080p30.js ├── Tdarr_Plugin_a9hc_HaveAGitGat_HandBrake_H264_Fast1080p30.js ├── Tdarr_Plugin_a9hd_FFMPEG_Transcode_Specific_Audio_Stream_Codecs.js ├── Tdarr_Plugin_a9he_New_file_size_check.js ├── Tdarr_Plugin_a9hf_New_file_duration_check.js ├── Tdarr_Plugin_b38x_Nosirus_h265_aac_no_meta.js ├── Tdarr_Plugin_b39x_the1poet_surround_sound_to_ac3.js ├── Tdarr_Plugin_bsh1_Boosh_FFMPEG_QSV_HEVC.js ├── Tdarr_Plugin_c0r1_SetDefaultAudioStream.js ├── Tdarr_Plugin_d5d3_iiDrakeii_FFMPEG_NVENC_Tiered_MKV.js ├── Tdarr_Plugin_d5d4_iiDrakeii_Not_A_Video_Mjpeg_Fix.js ├── Tdarr_Plugin_da11_Dallas_FFmpeg_Presets_H264_MP4.js ├── Tdarr_Plugin_drdd_standardise_all_in_one.js ├── Tdarr_Plugin_e3jc_Tharic_H.264_MKV_480p30_No_Subs_No_Title_Meta.js ├── Tdarr_Plugin_e3jd_Tharic_H.264_MKV_720p30_No_Subs_No_Title_Meta.js ├── Tdarr_Plugin_e3je_Tharic_H.264_MKV_1080p30_No_Subs_No_Title_Meta.js ├── Tdarr_Plugin_e5c3_CnT_Add_Subtitles.js ├── Tdarr_Plugin_e5c3_CnT_Keep_Preferred_Audio.js ├── Tdarr_Plugin_e5c3_CnT_Remove_Letterbox.js ├── Tdarr_Plugin_f4k1_aune_audio_to_flac.js ├── Tdarr_Plugin_fd5T_Sparticus_4K_AC3_No_Subs.js ├── Tdarr_Plugin_goof1_URL_Plex_Refresh.js ├── Tdarr_Plugin_henk_Add_Specific_Audio_Codec.js ├── Tdarr_Plugin_henk_Keep_Native_Lang_Plus_Eng.js ├── Tdarr_Plugin_hk75_Drawmonster_MP4_AAC_No_Subs_No_metaTitle.js ├── Tdarr_Plugin_hk76_GilbN_MP4_AAC_No_metaTitle.js ├── Tdarr_Plugin_jeons001_Downmix_to_stereo_and_apply_DRC.js ├── Tdarr_Plugin_lmg1_Reorder_Streams.js ├── Tdarr_Plugin_nc7x_Drawmonster_No_Title_Meta.js ├── Tdarr_Plugin_r002_rootuser_FFMPEG_HQ_HEVC_MKV_Animation.js ├── Tdarr_Plugin_raf4_Floorpie_FFmpeg_Tiered_HEVC_MKV.js ├── Tdarr_Plugin_rr01_drpeppershaker_extract_subs_to_SRT.js ├── Tdarr_Plugin_s710_nick_h265_nvenc_4K.js ├── Tdarr_Plugin_s7x8_winsome_h265.js ├── Tdarr_Plugin_s7x9_winsome_h265_10bit.js ├── Tdarr_Plugin_s7x9_winsome_h265_nvenc.js ├── Tdarr_Plugin_scha_rename_based_on_codec_schadi.js ├── Tdarr_Plugin_sdd3_Remove_Commentary_Tracks.js ├── Tdarr_Plugin_sdf5_Thierrrrry_Remove_Non_English_Audio.js ├── Tdarr_Plugin_tsld_filter_modified_date.js ├── Tdarr_Plugin_vdka_Remove_DataStreams.js ├── Tdarr_Plugin_vdka_Tiered_CPU_CRF_Based_Configurable.js ├── Tdarr_Plugin_vdka_Tiered_NVENC_CQV_BASED_CONFIGURABLE.js ├── Tdarr_Plugin_x7ab_Remove_Subs.js ├── Tdarr_Plugin_x7ac_Remove_Closed_Captions.js ├── Tdarr_Plugin_z0ab_TheRealShadoh_FFmpeg_Subs_H264_Medium.js ├── Tdarr_Plugin_z18s_rename_files_based_on_codec.js ├── Tdarr_Plugin_z18t_rename_files_based_on_codec_and_resolution.js ├── Tdarr_Plugin_z1ab_TheRealShadoh_FFmpeg_Subs_H264_Fast.js ├── Tdarr_Plugin_z2ab_TheRealShadoh_FFmpeg_Subs_H264_Slow.js ├── Tdarr_Plugin_z3ab_TheRealShadoh_FFmpeg_Subs_H264_VeryFast.js └── Tdarr_Plugin_z80t_keep_original_date.js ├── FlowPlugins ├── CommunityFlowPlugins │ ├── audio │ │ ├── checkAudioCodec │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── checkChannelCount │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ └── normalizeAudio │ │ │ └── 1.0.0 │ │ │ └── index.js │ ├── basic │ │ └── basicVideoOrAudio │ │ │ └── 1.0.0 │ │ │ └── index.js │ ├── classic │ │ ├── runClassicFilterPlugin │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ └── runClassicTranscodePlugin │ │ │ ├── 1.0.0 │ │ │ └── index.js │ │ │ └── 2.0.0 │ │ │ └── index.js │ ├── ffmpegCommand │ │ ├── ffmpegCommand10BitVideo │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── ffmpegCommandCropBlackBars │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── ffmpegCommandCustomArguments │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── ffmpegCommandEnsureAudioStream │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── ffmpegCommandExecute │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── ffmpegCommandHdrToSdr │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── ffmpegCommandNormalizeAudio │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── ffmpegCommandRemoveDataStreams │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── ffmpegCommandRemoveStreamByProperty │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── ffmpegCommandRemoveSubtitles │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── ffmpegCommandRorderStreams │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── ffmpegCommandSetContainer │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── ffmpegCommandSetVdeoFramerate │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── ffmpegCommandSetVdeoResolution │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── ffmpegCommandSetVideoBitrate │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── ffmpegCommandSetVideoEncoder │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ └── ffmpegCommandStart │ │ │ └── 1.0.0 │ │ │ └── index.js │ ├── file │ │ ├── checkFileExists │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── checkFileExtension │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── checkFileMedium │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── checkFileNameIncludes │ │ │ ├── 1.0.0 │ │ │ │ └── index.js │ │ │ └── 2.0.0 │ │ │ │ └── index.js │ │ ├── checkFileSize │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── compareFileDurationRatio │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── compareFileSize │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── compareFileSizeRatio │ │ │ ├── 1.0.0 │ │ │ │ └── index.js │ │ │ └── 2.0.0 │ │ │ │ └── index.js │ │ ├── compareFileSizeRatioLive │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── copyMoveFolderContent │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── copyToDirectory │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── copyToWorkDirectory │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── deleteFile │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── moveToDirectory │ │ │ ├── 1.0.0 │ │ │ │ └── index.js │ │ │ └── 2.0.0 │ │ │ │ └── index.js │ │ ├── moveToOriginalDirectory │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── renameFile │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── replaceOriginalFile │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── setOriginalFile │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ └── unpack │ │ │ └── 1.0.0 │ │ │ └── index.js │ ├── handbrake │ │ └── handbrakeCustomArguments │ │ │ ├── 1.0.0 │ │ │ └── index.js │ │ │ └── 2.0.0 │ │ │ └── index.js │ ├── input │ │ └── inputFile │ │ │ └── 1.0.0 │ │ │ └── index.js │ ├── tools │ │ ├── applyRadarrOrSonarrNamingPolicy │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── apprise │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── checkFlowVariable │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── checkNodeHardwareEncoder │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── clearCache │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── comment │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── customFunction │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── failFlow │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── goToFlow │ │ │ ├── 1.0.0 │ │ │ │ └── index.js │ │ │ └── 2.0.0 │ │ │ │ └── index.js │ │ ├── notifyRadarrOrSonarr │ │ │ ├── 1.0.0 │ │ │ │ └── index.js │ │ │ └── 2.0.0 │ │ │ │ └── index.js │ │ ├── onFlowError │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── pauseUnpauseAllNodes │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── processedAdd │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── processedCheck │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── removeFromTdarr │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── requireReview │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── resetFlowError │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── runCli │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── runMkvPropEdit │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── setFlowVariable │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── tagsRequeue │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── tagsWorkerType │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ ├── waitTimeout │ │ │ └── 1.0.0 │ │ │ │ └── index.js │ │ └── webRequest │ │ │ └── 1.0.0 │ │ │ └── index.js │ └── video │ │ ├── CheckVideoFramerate │ │ └── 1.0.0 │ │ │ └── index.js │ │ ├── check10Bit │ │ └── 1.0.0 │ │ │ └── index.js │ │ ├── checkHdr │ │ └── 1.0.0 │ │ │ └── index.js │ │ ├── checkOverallBitrate │ │ └── 1.0.0 │ │ │ └── index.js │ │ ├── checkVideoBitrate │ │ └── 1.0.0 │ │ │ └── index.js │ │ ├── checkVideoCodec │ │ └── 1.0.0 │ │ │ └── index.js │ │ ├── checkVideoResolution │ │ └── 1.0.0 │ │ │ └── index.js │ │ ├── checkVideoStreamsCount │ │ └── 1.0.0 │ │ │ └── index.js │ │ ├── runHealthCheck │ │ └── 1.0.0 │ │ │ └── index.js │ │ └── transcodeVideo │ │ └── 1.0.0 │ │ ├── index.js │ │ └── test.js ├── CommunityFlowTemplates │ ├── tutorials │ │ ├── chapter1.js │ │ ├── chapter2.js │ │ ├── chapter3.js │ │ ├── chapter4p1.js │ │ ├── chapter4p2.js │ │ ├── chapter4p3.js │ │ ├── chapter4p4.js │ │ ├── chapter5.js │ │ ├── chapter6.js │ │ ├── chapter7.js │ │ └── chapter8.js │ └── video │ │ ├── basicVideo.js │ │ ├── basicVideoMigz.js │ │ ├── cpuGpuWorkersWithClassicPlugins.js │ │ ├── libraryBasicVideoOrAudioSettings.js │ │ └── lowResolutionCopies.js └── FlowHelpers │ └── 1.0.0 │ ├── classicPlugins.js │ ├── cliParsers.js │ ├── cliUtils.js │ ├── fileMoveOrCopy.js │ ├── fileUtils.js │ ├── hardwareUtils.js │ ├── hardwareUtils.test.js │ ├── interfaces │ ├── flowUtils.js │ ├── interfaces.js │ └── synced │ │ ├── IFileObject.js │ │ └── jobInterface.js │ └── normJoinPath.js ├── FlowPluginsTs ├── CommunityFlowPlugins │ ├── audio │ │ ├── checkAudioCodec │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── checkChannelCount │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ └── normalizeAudio │ │ │ └── 1.0.0 │ │ │ └── index.ts │ ├── basic │ │ └── basicVideoOrAudio │ │ │ └── 1.0.0 │ │ │ └── index.ts │ ├── classic │ │ ├── runClassicFilterPlugin │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ └── runClassicTranscodePlugin │ │ │ ├── 1.0.0 │ │ │ └── index.ts │ │ │ └── 2.0.0 │ │ │ └── index.ts │ ├── ffmpegCommand │ │ ├── ffmpegCommand10BitVideo │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── ffmpegCommandCropBlackBars │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── ffmpegCommandCustomArguments │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── ffmpegCommandEnsureAudioStream │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── ffmpegCommandExecute │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── ffmpegCommandHdrToSdr │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── ffmpegCommandNormalizeAudio │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── ffmpegCommandRemoveDataStreams │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── ffmpegCommandRemoveStreamByProperty │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── ffmpegCommandRemoveSubtitles │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── ffmpegCommandRorderStreams │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── ffmpegCommandSetContainer │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── ffmpegCommandSetVdeoFramerate │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── ffmpegCommandSetVdeoResolution │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── ffmpegCommandSetVideoBitrate │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── ffmpegCommandSetVideoEncoder │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ └── ffmpegCommandStart │ │ │ └── 1.0.0 │ │ │ └── index.ts │ ├── file │ │ ├── checkFileExists │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── checkFileExtension │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── checkFileMedium │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── checkFileNameIncludes │ │ │ ├── 1.0.0 │ │ │ │ └── index.ts │ │ │ └── 2.0.0 │ │ │ │ └── index.ts │ │ ├── checkFileSize │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── compareFileDurationRatio │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── compareFileSize │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── compareFileSizeRatio │ │ │ ├── 1.0.0 │ │ │ │ └── index.ts │ │ │ └── 2.0.0 │ │ │ │ └── index.ts │ │ ├── compareFileSizeRatioLive │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── copyMoveFolderContent │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── copyToDirectory │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── copyToWorkDirectory │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── deleteFile │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── moveToDirectory │ │ │ ├── 1.0.0 │ │ │ │ └── index.ts │ │ │ └── 2.0.0 │ │ │ │ └── index.ts │ │ ├── moveToOriginalDirectory │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── renameFile │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── replaceOriginalFile │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── setOriginalFile │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ └── unpack │ │ │ └── 1.0.0 │ │ │ └── index.ts │ ├── handbrake │ │ └── handbrakeCustomArguments │ │ │ ├── 1.0.0 │ │ │ └── index.ts │ │ │ └── 2.0.0 │ │ │ └── index.ts │ ├── input │ │ └── inputFile │ │ │ └── 1.0.0 │ │ │ └── index.ts │ ├── tools │ │ ├── applyRadarrOrSonarrNamingPolicy │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── apprise │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── checkFlowVariable │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── checkNodeHardwareEncoder │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── clearCache │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── comment │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── customFunction │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── failFlow │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── goToFlow │ │ │ ├── 1.0.0 │ │ │ │ └── index.ts │ │ │ └── 2.0.0 │ │ │ │ └── index.ts │ │ ├── notifyRadarrOrSonarr │ │ │ ├── 1.0.0 │ │ │ │ └── index.ts │ │ │ └── 2.0.0 │ │ │ │ └── index.ts │ │ ├── onFlowError │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── pauseUnpauseAllNodes │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── processedAdd │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── processedCheck │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── removeFromTdarr │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── requireReview │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── resetFlowError │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── runCli │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── runMkvPropEdit │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── setFlowVariable │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── tagsRequeue │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── tagsWorkerType │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ ├── waitTimeout │ │ │ └── 1.0.0 │ │ │ │ └── index.ts │ │ └── webRequest │ │ │ └── 1.0.0 │ │ │ └── index.ts │ └── video │ │ ├── CheckVideoFramerate │ │ └── 1.0.0 │ │ │ └── index.ts │ │ ├── check10Bit │ │ └── 1.0.0 │ │ │ └── index.ts │ │ ├── checkHdr │ │ └── 1.0.0 │ │ │ └── index.ts │ │ ├── checkOverallBitrate │ │ └── 1.0.0 │ │ │ └── index.ts │ │ ├── checkVideoBitrate │ │ └── 1.0.0 │ │ │ └── index.ts │ │ ├── checkVideoCodec │ │ └── 1.0.0 │ │ │ └── index.ts │ │ ├── checkVideoResolution │ │ └── 1.0.0 │ │ │ └── index.ts │ │ ├── checkVideoStreamsCount │ │ └── 1.0.0 │ │ │ └── index.ts │ │ ├── runHealthCheck │ │ └── 1.0.0 │ │ │ └── index.ts │ │ └── transcodeVideo │ │ └── 1.0.0 │ │ ├── index.ts │ │ └── test.ts ├── CommunityFlowTemplates │ ├── tutorials │ │ ├── chapter1.tsx │ │ ├── chapter2.tsx │ │ ├── chapter3.tsx │ │ ├── chapter4p1.tsx │ │ ├── chapter4p2.tsx │ │ ├── chapter4p3.tsx │ │ ├── chapter4p4.tsx │ │ ├── chapter5.tsx │ │ ├── chapter6.tsx │ │ ├── chapter7.tsx │ │ └── chapter8.tsx │ └── video │ │ ├── basicVideo.ts │ │ ├── basicVideoMigz.ts │ │ ├── cpuGpuWorkersWithClassicPlugins.ts │ │ ├── libraryBasicVideoOrAudioSettings.ts │ │ └── lowResolutionCopies.ts └── FlowHelpers │ └── 1.0.0 │ ├── classicPlugins.ts │ ├── cliParsers.ts │ ├── cliUtils.ts │ ├── fileMoveOrCopy.ts │ ├── fileUtils.ts │ ├── hardwareUtils.test.ts │ ├── hardwareUtils.ts │ ├── interfaces │ ├── flowUtils.ts │ ├── interfaces.ts │ └── synced │ │ ├── IFileObject.ts │ │ └── jobInterface.ts │ └── normJoinPath.ts ├── LICENSE ├── README.md ├── examples ├── Tdarr_Plugin_a9he_New_file_size_check.js ├── Tdarr_Plugin_f001_Filter_Example.js ├── Tdarr_Plugin_f002_Filter_Example.js ├── Tdarr_Plugin_pos1_Post_Proc_Example.js ├── Tdarr_Plugin_pre1_Pre_Proc_Example.js └── force_transcode_filter_notes ├── methods ├── actions.js ├── filters.js ├── lib.js ├── library.js ├── library │ ├── actions │ │ ├── remuxContainer.js │ │ ├── transcodeAddAudioStream.js │ │ ├── transcodeKeepOneAudioStream.js │ │ └── transcodeStandardiseAudioCodecs.js │ └── filters │ │ ├── filterByAge.js │ │ ├── filterByBitrate.js │ │ ├── filterByCodec.js │ │ ├── filterByMedium.js │ │ ├── filterByResolution.js │ │ └── filterBySize.js ├── loadDefaultValues.js ├── node_modules │ ├── callsites │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── license │ │ ├── package.json │ │ └── readme.md │ ├── import-fresh │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── license │ │ ├── package.json │ │ └── readme.md │ ├── parent-module │ │ ├── index.js │ │ ├── license │ │ ├── package.json │ │ └── readme.md │ └── resolve-from │ │ ├── index.js │ │ ├── license │ │ ├── package.json │ │ └── readme.md ├── package-lock.json ├── package.json └── utils.js ├── package-lock.json ├── package.json ├── tests ├── Community │ ├── Tdarr_Plugin_00td_action_add_audio_stream_codec.js │ ├── Tdarr_Plugin_00td_action_handbrake_basic_options.js │ ├── Tdarr_Plugin_00td_action_handbrake_ffmpeg_custom.js │ ├── Tdarr_Plugin_00td_action_keep_one_audio_stream.js │ ├── Tdarr_Plugin_00td_action_re_order_all_streams_v2.js │ ├── Tdarr_Plugin_00td_action_remove_audio_by_channel_count.js │ ├── Tdarr_Plugin_00td_action_remove_stream_by_specified_property.js │ ├── Tdarr_Plugin_00td_action_remux_container.js │ ├── Tdarr_Plugin_00td_action_standardise_audio_stream_codecs.js │ ├── Tdarr_Plugin_00td_action_transcode.js │ ├── Tdarr_Plugin_00td_filter_bit_depth.js │ ├── Tdarr_Plugin_00td_filter_break_stack_if_processed.js │ ├── Tdarr_Plugin_00td_filter_by_bitrate.js │ ├── Tdarr_Plugin_00td_filter_by_codec.js │ ├── Tdarr_Plugin_00td_filter_by_codec_tag_string.js │ ├── Tdarr_Plugin_00td_filter_by_file_property.js │ ├── Tdarr_Plugin_00td_filter_by_resolution.js │ ├── Tdarr_Plugin_00td_filter_by_size.js │ ├── Tdarr_Plugin_00td_filter_by_stream_tag.js │ ├── Tdarr_Plugin_075a_FFMPEG_HEVC_Generic.js │ ├── Tdarr_Plugin_075a_Transcode_Customisable.js │ ├── Tdarr_Plugin_075b_FFMPEG_HEVC_Generic_Video_Audio_Only.js │ ├── Tdarr_Plugin_075c_FFMPEG_HEVC_Generic_Video_Audio_Only_CRF20.js │ ├── Tdarr_Plugin_075d_FFMPEG_HEVC_GPU_Generic_Video_Audio_Only_CRF20.js │ ├── Tdarr_Plugin_076a_re_order_audio_streams.js │ ├── Tdarr_Plugin_076b_re_order_subtitle_streams.js │ ├── Tdarr_Plugin_077b_HandBrake_NVENC_264_Configurable.js │ ├── Tdarr_Plugin_A47j_FFMPEG_NVENC_HEVC_Video_Only.js │ ├── Tdarr_Plugin_DOOM_NVENC_Tiered_MKV_CleanAll.js │ ├── Tdarr_Plugin_ER01_Transcode audio and video with HW (PC and Mac).js │ ├── Tdarr_Plugin_Greg_MP3_FFMPEG_CPU.js │ ├── Tdarr_Plugin_JB69_JBHEVCQSV_MinimalFile.js │ ├── Tdarr_Plugin_MC93_Migz1FFMPEG.js │ ├── Tdarr_Plugin_MC93_Migz1FFMPEG_CPU.js │ ├── Tdarr_Plugin_MC93_Migz1Remux.js │ ├── Tdarr_Plugin_MC93_Migz2CleanTitle.js │ ├── Tdarr_Plugin_MC93_Migz3CleanAudio.js │ ├── Tdarr_Plugin_MC93_Migz4CleanSubs.js │ ├── Tdarr_Plugin_MC93_Migz5ConvertAudio.js │ ├── Tdarr_Plugin_MC93_Migz6OrderStreams.js │ ├── Tdarr_Plugin_MC93_MigzImageRemoval.js │ ├── Tdarr_Plugin_MP01_MichPasCleanSubsAndAudioCodecs.js │ ├── Tdarr_Plugin_Mthr_VaapiHEVCTranscode.js │ ├── Tdarr_Plugin_SV6x_Smoove1FFMPEG_NVENC_H264.js │ ├── Tdarr_Plugin_VP92_VP9_Match_Bitrate_One_Pass.js │ ├── Tdarr_Plugin_a37x_Drawmonster_MP4_No_Title_Meta.js │ ├── Tdarr_Plugin_a8hc_HaveAGitGat_HandBrake_H264_VeryFast1080p30.js │ ├── Tdarr_Plugin_a9hc_HaveAGitGat_HandBrake_H264_Fast1080p30.js │ ├── Tdarr_Plugin_a9hd_FFMPEG_Transcode_Specific_Audio_Stream_Codecs.js │ ├── Tdarr_Plugin_a9he_New_file_size_check.js │ ├── Tdarr_Plugin_a9hf_New_file_duration_check.js │ ├── Tdarr_Plugin_b38x_Nosirus_h265_aac_no_meta.js │ ├── Tdarr_Plugin_b39x_the1poet_surround_sound_to_ac3.js │ ├── Tdarr_Plugin_bsh1_Boosh_FFMPEG_QSV_HEVC.js │ ├── Tdarr_Plugin_c0r1_SetDefaultAudioStream.js │ ├── Tdarr_Plugin_d5d3_iiDrakeii_FFMPEG_NVENC_Tiered_MKV.js │ ├── Tdarr_Plugin_d5d4_iiDrakeii_Not_A_Video_Mjpeg_Fix.js │ ├── Tdarr_Plugin_da11_Dallas_FFmpeg_Presets_H264_MP4.js │ ├── Tdarr_Plugin_drdd_standardise_all_in_one.js │ ├── Tdarr_Plugin_e3jc_Tharic_H.264_MKV_480p30_No_Subs_No_Title_Meta.js │ ├── Tdarr_Plugin_e3jd_Tharic_H.264_MKV_720p30_No_Subs_No_Title_Meta.js │ ├── Tdarr_Plugin_e3je_Tharic_H.264_MKV_1080p30_No_Subs_No_Title_Meta.js │ ├── Tdarr_Plugin_e5c3_CnT_Keep_Preferred_Audio.js │ ├── Tdarr_Plugin_f4k1_aune_audio_to_flac.js │ ├── Tdarr_Plugin_fd5T_Sparticus_4K_AC3_No_Subs.js │ ├── Tdarr_Plugin_henk_Add_Specific_Audio_Codec.js │ ├── Tdarr_Plugin_hk75_Drawmonster_MP4_AAC_No_Subs_No_metaTitle.js │ ├── Tdarr_Plugin_hk76_GilbN_MP4_AAC_No_metaTitle.js │ ├── Tdarr_Plugin_jeons001_Downmix_to_stereo_and_apply_DRC.js │ ├── Tdarr_Plugin_lmg1_Reorder_Streams.js │ ├── Tdarr_Plugin_nc7x_Drawmonster_No_Title_Meta.js │ ├── Tdarr_Plugin_r002_rootuser_FFMPEG_HQ_HEVC_MKV_Animation.js │ ├── Tdarr_Plugin_raf4_Floorpie_FFmpeg_Tiered_HEVC_MKV.js │ ├── Tdarr_Plugin_s710_nick_h265_nvenc_4K.js │ ├── Tdarr_Plugin_s7x8_winsome_h265.js │ ├── Tdarr_Plugin_s7x9_winsome_h265_10bit.js │ ├── Tdarr_Plugin_s7x9_winsome_h265_nvenc.js │ ├── Tdarr_Plugin_sdd3_Remove_Commentary_Tracks.js │ ├── Tdarr_Plugin_sdf5_Thierrrrry_Remove_Non_English_Audio.js │ ├── Tdarr_Plugin_tsld_filter_modified_date.js │ ├── Tdarr_Plugin_vdka_Remove_DataStreams.js │ ├── Tdarr_Plugin_vdka_Tiered_CPU_CRF_Based_Configurable.js │ ├── Tdarr_Plugin_vdka_Tiered_NVENC_CQV_BASED_CONFIGURABLE.js │ ├── Tdarr_Plugin_x7ab_Remove_Subs.js │ ├── Tdarr_Plugin_x7ac_Remove_Closed_Captions.js │ ├── Tdarr_Plugin_z0ab_TheRealShadoh_FFmpeg_Subs_H264_Medium.js │ ├── Tdarr_Plugin_z1ab_TheRealShadoh_FFmpeg_Subs_H264_Fast.js │ ├── Tdarr_Plugin_z2ab_TheRealShadoh_FFmpeg_Subs_H264_Slow.js │ └── Tdarr_Plugin_z3ab_TheRealShadoh_FFmpeg_Subs_H264_VeryFast.js ├── checkPlugins.js ├── helpers │ └── run.js ├── runTests.js └── sampleData │ └── media │ ├── sampleAAC_1.json │ ├── sampleH264_1.json │ ├── sampleH264_2.json │ ├── sampleH264_3.json │ ├── sampleH265_1.json │ └── sampleMP3_1.json ├── tsconfig.eslint.json └── tsconfig.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "settings": { 3 | "import/resolver": { 4 | "node": { 5 | "extensions": [ 6 | ".js", 7 | ".jsx", 8 | ".ts", 9 | ".tsx" 10 | ] 11 | } 12 | } 13 | }, 14 | "env": { 15 | "commonjs": true, 16 | "es6": true, 17 | "node": true 18 | }, 19 | "extends": [ 20 | "airbnb-base", 21 | "plugin:@typescript-eslint/eslint-recommended", 22 | "plugin:@typescript-eslint/recommended" 23 | ], 24 | "plugins": [ 25 | "prefer-arrow-functions" 26 | ], 27 | "globals": { 28 | "Atomics": "readonly", 29 | "SharedArrayBuffer": "readonly" 30 | }, 31 | "parserOptions": { 32 | "ecmaVersion": 2018, 33 | "project": [ 34 | "./tsconfig.eslint.json" 35 | ] 36 | }, 37 | "rules": { 38 | "func-style": [ 39 | "error", 40 | "expression" 41 | ], 42 | "prefer-arrow-functions/prefer-arrow-functions": [ 43 | "warn", 44 | { 45 | "classPropertiesAllowed": false, 46 | "disallowPrototype": false, 47 | "returnStyle": "unchanged", 48 | "singleReturnOnly": false 49 | } 50 | ], 51 | "require-await":["error"], 52 | "global-require": 0, 53 | "no-void": 0, 54 | "@typescript-eslint/no-floating-promises": [ 55 | "error" 56 | ], 57 | "@typescript-eslint/no-var-requires": 0, 58 | "class-methods-use-this": 0, 59 | "no-case-declarations": 0, 60 | "camelcase": 0, 61 | "jsx-a11y/click-events-have-key-events": 0, 62 | "no-underscore-dangle": [ 63 | "error", 64 | { 65 | "allow": [ 66 | "_id" 67 | ] 68 | } 69 | ], 70 | "max-len": [ 71 | "error", 72 | { 73 | "code": 120 74 | } 75 | ], 76 | "import/extensions": [ 77 | "error", 78 | "ignorePackages", 79 | { 80 | "js": "never", 81 | "jsx": "never", 82 | "ts": "never", 83 | "tsx": "never" 84 | } 85 | ] 86 | } 87 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/workflows/auto_build.yml: -------------------------------------------------------------------------------- 1 | name: Auto Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | test: 10 | strategy: 11 | matrix: 12 | node-version: [18.x] 13 | os: 14 | [ 15 | ["ubuntu-22.04"], 16 | ] 17 | runs-on: ${{ matrix.os }} 18 | 19 | steps: 20 | - name: Trigger 21 | run: "curl -XPOST -u \"${{ secrets.PAT_USERNAME}}:${{secrets.PAT_TOKEN}}\" -H \"Accept: application/vnd.github.everest-preview+json\" -H \"Content-Type: application/json\" https://api.github.com/repos/HaveAGitGat/tdarr_docs/actions/workflows/build_plug_docs.yml/dispatches --data '{\"ref\": \"main\"}'" -------------------------------------------------------------------------------- /.github/workflows/lint_and_test.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | 3 | on: 4 | pull_request: 5 | branches: ['**'] 6 | 7 | jobs: 8 | test: 9 | strategy: 10 | matrix: 11 | node-version: [18.x] 12 | os: 13 | [ 14 | ["ubuntu-22.04"], 15 | ["windows-2022"], 16 | ["macos-14"], 17 | ] 18 | runs-on: ${{ matrix.os }} 19 | 20 | steps: 21 | - name: Set git to use LF 22 | run: | 23 | git config --global core.autocrlf false 24 | git config --global core.eol lf 25 | 26 | - uses: actions/checkout@v3 27 | with: 28 | ref: ${{ github.event.pull_request.head.sha }} 29 | 30 | - name: Use Node.js ${{ matrix.node-version }} 31 | uses: actions/setup-node@v1 32 | with: 33 | node-version: ${{ matrix.node-version }} 34 | - run: npm i 35 | - run: npm i -g typescript && tsc 36 | - run: npm run checkPlugins 37 | - run: npm run lint 38 | - run: npm run test 39 | 40 | auto_compile_and_push: 41 | if: github.ref != 'refs/heads/master' 42 | needs: [ 43 | test 44 | ] 45 | runs-on: ubuntu-22.04 46 | 47 | permissions: 48 | # Give the default GITHUB_TOKEN write permission to commit and push the changed files back to the repository. 49 | contents: write 50 | 51 | steps: 52 | - uses: actions/checkout@v3 53 | with: 54 | ref: ${{ github.event.pull_request.head.sha }} 55 | 56 | - uses: actions/setup-node@v3 57 | with: 58 | node-version: '18.x' 59 | 60 | - run: npm i && npm i -g typescript && rm -rdf ./FlowPlugins && tsc -v && tsc 61 | 62 | - uses: stefanzweifel/git-auto-commit-action@v5 63 | ## fix this check 64 | if: github.event.pull_request.head.repo.full_name == github.repository 65 | with: 66 | commit_message: Apply auto-build changes 67 | 68 | - run: | 69 | (git diff --quiet HEAD -- 2>/dev/null && echo "No uncommitted changes" \ 70 | || (echo "Error - Uncommitted changes found." && git --no-pager diff HEAD && exit 1)) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | 3 | ### IntelliJ IDEA ### 4 | .idea 5 | *.iws 6 | *.iml 7 | *.ipr 8 | 9 | ### VS Code ### 10 | .vscode/ 11 | 12 | /Local 13 | /FlowPluginsTs/LocalFlowPlugins 14 | /FlowPlugins/LocalFlowPlugins -------------------------------------------------------------------------------- /Community/Tdarr_Plugin_00td_action_remux_container.js: -------------------------------------------------------------------------------- 1 | const details = () => ({ 2 | id: 'Tdarr_Plugin_00td_action_remux_container', 3 | Stage: 'Pre-processing', 4 | Name: 'Remux Container', 5 | Type: 'Video', 6 | Operation: 'Transcode', 7 | Description: ` 8 | This action has a built-in filter. Additional filters can be added.\n\n 9 | 10 | If not in the specified container, the file will be remuxed. 11 | `, 12 | Version: '1.00', 13 | Tags: 'action', 14 | Inputs: [ 15 | { 16 | name: 'container', 17 | type: 'string', 18 | defaultValue: 'mkv', 19 | inputUI: { 20 | type: 'text', 21 | }, 22 | tooltip: 23 | 'Enter the desired container', 24 | }, 25 | ], 26 | }); 27 | 28 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 29 | const plugin = (file, librarySettings, inputs, otherArguments) => { 30 | const lib = require('../methods/lib')(); 31 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 32 | inputs = lib.loadDefaultValues(inputs, details); 33 | const response = { 34 | processFile: false, 35 | preset: '', 36 | container: '', 37 | handBrakeMode: false, 38 | FFmpegMode: false, 39 | reQueueAfter: false, 40 | infoLog: '', 41 | }; 42 | 43 | const { container } = inputs; 44 | 45 | const remuxContainer = lib.actions.remuxContainer( 46 | file, 47 | container, 48 | ); 49 | 50 | response.preset = ', -map 0 -c copy'; 51 | response.container = `.${inputs.container}`; 52 | response.handbrakeMode = false; 53 | response.FFmpegMode = true; 54 | response.reQueueAfter = true; 55 | response.processFile = remuxContainer.processFile; 56 | response.infoLog += remuxContainer.note; 57 | return response; 58 | }; 59 | 60 | module.exports.details = details; 61 | module.exports.plugin = plugin; 62 | -------------------------------------------------------------------------------- /Community/Tdarr_Plugin_00td_filter_break_stack_if_processed.js: -------------------------------------------------------------------------------- 1 | const details = () => ({ 2 | id: 'Tdarr_Plugin_00td_filter_break_stack_if_processed', 3 | Stage: 'Pre-processing', 4 | Name: 'Filter - Break Out Of Plugin Stack If Processed', 5 | Type: 'Video', 6 | Operation: 'Filter', 7 | Description: `This plugin will break out of the plugin stack if the file has been processed 8 | (i.e. if a new file has been created). In general, place it before your transcode plugin 9 | (for example when trying to force h264 to h264 transcoding which is difficult to do with normal plugins)`, 10 | Version: '1.00', 11 | Tags: 'filter', 12 | Inputs: [], 13 | }); 14 | 15 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 16 | const plugin = (file, librarySettings, inputs, otherArguments) => { 17 | const lib = require('../methods/lib')(); 18 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 19 | inputs = lib.loadDefaultValues(inputs, details); 20 | const response = { 21 | processFile: true, 22 | infoLog: '', 23 | }; 24 | 25 | if (file.file !== otherArguments.originalLibraryFile.file) { 26 | response.processFile = false; 27 | response.infoLog = 'File has been processed, breaking out of plugin stack.'; 28 | } else { 29 | response.infoLog = 'File has not been processed yet. Continuing to next plugin.'; 30 | } 31 | 32 | return response; 33 | }; 34 | 35 | module.exports.details = details; 36 | module.exports.plugin = plugin; 37 | -------------------------------------------------------------------------------- /Community/Tdarr_Plugin_00td_filter_by_bitrate.js: -------------------------------------------------------------------------------- 1 | const details = () => ({ 2 | id: 'Tdarr_Plugin_00td_filter_by_bitrate', 3 | Stage: 'Pre-processing', 4 | Name: 'Filter By Bitrate', 5 | Type: 'Video', 6 | Operation: 'Filter', 7 | Description: 'Only allow files to be transcoded which are within the lower and upper bounds (Kb) \n\n', 8 | Version: '1.00', 9 | Tags: 'filter', 10 | Inputs: [ 11 | { 12 | name: 'upperBound', 13 | type: 'number', 14 | defaultValue: 10000, 15 | inputUI: { 16 | type: 'text', 17 | }, 18 | tooltip: 19 | 'Enter the upper bound bitrate in Kb for files which should be processed.', 20 | }, 21 | { 22 | name: 'lowerBound', 23 | type: 'number', 24 | defaultValue: 0, 25 | inputUI: { 26 | type: 'text', 27 | }, 28 | tooltip: 29 | 'Enter the lower bound bitrate in Kb for files which should be processed.', 30 | }, 31 | ], 32 | }); 33 | 34 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 35 | const plugin = (file, librarySettings, inputs, otherArguments) => { 36 | const lib = require('../methods/lib')(); 37 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 38 | inputs = lib.loadDefaultValues(inputs, details); 39 | const response = { 40 | processFile: false, 41 | infoLog: '', 42 | }; 43 | 44 | if ( 45 | file.bit_rate >= inputs.lowerBound * 1000 46 | && file.bit_rate <= inputs.upperBound * 1000 47 | ) { 48 | response.processFile = true; 49 | response.infoLog += '☑File bitrate is within filter limits. Moving to next plugin.'; 50 | } else { 51 | response.processFile = false; 52 | response.infoLog += '☒File bitrate is not within filter limits. Breaking out of plugin stack.\n'; 53 | return response; 54 | } 55 | return response; 56 | }; 57 | 58 | module.exports.details = details; 59 | module.exports.plugin = plugin; 60 | -------------------------------------------------------------------------------- /Community/Tdarr_Plugin_00td_filter_by_size.js: -------------------------------------------------------------------------------- 1 | const details = () => ({ 2 | id: 'Tdarr_Plugin_00td_filter_by_size', 3 | Stage: 'Pre-processing', 4 | Name: 'Filter By Size', 5 | Type: 'Video', 6 | Operation: 'Filter', 7 | Description: 'Only allow files to be transcoded which are within the lower and upper bounds (MB) \n\n', 8 | Version: '1.00', 9 | Tags: 'filter', 10 | Inputs: [ 11 | { 12 | name: 'upperBound', 13 | type: 'number', 14 | defaultValue: 100000, 15 | inputUI: { 16 | type: 'text', 17 | }, 18 | tooltip: 19 | 'Enter the upper bound size in MB for files which should be processed.' 20 | + ' Files above this size won\'t be processed.', 21 | }, 22 | { 23 | name: 'lowerBound', 24 | type: 'number', 25 | defaultValue: 0, 26 | inputUI: { 27 | type: 'text', 28 | }, 29 | tooltip: 30 | 'Enter the lower bound size in MB for files which should be processed.' 31 | + ' Files below this size won\'t be processed.', 32 | }, 33 | ], 34 | }); 35 | 36 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 37 | const plugin = (file, librarySettings, inputs, otherArguments) => { 38 | const lib = require('../methods/lib')(); 39 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 40 | inputs = lib.loadDefaultValues(inputs, details); 41 | const response = { 42 | processFile: false, 43 | infoLog: '', 44 | }; 45 | 46 | const fileSize = file.file_size; 47 | if (fileSize >= inputs.lowerBound && fileSize <= inputs.upperBound) { 48 | response.processFile = true; 49 | response.infoLog += 'File is within lower and upper bound size limits. Moving to next plugin.'; 50 | } else { 51 | response.infoLog += 'File is not within lower and upper bound size limits. Breaking out of plugin stack.'; 52 | } 53 | 54 | return response; 55 | }; 56 | 57 | module.exports.details = details; 58 | module.exports.plugin = plugin; 59 | -------------------------------------------------------------------------------- /Community/Tdarr_Plugin_075a_FFMPEG_HEVC_Generic.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | const details = () => ({ 3 | id: 'Tdarr_Plugin_075a_FFMPEG_HEVC_Generic', 4 | Stage: 'Pre-processing', 5 | Name: 'FFMPEG H265', 6 | Type: 'Video', 7 | Operation: 'Transcode', 8 | Description: '[Contains built-in filter] This plugin transcodes non h265 files into h265 mkv using default settings. Audio/subtitles not affected. \n\n', 9 | Version: '1.00', 10 | Tags: 'pre-processing,ffmpeg,h265,video only', 11 | Inputs: [], 12 | }); 13 | 14 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 15 | const plugin = (file, librarySettings, inputs, otherArguments) => { 16 | 17 | const lib = require('../methods/lib')(); 18 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 19 | inputs = lib.loadDefaultValues(inputs, details); 20 | // Must return this object 21 | 22 | const response = { 23 | processFile: false, 24 | preset: '', 25 | container: '.mp4', 26 | handBrakeMode: false, 27 | FFmpegMode: false, 28 | reQueueAfter: false, 29 | infoLog: '', 30 | }; 31 | 32 | if (file.fileMedium !== 'video') { 33 | response.processFile = false; 34 | response.infoLog += '☒File is not a video! \n'; 35 | return response; 36 | } 37 | response.infoLog += '☑File is a video! \n'; 38 | 39 | if (file.ffProbeData.streams[0].codec_name == 'hevc') { 40 | response.processFile = false; 41 | response.infoLog += '☑File is already in hevc! \n'; 42 | return response; 43 | } 44 | 45 | response.processFile = true; 46 | response.preset = ',-map 0:v -map 0:a -map 0:s? -map 0:d? -c copy -c:v:0 libx265 -max_muxing_queue_size 9999'; 47 | response.container = '.mkv'; 48 | response.handBrakeMode = false; 49 | response.FFmpegMode = true; 50 | response.reQueueAfter = true; 51 | response.infoLog += '☒File is not hevc! \n'; 52 | return response; 53 | }; 54 | 55 | module.exports.details = details; 56 | module.exports.plugin = plugin; 57 | -------------------------------------------------------------------------------- /Community/Tdarr_Plugin_O8O0dCTlb_Set_File_Permissions_For_UnRaid.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | 4 | // tdarrSkipTest 5 | const details = () => { 6 | return { 7 | id: "Tdarr_Plugin_O8O0dCTlb_Set_File_Permissions_For_UnRaid", 8 | Stage: 'Pre-processing', 9 | Name: "Set File Permissions For UnRaid", 10 | Type: "Video", 11 | Operation: "Transcode", 12 | Description: "Sets file permissions using chown nobody:users to prevent lock from root. Use at end of stack. ", 13 | Version: "", 14 | Tags: "post-processing", 15 | Inputs:[], 16 | } 17 | } 18 | 19 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 20 | const plugin = (file, librarySettings, inputs, otherArguments) => { 21 | 22 | const lib = require('../methods/lib')(); 23 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 24 | inputs = lib.loadDefaultValues(inputs, details); 25 | 26 | //Must return this object at some point 27 | var response = { 28 | processFile: false, 29 | preset: '', 30 | container: '.mkv', 31 | handBrakeMode: false, 32 | FFmpegMode: true, 33 | reQueueAfter: true, 34 | infoLog: '', 35 | 36 | } 37 | 38 | response.infoLog += "" 39 | 40 | if ((true) || file.forceProcessing === true) { 41 | 42 | require("child_process").execSync(`chown nobody:users "${file._id}"`) 43 | response.preset = '' 44 | response.container = '.mkv' 45 | response.handBrakeMode = false 46 | response.FFmpegMode = true 47 | response.reQueueAfter = true; 48 | response.processFile = false 49 | response.infoLog += "File permissions set \n" 50 | return response 51 | } 52 | } 53 | 54 | 55 | module.exports.details = details; 56 | module.exports.plugin = plugin; -------------------------------------------------------------------------------- /Community/Tdarr_Plugin_a37x_Drawmonster_MP4_No_Title_Meta.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | const details = () => { 3 | return { 4 | id: "Tdarr_Plugin_a37x_Drawmonster_MP4_No_Title_Meta", 5 | Stage: "Pre-processing", 6 | Name: "Drawmonster MP4 No Title Meta Data", 7 | Type: "Video", 8 | Operation: 'Transcode', 9 | Description: `[Contains built-in filter] This plugin removes metadata (if a title exists). The output container is mp4. \n\n`, 10 | Version: "1.00", 11 | Tags: "pre-processing,ffmpeg", 12 | Inputs:[], 13 | }; 14 | } 15 | 16 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 17 | const plugin = (file, librarySettings, inputs, otherArguments) => { 18 | 19 | const lib = require('../methods/lib')(); 20 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 21 | inputs = lib.loadDefaultValues(inputs, details); 22 | //Must return this object 23 | 24 | var response = { 25 | processFile: false, 26 | preset: "", 27 | container: ".mp4", 28 | handBrakeMode: false, 29 | FFmpegMode: false, 30 | reQueueAfter: false, 31 | infoLog: "", 32 | }; 33 | 34 | response.FFmpegMode = true; 35 | 36 | if (file.fileMedium !== "video") { 37 | console.log("File is not video"); 38 | 39 | response.infoLog += "☒File is not video \n"; 40 | response.processFile = false; 41 | 42 | return response; 43 | } else { 44 | var jsonString = JSON.stringify(file); 45 | 46 | if (file.meta.Title != undefined) { 47 | response.infoLog += "☒File has title metadata \n"; 48 | response.preset = ",-map_metadata -1 -map 0 -c copy"; 49 | response.reQueueAfter = true; 50 | response.processFile = true; 51 | return response; 52 | } else { 53 | response.infoLog += "☑File has no title metadata \n"; 54 | } 55 | 56 | response.infoLog += "☑File meets conditions! \n"; 57 | return response; 58 | } 59 | } 60 | 61 | module.exports.details = details; 62 | module.exports.plugin = plugin; 63 | -------------------------------------------------------------------------------- /Community/Tdarr_Plugin_lmg1_Reorder_Streams.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | const details = () => { 3 | return { 4 | id: "Tdarr_Plugin_lmg1_Reorder_Streams", 5 | Stage: "Pre-processing", 6 | Name: "Lmg1 Reorder Streams", 7 | Type: "Video", 8 | Operation: "Transcode", 9 | Description: `[Contains built-in filter] This plugin will move the video stream to the front so Tdarr will recognize the codec correctly.\n\n`, 10 | Version: "1.00", 11 | Tags: "pre-processing,ffmpeg", 12 | Inputs:[], 13 | }; 14 | } 15 | 16 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 17 | const plugin = (file, librarySettings, inputs, otherArguments) => { 18 | 19 | const lib = require('../methods/lib')(); 20 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 21 | inputs = lib.loadDefaultValues(inputs, details); 22 | //Must return this object 23 | 24 | var response = { 25 | processFile: false, 26 | preset: "", 27 | container: ".mp4", 28 | handBrakeMode: false, 29 | FFmpegMode: false, 30 | reQueueAfter: false, 31 | infoLog: "", 32 | }; 33 | 34 | if (file.fileMedium !== "video") { 35 | console.log("File is not video"); 36 | 37 | response.infoLog += " File is not video\n"; 38 | response.processFile = false; 39 | 40 | return response; 41 | } else { 42 | response.FFmpegMode = true; 43 | response.container = "." + file.container; 44 | 45 | if (file.ffProbeData.streams[0].codec_type != "video") { 46 | response.infoLog += "Video is not in the first stream"; 47 | response.preset = 48 | ",-map 0:v? -map 0:a? -map 0:s? -map 0:d? -map 0:t? -c copy"; 49 | response.reQueueAfter = true; 50 | response.processFile = true; 51 | 52 | return response; 53 | } else { 54 | response.infoLog += "File has video in first stream\n"; 55 | } 56 | 57 | response.infoLog += " File meets conditions!\n"; 58 | return response; 59 | } 60 | } 61 | 62 | module.exports.details = details; 63 | module.exports.plugin = plugin; 64 | -------------------------------------------------------------------------------- /Community/Tdarr_Plugin_nc7x_Drawmonster_No_Title_Meta.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | const details = () => { 3 | return { 4 | id: "Tdarr_Plugin_nc7x_Drawmonster_No_Title_Meta", 5 | Stage: "Pre-processing", 6 | Name: "Drawmonster No Title Meta Data", 7 | Type: "Video", 8 | Operation: "Transcode", 9 | Description: `[Contains built-in filter] This plugin removes metadata (if a title exists). The output container is the same as the original. \n\n 10 | `, 11 | Version: "1.00", 12 | Tags: "pre-processing,ffmpeg", 13 | Inputs:[] 14 | }; 15 | } 16 | 17 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 18 | const plugin = (file, librarySettings, inputs, otherArguments) => { 19 | 20 | const lib = require('../methods/lib')(); 21 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 22 | inputs = lib.loadDefaultValues(inputs, details); 23 | //Must return this object 24 | 25 | var response = { 26 | processFile: false, 27 | preset: "", 28 | container: ".mp4", 29 | handBrakeMode: false, 30 | FFmpegMode: false, 31 | reQueueAfter: false, 32 | infoLog: "", 33 | }; 34 | 35 | response.container = "." + file.container; 36 | response.FFmpegMode = true; 37 | 38 | if (file.fileMedium !== "video") { 39 | console.log("File is not video"); 40 | 41 | response.infoLog += "☒File is not video \n"; 42 | response.processFile = false; 43 | 44 | return response; 45 | } else { 46 | // var jsonString = JSON.stringify(file) 47 | 48 | if (file.meta.Title != undefined) { 49 | response.infoLog += "☒File has title metadata \n"; 50 | response.preset = ",-map_metadata -1 -map 0 -c copy"; 51 | response.reQueueAfter = true; 52 | response.processFile = true; 53 | return response; 54 | } else { 55 | response.infoLog += "☑File has no title metadata \n"; 56 | } 57 | 58 | response.infoLog += "☑File meets conditions! \n"; 59 | return response; 60 | } 61 | } 62 | 63 | module.exports.details = details; 64 | module.exports.plugin = plugin; 65 | -------------------------------------------------------------------------------- /Community/Tdarr_Plugin_tsld_filter_modified_date.js: -------------------------------------------------------------------------------- 1 | const details = () => ({ 2 | id: 'Tdarr_Plugin_tsld_filter_modified_date', 3 | Stage: 'Pre-processing', 4 | Name: 'Filter Modified Date', 5 | Type: 'Video', 6 | Operation: 'Filter', 7 | Description: 'This plugin prevents processing files older than 30 days \n\n', 8 | Version: '1.00', 9 | Tags: '', 10 | Inputs: [ 11 | // (Optional) Inputs you'd like the user to enter to allow your plugin to be easily configurable from the UI 12 | { 13 | name: 'minModifiedDaysOld', 14 | type: 'number', 15 | defaultValue: 30, 16 | inputUI: { 17 | type: 'text', 18 | }, 19 | tooltip: `Enter minimum number of days since modified since now file must be. 20 | 21 | \\nExample:\\n 22 | 365 23 | 24 | \\nExample:\\n 25 | 30`, 26 | }, 27 | ], 28 | }); 29 | 30 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 31 | const plugin = (file, librarySettings, inputs, otherArguments) => { 32 | const lib = require('../methods/lib')(); 33 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 34 | inputs = lib.loadDefaultValues(inputs, details); 35 | const response = { 36 | processFile: true, 37 | infoLog: '', 38 | }; 39 | // response.infoLog += `Filter preventing processing. File mod time ${file.statSync.mtimeMs}`; 40 | // response.infoLog += ` Now ${Date.now()}`; 41 | const age = Date.now() - file.statSync.mtimeMs; 42 | const reqage = Number(inputs.minModifiedDaysOld) * 86400000; 43 | // response.infoLog += ` Age ${age} Require Min Age: ${reqage}`; 44 | if (reqage < age) { 45 | response.infoLog += 'File modified date old enough. Moving to next plugin.'; 46 | response.processFile = true; 47 | } else { 48 | response.infoLog += 'Skipping, file modified date not old enough'; 49 | response.processFile = false; 50 | } 51 | 52 | return response; 53 | }; 54 | 55 | module.exports.details = details; 56 | module.exports.plugin = plugin; 57 | -------------------------------------------------------------------------------- /Community/Tdarr_Plugin_x7ac_Remove_Closed_Captions.js: -------------------------------------------------------------------------------- 1 | const details = () => ({ 2 | id: 'Tdarr_Plugin_x7ac_Remove_Closed_Captions', 3 | Stage: 'Pre-processing', 4 | Name: 'Remove Burned Closed Captions', 5 | Type: 'Video', 6 | Operation: 'Transcode', 7 | Description: 8 | '[Contains built-in filter] If detected, closed captions (XDS,608,708) will be removed from streams.', 9 | Version: '1.01', 10 | Tags: 'pre-processing,ffmpeg,subtitle only', 11 | Inputs: [], 12 | }); 13 | 14 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 15 | const plugin = (file, librarySettings, inputs, otherArguments) => { 16 | const lib = require('../methods/lib')(); 17 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 18 | inputs = lib.loadDefaultValues(inputs, details); 19 | const response = { 20 | processFile: false, 21 | // eslint-disable-next-line no-useless-escape 22 | preset: ',-map 0 -codec copy -bsf:v \"filter_units=remove_types=6\"', 23 | container: `.${file.container}`, 24 | handBrakeMode: false, 25 | FFmpegMode: true, 26 | reQueueAfter: true, 27 | infoLog: '', 28 | }; 29 | if (file.fileMedium !== 'video') { 30 | response.infoLog += '☒File is not video \n'; 31 | return response; 32 | } 33 | // Check if Closed Captions are set at file level 34 | if (file.hasClosedCaptions) { 35 | response.processFile = true; 36 | response.infoLog += '☒This file has closed captions \n'; 37 | return response; 38 | } 39 | // If not, check for Closed Captions in the streams 40 | const { streams } = file.ffProbeData; 41 | streams.forEach((stream) => { 42 | if (stream.closed_captions) { 43 | response.processFile = true; 44 | } 45 | }); 46 | 47 | response.infoLog += response.processFile ? '☒This file has burnt closed captions \n' 48 | : '☑Closed captions have not been detected on this file \n'; 49 | return response; 50 | }; 51 | module.exports.details = details; 52 | module.exports.plugin = plugin; 53 | -------------------------------------------------------------------------------- /FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandCropBlackBars/1.0.0/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.plugin = exports.details = void 0; 4 | var flowUtils_1 = require("../../../../FlowHelpers/1.0.0/interfaces/flowUtils"); 5 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 6 | var details = function () { return ({ 7 | name: 'Crop Black Bars', 8 | description: 'Crop Black Bars', 9 | style: { 10 | borderColor: '#6efefc', 11 | opacity: 0.5, 12 | }, 13 | tags: 'video', 14 | isStartPlugin: false, 15 | pType: '', 16 | requiresVersion: '2.11.01', 17 | sidebarPosition: -1, 18 | icon: '', 19 | inputs: [], 20 | outputs: [ 21 | { 22 | number: 1, 23 | tooltip: 'Continue to next plugin', 24 | }, 25 | ], 26 | }); }; 27 | exports.details = details; 28 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 29 | var plugin = function (args) { 30 | var lib = require('../../../../../methods/lib')(); 31 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 32 | args.inputs = lib.loadDefaultValues(args.inputs, details); 33 | (0, flowUtils_1.checkFfmpegCommandInit)(args); 34 | return { 35 | outputFileObj: args.inputFileObj, 36 | outputNumber: 1, 37 | variables: args.variables, 38 | }; 39 | }; 40 | exports.plugin = plugin; 41 | -------------------------------------------------------------------------------- /FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandHdrToSdr/1.0.0/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.plugin = exports.details = void 0; 4 | var flowUtils_1 = require("../../../../FlowHelpers/1.0.0/interfaces/flowUtils"); 5 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 6 | var details = function () { return ({ 7 | name: 'HDR to SDR', 8 | description: 'Convert HDR to SDR', 9 | style: { 10 | borderColor: '#6efefc', 11 | }, 12 | tags: 'video', 13 | isStartPlugin: false, 14 | pType: '', 15 | requiresVersion: '2.11.01', 16 | sidebarPosition: -1, 17 | icon: '', 18 | inputs: [], 19 | outputs: [ 20 | { 21 | number: 1, 22 | tooltip: 'Continue to next plugin', 23 | }, 24 | ], 25 | }); }; 26 | exports.details = details; 27 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 28 | var plugin = function (args) { 29 | var lib = require('../../../../../methods/lib')(); 30 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 31 | args.inputs = lib.loadDefaultValues(args.inputs, details); 32 | (0, flowUtils_1.checkFfmpegCommandInit)(args); 33 | args.variables.ffmpegCommand.streams.forEach(function (stream) { 34 | if (stream.codec_type === 'video') { 35 | stream.outputArgs.push('-vf', 'zscale=t=linear:npl=100,format=yuv420p'); 36 | } 37 | }); 38 | return { 39 | outputFileObj: args.inputFileObj, 40 | outputNumber: 1, 41 | variables: args.variables, 42 | }; 43 | }; 44 | exports.plugin = plugin; 45 | -------------------------------------------------------------------------------- /FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandNormalizeAudio/1.0.0/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.plugin = exports.details = void 0; 4 | var flowUtils_1 = require("../../../../FlowHelpers/1.0.0/interfaces/flowUtils"); 5 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 6 | var details = function () { return ({ 7 | name: 'Normalize Audio', 8 | description: 'Normalize Audio', 9 | style: { 10 | borderColor: '#6efefc', 11 | opacity: 0.5, 12 | }, 13 | tags: 'video', 14 | isStartPlugin: false, 15 | pType: '', 16 | requiresVersion: '2.11.01', 17 | sidebarPosition: -1, 18 | icon: '', 19 | inputs: [], 20 | outputs: [ 21 | { 22 | number: 1, 23 | tooltip: 'Continue to next plugin', 24 | }, 25 | ], 26 | }); }; 27 | exports.details = details; 28 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 29 | var plugin = function (args) { 30 | var lib = require('../../../../../methods/lib')(); 31 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 32 | args.inputs = lib.loadDefaultValues(args.inputs, details); 33 | (0, flowUtils_1.checkFfmpegCommandInit)(args); 34 | return { 35 | outputFileObj: args.inputFileObj, 36 | outputNumber: 1, 37 | variables: args.variables, 38 | }; 39 | }; 40 | exports.plugin = plugin; 41 | -------------------------------------------------------------------------------- /FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveDataStreams/1.0.0/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 3 | Object.defineProperty(exports, "__esModule", { value: true }); 4 | exports.plugin = exports.details = void 0; 5 | var flowUtils_1 = require("../../../../FlowHelpers/1.0.0/interfaces/flowUtils"); 6 | /* eslint-disable no-param-reassign */ 7 | var details = function () { return ({ 8 | name: 'Remove Data Streams', 9 | description: 'Remove Data Streams ', 10 | style: { 11 | borderColor: '#6efefc', 12 | }, 13 | tags: 'video', 14 | isStartPlugin: false, 15 | pType: '', 16 | requiresVersion: '2.11.01', 17 | sidebarPosition: -1, 18 | icon: '', 19 | inputs: [], 20 | outputs: [ 21 | { 22 | number: 1, 23 | tooltip: 'Continue to next plugin', 24 | }, 25 | ], 26 | }); }; 27 | exports.details = details; 28 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 29 | var plugin = function (args) { 30 | var lib = require('../../../../../methods/lib')(); 31 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 32 | args.inputs = lib.loadDefaultValues(args.inputs, details); 33 | (0, flowUtils_1.checkFfmpegCommandInit)(args); 34 | args.variables.ffmpegCommand.streams.forEach(function (stream) { 35 | if (stream.codec_type === 'data') { 36 | stream.removed = true; 37 | } 38 | }); 39 | return { 40 | outputFileObj: args.inputFileObj, 41 | outputNumber: 1, 42 | variables: args.variables, 43 | }; 44 | }; 45 | exports.plugin = plugin; 46 | -------------------------------------------------------------------------------- /FlowPlugins/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveSubtitles/1.0.0/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 3 | Object.defineProperty(exports, "__esModule", { value: true }); 4 | exports.plugin = exports.details = void 0; 5 | var flowUtils_1 = require("../../../../FlowHelpers/1.0.0/interfaces/flowUtils"); 6 | /* eslint-disable no-param-reassign */ 7 | var details = function () { return ({ 8 | name: 'Remove Subtitles', 9 | description: 'Remove subtitles from the file', 10 | style: { 11 | borderColor: '#6efefc', 12 | }, 13 | tags: 'video', 14 | isStartPlugin: false, 15 | pType: '', 16 | requiresVersion: '2.11.01', 17 | sidebarPosition: -1, 18 | icon: '', 19 | inputs: [], 20 | outputs: [ 21 | { 22 | number: 1, 23 | tooltip: 'Continue to next plugin', 24 | }, 25 | ], 26 | }); }; 27 | exports.details = details; 28 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 29 | var plugin = function (args) { 30 | var lib = require('../../../../../methods/lib')(); 31 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 32 | args.inputs = lib.loadDefaultValues(args.inputs, details); 33 | (0, flowUtils_1.checkFfmpegCommandInit)(args); 34 | args.variables.ffmpegCommand.streams.forEach(function (stream) { 35 | if (stream.codec_type === 'subtitle') { 36 | stream.removed = true; 37 | } 38 | }); 39 | return { 40 | outputFileObj: args.inputFileObj, 41 | outputNumber: 1, 42 | variables: args.variables, 43 | }; 44 | }; 45 | exports.plugin = plugin; 46 | -------------------------------------------------------------------------------- /FlowPlugins/CommunityFlowPlugins/file/checkFileMedium/1.0.0/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.plugin = exports.details = void 0; 4 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 5 | var details = function () { return ({ 6 | name: 'Check File Medium', 7 | description: 'Check if file is video, audio or other type of file', 8 | style: { 9 | borderColor: 'orange', 10 | }, 11 | tags: '', 12 | isStartPlugin: false, 13 | pType: '', 14 | requiresVersion: '2.11.01', 15 | sidebarPosition: -1, 16 | icon: 'faQuestion', 17 | inputs: [], 18 | outputs: [ 19 | { 20 | number: 1, 21 | tooltip: 'File medium is a Video', 22 | }, 23 | { 24 | number: 2, 25 | tooltip: 'File medium is an Audio', 26 | }, 27 | { 28 | number: 3, 29 | tooltip: 'File medium is Other', 30 | }, 31 | ], 32 | }); }; 33 | exports.details = details; 34 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 35 | var plugin = function (args) { 36 | var lib = require('../../../../../methods/lib')(); 37 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 38 | args.inputs = lib.loadDefaultValues(args.inputs, details); 39 | var outputNumber = 1; 40 | switch (args.inputFileObj.fileMedium) { 41 | case 'video': 42 | outputNumber = 1; 43 | break; 44 | case 'audio': 45 | outputNumber = 2; 46 | break; 47 | case 'other': 48 | outputNumber = 3; 49 | break; 50 | default: 51 | throw new Error('File has no fileMedium!'); 52 | } 53 | return { 54 | outputFileObj: args.inputFileObj, 55 | outputNumber: outputNumber, 56 | variables: args.variables, 57 | }; 58 | }; 59 | exports.plugin = plugin; 60 | -------------------------------------------------------------------------------- /FlowPlugins/CommunityFlowPlugins/file/moveToDirectory/1.0.0/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.plugin = exports.details = void 0; 4 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 5 | var details = function () { return ({ 6 | name: 'Move To Directory', 7 | description: 'Move working file to directory.', 8 | style: { 9 | borderColor: 'green', 10 | opacity: 0.5, 11 | }, 12 | tags: '', 13 | isStartPlugin: false, 14 | pType: '', 15 | requiresVersion: '2.11.01', 16 | sidebarPosition: -1, 17 | icon: '', 18 | inputs: [], 19 | outputs: [ 20 | { 21 | number: 1, 22 | tooltip: 'Continue to next plugin', 23 | }, 24 | ], 25 | }); }; 26 | exports.details = details; 27 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 28 | var plugin = function (args) { 29 | var lib = require('../../../../../methods/lib')(); 30 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 31 | args.inputs = lib.loadDefaultValues(args.inputs, details); 32 | return { 33 | outputFileObj: args.inputFileObj, 34 | outputNumber: 1, 35 | variables: args.variables, 36 | }; 37 | }; 38 | exports.plugin = plugin; 39 | -------------------------------------------------------------------------------- /FlowPlugins/CommunityFlowPlugins/file/setOriginalFile/1.0.0/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.plugin = exports.details = void 0; 4 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 5 | var details = function () { return ({ 6 | name: 'Set Original File', 7 | description: 'Set the working file to the original file path at the very start of the flow.', 8 | style: { 9 | borderColor: 'green', 10 | }, 11 | tags: '', 12 | isStartPlugin: false, 13 | pType: '', 14 | requiresVersion: '2.11.01', 15 | sidebarPosition: -1, 16 | icon: '', 17 | inputs: [], 18 | outputs: [ 19 | { 20 | number: 1, 21 | tooltip: 'Continue to next plugin', 22 | }, 23 | ], 24 | }); }; 25 | exports.details = details; 26 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 27 | var plugin = function (args) { 28 | var lib = require('../../../../../methods/lib')(); 29 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 30 | args.inputs = lib.loadDefaultValues(args.inputs, details); 31 | return { 32 | outputFileObj: { 33 | _id: args.originalLibraryFile._id, 34 | }, 35 | outputNumber: 1, 36 | variables: args.variables, 37 | }; 38 | }; 39 | exports.plugin = plugin; 40 | -------------------------------------------------------------------------------- /FlowPlugins/CommunityFlowPlugins/file/unpack/1.0.0/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.plugin = exports.details = void 0; 4 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 5 | var details = function () { return ({ 6 | name: 'Unpack File', 7 | description: 'Unpack a file', 8 | style: { 9 | borderColor: 'green', 10 | opacity: 0.5, 11 | }, 12 | tags: '', 13 | isStartPlugin: false, 14 | pType: '', 15 | requiresVersion: '2.11.01', 16 | sidebarPosition: -1, 17 | icon: 'faArrowRight', 18 | inputs: [], 19 | outputs: [ 20 | { 21 | number: 1, 22 | tooltip: 'Continue to next plugin', 23 | }, 24 | ], 25 | }); }; 26 | exports.details = details; 27 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 28 | var plugin = function (args) { 29 | var lib = require('../../../../../methods/lib')(); 30 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 31 | args.inputs = lib.loadDefaultValues(args.inputs, details); 32 | return { 33 | outputFileObj: args.inputFileObj, 34 | outputNumber: 1, 35 | variables: args.variables, 36 | }; 37 | }; 38 | exports.plugin = plugin; 39 | -------------------------------------------------------------------------------- /FlowPlugins/CommunityFlowPlugins/tools/comment/1.0.0/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.plugin = exports.details = void 0; 4 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 5 | var details = function () { return ({ 6 | name: 'Comment', 7 | nameUI: { 8 | type: 'textarea', 9 | style: { 10 | height: '250px', 11 | }, 12 | }, 13 | description: "Add a comment to your flow. Can place anywhere and link together.\n Any file input into the comment will be passed straight through.", 14 | style: { 15 | borderColor: 'white', 16 | borderRadius: '10px', 17 | backgroundColor: '#043775', 18 | }, 19 | tags: '', 20 | isStartPlugin: false, 21 | pType: '', 22 | requiresVersion: '2.11.01', 23 | sidebarPosition: -1, 24 | icon: 'faComment', 25 | inputs: [], 26 | outputs: [ 27 | { 28 | number: 1, 29 | tooltip: 'Continue to next plugin', 30 | }, 31 | ], 32 | }); }; 33 | exports.details = details; 34 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 35 | var plugin = function (args) { 36 | var lib = require('../../../../../methods/lib')(); 37 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 38 | args.inputs = lib.loadDefaultValues(args.inputs, details); 39 | return { 40 | outputFileObj: args.inputFileObj, 41 | outputNumber: 1, 42 | variables: args.variables, 43 | }; 44 | }; 45 | exports.plugin = plugin; 46 | -------------------------------------------------------------------------------- /FlowPlugins/CommunityFlowPlugins/tools/failFlow/1.0.0/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.plugin = exports.details = void 0; 4 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 5 | var details = function () { return ({ 6 | name: 'Fail Flow', 7 | description: 'Force the flow to fail and be move to the error table', 8 | style: { 9 | borderColor: 'red', 10 | }, 11 | tags: '', 12 | isStartPlugin: false, 13 | pType: '', 14 | requiresVersion: '2.11.01', 15 | sidebarPosition: -1, 16 | icon: 'faExclamationTriangle', 17 | inputs: [], 18 | outputs: [], 19 | }); }; 20 | exports.details = details; 21 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 22 | var plugin = function (args) { 23 | var lib = require('../../../../../methods/lib')(); 24 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 25 | args.inputs = lib.loadDefaultValues(args.inputs, details); 26 | throw new Error('Forcing flow to fail!'); 27 | return { 28 | outputFileObj: args.inputFileObj, 29 | outputNumber: 1, 30 | variables: args.variables, 31 | }; 32 | }; 33 | exports.plugin = plugin; 34 | -------------------------------------------------------------------------------- /FlowPlugins/CommunityFlowPlugins/tools/goToFlow/1.0.0/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.plugin = exports.details = void 0; 4 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 5 | var details = function () { return ({ 6 | name: 'Go To Flow', 7 | description: 'Go to a different flow', 8 | style: { 9 | borderColor: 'green', 10 | }, 11 | tags: '', 12 | isStartPlugin: false, 13 | pType: '', 14 | requiresVersion: '2.11.01', 15 | sidebarPosition: -1, 16 | icon: 'faArrowRight', 17 | inputs: [ 18 | { 19 | label: 'Flow ID', 20 | name: 'flowId', 21 | type: 'string', 22 | defaultValue: '', 23 | inputUI: { 24 | type: 'dropdown', 25 | options: [], 26 | }, 27 | tooltip: 'Specify flow ID to go to', 28 | }, 29 | ], 30 | outputs: [], 31 | }); }; 32 | exports.details = details; 33 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 34 | var plugin = function (args) { 35 | var lib = require('../../../../../methods/lib')(); 36 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 37 | args.inputs = lib.loadDefaultValues(args.inputs, details); 38 | return { 39 | outputFileObj: args.inputFileObj, 40 | outputNumber: 1, 41 | variables: args.variables, 42 | }; 43 | }; 44 | exports.plugin = plugin; 45 | -------------------------------------------------------------------------------- /FlowPlugins/CommunityFlowPlugins/tools/goToFlow/2.0.0/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.plugin = exports.details = void 0; 4 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 5 | var details = function () { return ({ 6 | name: 'Go To Flow', 7 | description: 'Go to a different flow', 8 | style: { 9 | borderColor: 'green', 10 | }, 11 | tags: '', 12 | isStartPlugin: false, 13 | pType: '', 14 | requiresVersion: '2.14.01', 15 | sidebarPosition: -1, 16 | icon: 'faArrowRight', 17 | inputs: [ 18 | { 19 | label: 'Flow ID', 20 | name: 'flowId', 21 | type: 'string', 22 | defaultValue: '', 23 | inputUI: { 24 | type: 'dropdown', 25 | options: [], 26 | }, 27 | tooltip: 'Specify flow ID to go to', 28 | }, 29 | { 30 | label: 'Plugin ID', 31 | name: 'pluginId', 32 | type: 'string', 33 | defaultValue: 'start', 34 | inputUI: { 35 | type: 'text', 36 | }, 37 | tooltip: 'Specify plugin ID to go to', 38 | }, 39 | ], 40 | outputs: [], 41 | }); }; 42 | exports.details = details; 43 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 44 | var plugin = function (args) { 45 | var lib = require('../../../../../methods/lib')(); 46 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 47 | args.inputs = lib.loadDefaultValues(args.inputs, details); 48 | return { 49 | outputFileObj: args.inputFileObj, 50 | outputNumber: 1, 51 | variables: args.variables, 52 | }; 53 | }; 54 | exports.plugin = plugin; 55 | -------------------------------------------------------------------------------- /FlowPlugins/CommunityFlowPlugins/tools/onFlowError/1.0.0/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.plugin = exports.details = void 0; 4 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 5 | var details = function () { return ({ 6 | name: 'On Flow Error', 7 | description: "Runs if an error occurs in this specific flow. \n Won't run if error occurs in after going to a different flow (unless that flow comes back to this one).", 8 | style: { 9 | borderColor: 'red', 10 | }, 11 | tags: '', 12 | isStartPlugin: false, 13 | pType: 'onFlowError', 14 | requiresVersion: '2.11.01', 15 | sidebarPosition: -1, 16 | icon: 'faArrowRight', 17 | inputs: [], 18 | outputs: [ 19 | { 20 | number: 1, 21 | tooltip: 'Continue to next plugin', 22 | }, 23 | ], 24 | }); }; 25 | exports.details = details; 26 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 27 | var plugin = function (args) { 28 | var lib = require('../../../../../methods/lib')(); 29 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 30 | args.inputs = lib.loadDefaultValues(args.inputs, details); 31 | return { 32 | outputFileObj: args.inputFileObj, 33 | outputNumber: 1, 34 | variables: args.variables, 35 | }; 36 | }; 37 | exports.plugin = plugin; 38 | -------------------------------------------------------------------------------- /FlowPlugins/CommunityFlowPlugins/tools/removeFromTdarr/1.0.0/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.plugin = exports.details = void 0; 4 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 5 | var details = function () { return ({ 6 | name: 'Remove From Tdarr', 7 | description: "\n If this plugin is executed, then when the flow ends, the item will be \n removed from the Tdarr database and won't appear in Transcode Success or Error tables on the 'Tdarr' tab.\n Use the 'Delete File' plugin if you would like to delete the file from disk.\n ", 8 | style: { 9 | borderColor: 'red', 10 | }, 11 | tags: '', 12 | isStartPlugin: false, 13 | pType: '', 14 | requiresVersion: '2.31.01', 15 | sidebarPosition: -1, 16 | icon: 'faTrash', 17 | inputs: [], 18 | outputs: [ 19 | { 20 | number: 1, 21 | tooltip: 'Continue to next plugin', 22 | }, 23 | ], 24 | }); }; 25 | exports.details = details; 26 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 27 | var plugin = function (args) { 28 | // eslint-disable-next-line no-param-reassign 29 | args.variables.removeFromTdarr = true; 30 | return { 31 | outputFileObj: args.inputFileObj, 32 | outputNumber: 1, 33 | variables: args.variables, 34 | }; 35 | }; 36 | exports.plugin = plugin; 37 | -------------------------------------------------------------------------------- /FlowPlugins/CommunityFlowPlugins/tools/requireReview/1.0.0/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.plugin = exports.details = void 0; 4 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 5 | var details = function () { return ({ 6 | name: 'Require Review', 7 | description: "Makes the flow pause.\n The file will stay in the staging section on the Tdarr tab until the user clicks the \"Reviewed\" button.\n\n Note: The 'Auto accept successful transcodes' option on the Tdarr tab will cause this plugin to be skipped.\n ", 8 | style: { 9 | borderColor: 'yellow', 10 | }, 11 | tags: '', 12 | isStartPlugin: false, 13 | pType: '', 14 | requiresVersion: '2.11.01', 15 | sidebarPosition: -1, 16 | icon: 'faHand', 17 | inputs: [], 18 | outputs: [ 19 | { 20 | number: 1, 21 | tooltip: 'Continue to next plugin', 22 | }, 23 | ], 24 | }); }; 25 | exports.details = details; 26 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 27 | var plugin = function (args) { 28 | var lib = require('../../../../../methods/lib')(); 29 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 30 | args.inputs = lib.loadDefaultValues(args.inputs, details); 31 | return { 32 | outputFileObj: args.inputFileObj, 33 | outputNumber: 1, 34 | variables: args.variables, 35 | }; 36 | }; 37 | exports.plugin = plugin; 38 | -------------------------------------------------------------------------------- /FlowPlugins/CommunityFlowPlugins/tools/resetFlowError/1.0.0/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.plugin = exports.details = void 0; 4 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 5 | var details = function () { return ({ 6 | name: 'Reset Flow Error', 7 | description: "After a flow error occurs, this plugin will reset the flow\nerror so that the flow will not go to error status at the end of the flow.", 8 | style: { 9 | borderColor: 'red', 10 | }, 11 | tags: '', 12 | isStartPlugin: false, 13 | pType: '', 14 | requiresVersion: '2.11.01', 15 | sidebarPosition: -1, 16 | icon: 'faUndo', 17 | inputs: [], 18 | outputs: [ 19 | { 20 | number: 1, 21 | tooltip: 'Continue to next plugin', 22 | }, 23 | ], 24 | }); }; 25 | exports.details = details; 26 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 27 | var plugin = function (args) { 28 | var lib = require('../../../../../methods/lib')(); 29 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 30 | args.inputs = lib.loadDefaultValues(args.inputs, details); 31 | // eslint-disable-next-line no-param-reassign 32 | args.variables.flowFailed = false; 33 | return { 34 | outputFileObj: args.inputFileObj, 35 | outputNumber: 1, 36 | variables: args.variables, 37 | }; 38 | }; 39 | exports.plugin = plugin; 40 | -------------------------------------------------------------------------------- /FlowPlugins/CommunityFlowPlugins/video/transcodeVideo/1.0.0/test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | -------------------------------------------------------------------------------- /FlowPlugins/FlowHelpers/1.0.0/interfaces/flowUtils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.checkFfmpegCommandInit = void 0; 4 | // eslint-disable-next-line import/prefer-default-export 5 | var checkFfmpegCommandInit = function (args) { 6 | var _a, _b; 7 | if (!((_b = (_a = args === null || args === void 0 ? void 0 : args.variables) === null || _a === void 0 ? void 0 : _a.ffmpegCommand) === null || _b === void 0 ? void 0 : _b.init)) { 8 | throw new Error('FFmpeg command plugins not used correctly.' 9 | + ' Please use the "Begin Command" plugin before using this plugin.' 10 | + ' Afterwards, use the "Execute" plugin to execute the built FFmpeg command.' 11 | + ' Once the "Execute" plugin has been used, you need to use a new "Begin Command"' 12 | + ' plugin to start a new FFmpeg command.'); 13 | } 14 | }; 15 | exports.checkFfmpegCommandInit = checkFfmpegCommandInit; 16 | -------------------------------------------------------------------------------- /FlowPlugins/FlowHelpers/1.0.0/interfaces/interfaces.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /FlowPlugins/FlowHelpers/1.0.0/interfaces/synced/IFileObject.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /FlowPlugins/FlowHelpers/1.0.0/interfaces/synced/jobInterface.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /FlowPlugins/FlowHelpers/1.0.0/normJoinPath.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var formatWindowsRootFolder = function (path) { 4 | // Remove '.' from end of Windows root folder mapping e.g. 'E:.' 5 | if (path.length === 3 6 | && path.charAt(1) === ':' 7 | && path.charAt(2) === '.') { 8 | // eslint-disable-next-line no-param-reassign 9 | path = path.slice(0, -1); 10 | } 11 | return path; 12 | }; 13 | var normJoinPath = function (_a) { 14 | var upath = _a.upath, paths = _a.paths; 15 | var path = upath.joinSafe.apply(upath, paths); 16 | path = formatWindowsRootFolder(path); 17 | return path; 18 | }; 19 | exports.default = normJoinPath; 20 | -------------------------------------------------------------------------------- /FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommand10BitVideo/1.0.0/index.ts: -------------------------------------------------------------------------------- 1 | import os from 'os'; 2 | import { 3 | IpluginDetails, 4 | IpluginInputArgs, 5 | IpluginOutputArgs, 6 | } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; 7 | import { checkFfmpegCommandInit } from '../../../../FlowHelpers/1.0.0/interfaces/flowUtils'; 8 | 9 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 10 | const details = () :IpluginDetails => ({ 11 | name: '10 Bit Video', 12 | description: 'Set 10 Bit Video', 13 | style: { 14 | borderColor: '#6efefc', 15 | }, 16 | tags: 'video', 17 | isStartPlugin: false, 18 | pType: '', 19 | requiresVersion: '2.11.01', 20 | sidebarPosition: -1, 21 | icon: '', 22 | inputs: [], 23 | outputs: [ 24 | { 25 | number: 1, 26 | tooltip: 'Continue to next plugin', 27 | }, 28 | ], 29 | }); 30 | 31 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 32 | const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { 33 | const lib = require('../../../../../methods/lib')(); 34 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 35 | args.inputs = lib.loadDefaultValues(args.inputs, details); 36 | 37 | checkFfmpegCommandInit(args); 38 | 39 | for (let i = 0; i < args.variables.ffmpegCommand.streams.length; i += 1) { 40 | const stream = args.variables.ffmpegCommand.streams[i]; 41 | if (stream.codec_type === 'video') { 42 | stream.outputArgs.push('-profile:v:{outputTypeIndex}', 'main10'); 43 | 44 | if (stream.outputArgs.some((row) => row.includes('qsv')) && os.platform() !== 'win32') { 45 | stream.outputArgs.push('-vf', 'scale_qsv=format=p010le'); 46 | } else { 47 | stream.outputArgs.push('-pix_fmt:v:{outputTypeIndex}', 'p010le'); 48 | } 49 | } 50 | } 51 | 52 | return { 53 | outputFileObj: args.inputFileObj, 54 | outputNumber: 1, 55 | variables: args.variables, 56 | }; 57 | }; 58 | export { 59 | details, 60 | plugin, 61 | }; 62 | -------------------------------------------------------------------------------- /FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandCropBlackBars/1.0.0/index.ts: -------------------------------------------------------------------------------- 1 | import { checkFfmpegCommandInit } from '../../../../FlowHelpers/1.0.0/interfaces/flowUtils'; 2 | import { 3 | IpluginDetails, 4 | IpluginInputArgs, 5 | IpluginOutputArgs, 6 | } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; 7 | 8 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 9 | const details = () :IpluginDetails => ({ 10 | name: 'Crop Black Bars', 11 | description: 'Crop Black Bars', 12 | style: { 13 | borderColor: '#6efefc', 14 | opacity: 0.5, 15 | }, 16 | tags: 'video', 17 | isStartPlugin: false, 18 | pType: '', 19 | requiresVersion: '2.11.01', 20 | sidebarPosition: -1, 21 | icon: '', 22 | inputs: [], 23 | outputs: [ 24 | { 25 | number: 1, 26 | tooltip: 'Continue to next plugin', 27 | }, 28 | ], 29 | }); 30 | 31 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 32 | const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { 33 | const lib = require('../../../../../methods/lib')(); 34 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 35 | args.inputs = lib.loadDefaultValues(args.inputs, details); 36 | 37 | checkFfmpegCommandInit(args); 38 | 39 | return { 40 | outputFileObj: args.inputFileObj, 41 | outputNumber: 1, 42 | variables: args.variables, 43 | }; 44 | }; 45 | export { 46 | details, 47 | plugin, 48 | }; 49 | -------------------------------------------------------------------------------- /FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandHdrToSdr/1.0.0/index.ts: -------------------------------------------------------------------------------- 1 | import { checkFfmpegCommandInit } from '../../../../FlowHelpers/1.0.0/interfaces/flowUtils'; 2 | import { 3 | IpluginDetails, 4 | IpluginInputArgs, 5 | IpluginOutputArgs, 6 | } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; 7 | 8 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 9 | const details = () :IpluginDetails => ({ 10 | name: 'HDR to SDR', 11 | description: 'Convert HDR to SDR', 12 | style: { 13 | borderColor: '#6efefc', 14 | }, 15 | tags: 'video', 16 | isStartPlugin: false, 17 | pType: '', 18 | requiresVersion: '2.11.01', 19 | sidebarPosition: -1, 20 | icon: '', 21 | inputs: [], 22 | outputs: [ 23 | { 24 | number: 1, 25 | tooltip: 'Continue to next plugin', 26 | }, 27 | ], 28 | }); 29 | 30 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 31 | const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { 32 | const lib = require('../../../../../methods/lib')(); 33 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 34 | args.inputs = lib.loadDefaultValues(args.inputs, details); 35 | 36 | checkFfmpegCommandInit(args); 37 | 38 | args.variables.ffmpegCommand.streams.forEach((stream) => { 39 | if (stream.codec_type === 'video') { 40 | stream.outputArgs.push('-vf', 'zscale=t=linear:npl=100,format=yuv420p'); 41 | } 42 | }); 43 | 44 | return { 45 | outputFileObj: args.inputFileObj, 46 | outputNumber: 1, 47 | variables: args.variables, 48 | }; 49 | }; 50 | export { 51 | details, 52 | plugin, 53 | }; 54 | -------------------------------------------------------------------------------- /FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandNormalizeAudio/1.0.0/index.ts: -------------------------------------------------------------------------------- 1 | import { checkFfmpegCommandInit } from '../../../../FlowHelpers/1.0.0/interfaces/flowUtils'; 2 | import { 3 | IpluginDetails, 4 | IpluginInputArgs, 5 | IpluginOutputArgs, 6 | } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; 7 | 8 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 9 | const details = () :IpluginDetails => ({ 10 | name: 'Normalize Audio', 11 | description: 'Normalize Audio', 12 | style: { 13 | borderColor: '#6efefc', 14 | opacity: 0.5, 15 | }, 16 | tags: 'video', 17 | isStartPlugin: false, 18 | pType: '', 19 | requiresVersion: '2.11.01', 20 | sidebarPosition: -1, 21 | icon: '', 22 | inputs: [], 23 | outputs: [ 24 | { 25 | number: 1, 26 | tooltip: 'Continue to next plugin', 27 | }, 28 | ], 29 | }); 30 | 31 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 32 | const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { 33 | const lib = require('../../../../../methods/lib')(); 34 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 35 | args.inputs = lib.loadDefaultValues(args.inputs, details); 36 | 37 | checkFfmpegCommandInit(args); 38 | 39 | return { 40 | outputFileObj: args.inputFileObj, 41 | outputNumber: 1, 42 | variables: args.variables, 43 | }; 44 | }; 45 | export { 46 | details, 47 | plugin, 48 | }; 49 | -------------------------------------------------------------------------------- /FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveDataStreams/1.0.0/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 2 | 3 | import { checkFfmpegCommandInit } from '../../../../FlowHelpers/1.0.0/interfaces/flowUtils'; 4 | import { 5 | IpluginDetails, 6 | IpluginInputArgs, 7 | IpluginOutputArgs, 8 | } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; 9 | 10 | /* eslint-disable no-param-reassign */ 11 | const details = ():IpluginDetails => ({ 12 | name: 'Remove Data Streams', 13 | description: 'Remove Data Streams ', 14 | style: { 15 | borderColor: '#6efefc', 16 | }, 17 | tags: 'video', 18 | 19 | isStartPlugin: false, 20 | pType: '', 21 | requiresVersion: '2.11.01', 22 | sidebarPosition: -1, 23 | icon: '', 24 | inputs: [], 25 | outputs: [ 26 | { 27 | number: 1, 28 | tooltip: 'Continue to next plugin', 29 | }, 30 | ], 31 | }); 32 | 33 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 34 | const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { 35 | const lib = require('../../../../../methods/lib')(); 36 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 37 | args.inputs = lib.loadDefaultValues(args.inputs, details); 38 | 39 | checkFfmpegCommandInit(args); 40 | 41 | args.variables.ffmpegCommand.streams.forEach((stream) => { 42 | if (stream.codec_type === 'data') { 43 | stream.removed = true; 44 | } 45 | }); 46 | 47 | return { 48 | outputFileObj: args.inputFileObj, 49 | outputNumber: 1, 50 | variables: args.variables, 51 | }; 52 | }; 53 | export { 54 | details, 55 | plugin, 56 | }; 57 | -------------------------------------------------------------------------------- /FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandRemoveSubtitles/1.0.0/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 2 | 3 | import { checkFfmpegCommandInit } from '../../../../FlowHelpers/1.0.0/interfaces/flowUtils'; 4 | import { 5 | IpluginDetails, 6 | IpluginInputArgs, 7 | IpluginOutputArgs, 8 | } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; 9 | 10 | /* eslint-disable no-param-reassign */ 11 | const details = ():IpluginDetails => ({ 12 | name: 'Remove Subtitles', 13 | description: 'Remove subtitles from the file', 14 | style: { 15 | borderColor: '#6efefc', 16 | }, 17 | tags: 'video', 18 | isStartPlugin: false, 19 | pType: '', 20 | requiresVersion: '2.11.01', 21 | sidebarPosition: -1, 22 | icon: '', 23 | inputs: [], 24 | outputs: [ 25 | { 26 | number: 1, 27 | tooltip: 'Continue to next plugin', 28 | }, 29 | ], 30 | }); 31 | 32 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 33 | const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { 34 | const lib = require('../../../../../methods/lib')(); 35 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 36 | args.inputs = lib.loadDefaultValues(args.inputs, details); 37 | 38 | checkFfmpegCommandInit(args); 39 | 40 | args.variables.ffmpegCommand.streams.forEach((stream) => { 41 | if (stream.codec_type === 'subtitle') { 42 | stream.removed = true; 43 | } 44 | }); 45 | 46 | return { 47 | outputFileObj: args.inputFileObj, 48 | outputNumber: 1, 49 | variables: args.variables, 50 | }; 51 | }; 52 | 53 | export { 54 | details, 55 | plugin, 56 | }; 57 | -------------------------------------------------------------------------------- /FlowPluginsTs/CommunityFlowPlugins/file/checkFileExtension/1.0.0/index.ts: -------------------------------------------------------------------------------- 1 | import { getContainer } from '../../../../FlowHelpers/1.0.0/fileUtils'; 2 | import { 3 | IpluginDetails, 4 | IpluginInputArgs, 5 | IpluginOutputArgs, 6 | } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; 7 | 8 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 9 | const details = (): IpluginDetails => ({ 10 | name: 'Check File Extension', 11 | description: 'Check file extension', 12 | style: { 13 | borderColor: 'orange', 14 | }, 15 | tags: 'video', 16 | isStartPlugin: false, 17 | pType: '', 18 | requiresVersion: '2.11.01', 19 | sidebarPosition: -1, 20 | icon: 'faQuestion', 21 | inputs: [ 22 | { 23 | label: 'Extensions', 24 | name: 'extensions', 25 | type: 'string', 26 | defaultValue: 'mkv,mp4', 27 | inputUI: { 28 | type: 'text', 29 | }, 30 | tooltip: 'A comma separated list of extensions to check', 31 | }, 32 | ], 33 | outputs: [ 34 | { 35 | number: 1, 36 | tooltip: 'File is one of extensions', 37 | }, 38 | { 39 | number: 2, 40 | tooltip: 'File is not one of extensions', 41 | }, 42 | ], 43 | }); 44 | 45 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 46 | const plugin = (args: IpluginInputArgs): IpluginOutputArgs => { 47 | const lib = require('../../../../../methods/lib')(); 48 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 49 | args.inputs = lib.loadDefaultValues(args.inputs, details); 50 | 51 | const extensions = String(args.inputs.extensions); 52 | const extensionArray = extensions.trim().split(',').map((row) => row.toLowerCase()); 53 | 54 | const extension = getContainer(args.inputFileObj._id).toLowerCase(); 55 | 56 | let extensionMatch = false; 57 | 58 | if (extensionArray.includes(extension)) { 59 | extensionMatch = true; 60 | } 61 | 62 | return { 63 | outputFileObj: args.inputFileObj, 64 | outputNumber: extensionMatch ? 1 : 2, 65 | variables: args.variables, 66 | }; 67 | }; 68 | export { 69 | details, 70 | plugin, 71 | }; 72 | -------------------------------------------------------------------------------- /FlowPluginsTs/CommunityFlowPlugins/file/checkFileMedium/1.0.0/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IpluginDetails, 3 | IpluginInputArgs, 4 | IpluginOutputArgs, 5 | } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; 6 | 7 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 8 | const details = (): IpluginDetails => ({ 9 | name: 'Check File Medium', 10 | description: 'Check if file is video, audio or other type of file', 11 | style: { 12 | borderColor: 'orange', 13 | }, 14 | tags: '', 15 | 16 | isStartPlugin: false, 17 | pType: '', 18 | requiresVersion: '2.11.01', 19 | sidebarPosition: -1, 20 | icon: 'faQuestion', 21 | inputs: [], 22 | outputs: [ 23 | { 24 | number: 1, 25 | tooltip: 'File medium is a Video', 26 | 27 | }, 28 | { 29 | number: 2, 30 | tooltip: 'File medium is an Audio', 31 | }, 32 | { 33 | number: 3, 34 | tooltip: 'File medium is Other', 35 | }, 36 | ], 37 | }); 38 | 39 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 40 | const plugin = (args: IpluginInputArgs): IpluginOutputArgs => { 41 | const lib = require('../../../../../methods/lib')(); 42 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 43 | args.inputs = lib.loadDefaultValues(args.inputs, details); 44 | 45 | let outputNumber = 1; 46 | switch (args.inputFileObj.fileMedium) { 47 | case 'video': 48 | outputNumber = 1; 49 | break; 50 | case 'audio': 51 | outputNumber = 2; 52 | break; 53 | case 'other': 54 | outputNumber = 3; 55 | break; 56 | default: 57 | throw new Error('File has no fileMedium!'); 58 | } 59 | 60 | return { 61 | outputFileObj: args.inputFileObj, 62 | outputNumber, 63 | variables: args.variables, 64 | }; 65 | }; 66 | export { 67 | details, 68 | plugin, 69 | }; 70 | -------------------------------------------------------------------------------- /FlowPluginsTs/CommunityFlowPlugins/file/moveToDirectory/1.0.0/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IpluginDetails, 3 | IpluginInputArgs, 4 | IpluginOutputArgs, 5 | } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; 6 | 7 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 8 | const details = ():IpluginDetails => ({ 9 | name: 'Move To Directory', 10 | description: 'Move working file to directory.', 11 | style: { 12 | borderColor: 'green', 13 | opacity: 0.5, 14 | }, 15 | tags: '', 16 | isStartPlugin: false, 17 | pType: '', 18 | requiresVersion: '2.11.01', 19 | sidebarPosition: -1, 20 | icon: '', 21 | inputs: [], 22 | outputs: [ 23 | { 24 | number: 1, 25 | tooltip: 'Continue to next plugin', 26 | }, 27 | ], 28 | }); 29 | 30 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 31 | const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { 32 | const lib = require('../../../../../methods/lib')(); 33 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 34 | args.inputs = lib.loadDefaultValues(args.inputs, details); 35 | 36 | return { 37 | outputFileObj: args.inputFileObj, 38 | outputNumber: 1, 39 | variables: args.variables, 40 | }; 41 | }; 42 | export { 43 | details, 44 | plugin, 45 | }; 46 | -------------------------------------------------------------------------------- /FlowPluginsTs/CommunityFlowPlugins/file/setOriginalFile/1.0.0/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IpluginDetails, 3 | IpluginInputArgs, 4 | IpluginOutputArgs, 5 | } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; 6 | 7 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 8 | const details = ():IpluginDetails => ({ 9 | name: 'Set Original File', 10 | description: 'Set the working file to the original file path at the very start of the flow.', 11 | style: { 12 | borderColor: 'green', 13 | }, 14 | tags: '', 15 | isStartPlugin: false, 16 | pType: '', 17 | requiresVersion: '2.11.01', 18 | sidebarPosition: -1, 19 | icon: '', 20 | inputs: [], 21 | outputs: [ 22 | { 23 | number: 1, 24 | tooltip: 'Continue to next plugin', 25 | }, 26 | ], 27 | }); 28 | 29 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 30 | const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { 31 | const lib = require('../../../../../methods/lib')(); 32 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 33 | args.inputs = lib.loadDefaultValues(args.inputs, details); 34 | 35 | return { 36 | outputFileObj: { 37 | _id: args.originalLibraryFile._id, 38 | }, 39 | outputNumber: 1, 40 | variables: args.variables, 41 | }; 42 | }; 43 | export { 44 | details, 45 | plugin, 46 | }; 47 | -------------------------------------------------------------------------------- /FlowPluginsTs/CommunityFlowPlugins/file/unpack/1.0.0/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IpluginDetails, 3 | IpluginInputArgs, 4 | IpluginOutputArgs, 5 | } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; 6 | 7 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 8 | const details = ():IpluginDetails => ({ 9 | name: 'Unpack File', 10 | description: 'Unpack a file', 11 | style: { 12 | borderColor: 'green', 13 | opacity: 0.5, 14 | }, 15 | tags: '', 16 | isStartPlugin: false, 17 | pType: '', 18 | requiresVersion: '2.11.01', 19 | sidebarPosition: -1, 20 | icon: 'faArrowRight', 21 | inputs: [], 22 | outputs: [ 23 | { 24 | number: 1, 25 | tooltip: 'Continue to next plugin', 26 | }, 27 | ], 28 | }); 29 | 30 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 31 | const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { 32 | const lib = require('../../../../../methods/lib')(); 33 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 34 | args.inputs = lib.loadDefaultValues(args.inputs, details); 35 | 36 | return { 37 | outputFileObj: args.inputFileObj, 38 | outputNumber: 1, 39 | variables: args.variables, 40 | }; 41 | }; 42 | export { 43 | details, 44 | plugin, 45 | }; 46 | -------------------------------------------------------------------------------- /FlowPluginsTs/CommunityFlowPlugins/tools/comment/1.0.0/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IpluginDetails, 3 | IpluginInputArgs, 4 | IpluginOutputArgs, 5 | } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; 6 | 7 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 8 | const details = (): IpluginDetails => ({ 9 | name: 'Comment', 10 | nameUI: { 11 | type: 'textarea', 12 | style: { 13 | height: '250px', 14 | }, 15 | }, 16 | description: `Add a comment to your flow. Can place anywhere and link together. 17 | Any file input into the comment will be passed straight through.`, 18 | style: { 19 | borderColor: 'white', 20 | borderRadius: '10px', 21 | backgroundColor: '#043775', 22 | }, 23 | tags: '', 24 | isStartPlugin: false, 25 | pType: '', 26 | requiresVersion: '2.11.01', 27 | sidebarPosition: -1, 28 | icon: 'faComment', 29 | inputs: [], 30 | outputs: [ 31 | { 32 | number: 1, 33 | tooltip: 'Continue to next plugin', 34 | }, 35 | ], 36 | }); 37 | 38 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 39 | const plugin = (args: IpluginInputArgs): IpluginOutputArgs => { 40 | const lib = require('../../../../../methods/lib')(); 41 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 42 | args.inputs = lib.loadDefaultValues(args.inputs, details); 43 | 44 | return { 45 | outputFileObj: args.inputFileObj, 46 | outputNumber: 1, 47 | variables: args.variables, 48 | }; 49 | }; 50 | export { 51 | details, 52 | plugin, 53 | }; 54 | -------------------------------------------------------------------------------- /FlowPluginsTs/CommunityFlowPlugins/tools/failFlow/1.0.0/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IpluginDetails, 3 | IpluginInputArgs, 4 | IpluginOutputArgs, 5 | } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; 6 | 7 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 8 | const details = ():IpluginDetails => ({ 9 | name: 'Fail Flow', 10 | description: 'Force the flow to fail and be move to the error table', 11 | style: { 12 | borderColor: 'red', 13 | }, 14 | tags: '', 15 | isStartPlugin: false, 16 | pType: '', 17 | requiresVersion: '2.11.01', 18 | sidebarPosition: -1, 19 | icon: 'faExclamationTriangle', 20 | inputs: [], 21 | outputs: [], 22 | }); 23 | 24 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 25 | const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { 26 | const lib = require('../../../../../methods/lib')(); 27 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 28 | args.inputs = lib.loadDefaultValues(args.inputs, details); 29 | 30 | throw new Error('Forcing flow to fail!'); 31 | 32 | return { 33 | outputFileObj: args.inputFileObj, 34 | outputNumber: 1, 35 | variables: args.variables, 36 | }; 37 | }; 38 | export { 39 | details, 40 | plugin, 41 | }; 42 | -------------------------------------------------------------------------------- /FlowPluginsTs/CommunityFlowPlugins/tools/goToFlow/1.0.0/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IpluginDetails, 3 | IpluginInputArgs, 4 | IpluginOutputArgs, 5 | } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; 6 | 7 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 8 | const details = ():IpluginDetails => ({ 9 | name: 'Go To Flow', 10 | description: 'Go to a different flow', 11 | style: { 12 | borderColor: 'green', 13 | }, 14 | tags: '', 15 | isStartPlugin: false, 16 | pType: '', 17 | requiresVersion: '2.11.01', 18 | sidebarPosition: -1, 19 | icon: 'faArrowRight', 20 | inputs: [ 21 | { 22 | label: 'Flow ID', 23 | name: 'flowId', 24 | type: 'string', 25 | defaultValue: '', 26 | inputUI: { 27 | type: 'dropdown', 28 | options: [], 29 | }, 30 | tooltip: 'Specify flow ID to go to', 31 | }, 32 | ], 33 | outputs: [], 34 | }); 35 | 36 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 37 | const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { 38 | const lib = require('../../../../../methods/lib')(); 39 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 40 | args.inputs = lib.loadDefaultValues(args.inputs, details); 41 | 42 | return { 43 | outputFileObj: args.inputFileObj, 44 | outputNumber: 1, 45 | variables: args.variables, 46 | }; 47 | }; 48 | export { 49 | details, 50 | plugin, 51 | }; 52 | -------------------------------------------------------------------------------- /FlowPluginsTs/CommunityFlowPlugins/tools/goToFlow/2.0.0/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IpluginDetails, 3 | IpluginInputArgs, 4 | IpluginOutputArgs, 5 | } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; 6 | 7 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 8 | const details = ():IpluginDetails => ({ 9 | name: 'Go To Flow', 10 | description: 'Go to a different flow', 11 | style: { 12 | borderColor: 'green', 13 | }, 14 | tags: '', 15 | isStartPlugin: false, 16 | pType: '', 17 | requiresVersion: '2.14.01', 18 | sidebarPosition: -1, 19 | icon: 'faArrowRight', 20 | inputs: [ 21 | { 22 | label: 'Flow ID', 23 | name: 'flowId', 24 | type: 'string', 25 | defaultValue: '', 26 | inputUI: { 27 | type: 'dropdown', 28 | options: [], 29 | }, 30 | tooltip: 'Specify flow ID to go to', 31 | }, 32 | { 33 | label: 'Plugin ID', 34 | name: 'pluginId', 35 | type: 'string', 36 | defaultValue: 'start', 37 | inputUI: { 38 | type: 'text', 39 | }, 40 | tooltip: 'Specify plugin ID to go to', 41 | }, 42 | ], 43 | outputs: [], 44 | }); 45 | 46 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 47 | const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { 48 | const lib = require('../../../../../methods/lib')(); 49 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 50 | args.inputs = lib.loadDefaultValues(args.inputs, details); 51 | 52 | return { 53 | outputFileObj: args.inputFileObj, 54 | outputNumber: 1, 55 | variables: args.variables, 56 | }; 57 | }; 58 | export { 59 | details, 60 | plugin, 61 | }; 62 | -------------------------------------------------------------------------------- /FlowPluginsTs/CommunityFlowPlugins/tools/onFlowError/1.0.0/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IpluginDetails, 3 | IpluginInputArgs, 4 | IpluginOutputArgs, 5 | } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; 6 | 7 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 8 | const details = ():IpluginDetails => ({ 9 | name: 'On Flow Error', 10 | description: `Runs if an error occurs in this specific flow. 11 | Won't run if error occurs in after going to a different flow (unless that flow comes back to this one).`, 12 | style: { 13 | borderColor: 'red', 14 | }, 15 | tags: '', 16 | isStartPlugin: false, 17 | pType: 'onFlowError', 18 | requiresVersion: '2.11.01', 19 | sidebarPosition: -1, 20 | icon: 'faArrowRight', 21 | inputs: [], 22 | outputs: [ 23 | { 24 | number: 1, 25 | tooltip: 'Continue to next plugin', 26 | }, 27 | ], 28 | }); 29 | 30 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 31 | const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { 32 | const lib = require('../../../../../methods/lib')(); 33 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 34 | args.inputs = lib.loadDefaultValues(args.inputs, details); 35 | 36 | return { 37 | outputFileObj: args.inputFileObj, 38 | outputNumber: 1, 39 | variables: args.variables, 40 | }; 41 | }; 42 | export { 43 | details, 44 | plugin, 45 | }; 46 | -------------------------------------------------------------------------------- /FlowPluginsTs/CommunityFlowPlugins/tools/pauseUnpauseAllNodes/1.0.0/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IpluginDetails, 3 | IpluginInputArgs, 4 | IpluginOutputArgs, 5 | } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; 6 | 7 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 8 | const details = (): IpluginDetails => ({ 9 | name: 'Pause/Unpause All Nodes', 10 | description: ` 11 | Pause/Unpause All Nodes 12 | `, 13 | style: { 14 | borderColor: 'yellow', 15 | }, 16 | tags: '', 17 | isStartPlugin: false, 18 | pType: '', 19 | requiresVersion: '2.11.01', 20 | sidebarPosition: -1, 21 | icon: 'faHand', 22 | inputs: [ 23 | { 24 | label: 'Pause?', 25 | name: 'pause', 26 | type: 'boolean', 27 | defaultValue: 'false', 28 | inputUI: { 29 | type: 'switch', 30 | }, 31 | tooltip: 'Specify whether to pause or unpause all nodes', 32 | }, 33 | ], 34 | outputs: [ 35 | { 36 | number: 1, 37 | tooltip: 'Continue to next plugin', 38 | }, 39 | ], 40 | }); 41 | 42 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 43 | const plugin = async (args: IpluginInputArgs): Promise => { 44 | const lib = require('../../../../../methods/lib')(); 45 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 46 | args.inputs = lib.loadDefaultValues(args.inputs, details); 47 | 48 | const { pause } = args.inputs; 49 | 50 | await args.deps.crudTransDBN('SettingsGlobalJSONDB', 'update', 'globalsettings', { 51 | pauseAllNodes: pause, 52 | }); 53 | 54 | return { 55 | outputFileObj: args.inputFileObj, 56 | outputNumber: 1, 57 | variables: args.variables, 58 | }; 59 | }; 60 | export { 61 | details, 62 | plugin, 63 | }; 64 | -------------------------------------------------------------------------------- /FlowPluginsTs/CommunityFlowPlugins/tools/removeFromTdarr/1.0.0/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IpluginDetails, 3 | IpluginInputArgs, 4 | IpluginOutputArgs, 5 | } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; 6 | 7 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 8 | const details = (): IpluginDetails => ({ 9 | name: 'Remove From Tdarr', 10 | description: ` 11 | If this plugin is executed, then when the flow ends, the item will be 12 | removed from the Tdarr database and won't appear in Transcode Success or Error tables on the 'Tdarr' tab. 13 | Use the 'Delete File' plugin if you would like to delete the file from disk. 14 | `, 15 | style: { 16 | borderColor: 'red', 17 | }, 18 | tags: '', 19 | isStartPlugin: false, 20 | pType: '', 21 | requiresVersion: '2.31.01', 22 | sidebarPosition: -1, 23 | icon: 'faTrash', 24 | inputs: [], 25 | outputs: [ 26 | { 27 | number: 1, 28 | tooltip: 'Continue to next plugin', 29 | }, 30 | ], 31 | }); 32 | 33 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 34 | const plugin = (args: IpluginInputArgs): IpluginOutputArgs => { 35 | // eslint-disable-next-line no-param-reassign 36 | args.variables.removeFromTdarr = true; 37 | 38 | return { 39 | outputFileObj: args.inputFileObj, 40 | outputNumber: 1, 41 | variables: args.variables, 42 | }; 43 | }; 44 | export { 45 | details, 46 | plugin, 47 | }; 48 | -------------------------------------------------------------------------------- /FlowPluginsTs/CommunityFlowPlugins/tools/requireReview/1.0.0/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IpluginDetails, 3 | IpluginInputArgs, 4 | IpluginOutputArgs, 5 | } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; 6 | 7 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 8 | const details = (): IpluginDetails => ({ 9 | name: 'Require Review', 10 | description: `Makes the flow pause. 11 | The file will stay in the staging section on the Tdarr tab until the user clicks the "Reviewed" button. 12 | 13 | Note: The 'Auto accept successful transcodes' option on the Tdarr tab will cause this plugin to be skipped. 14 | `, 15 | style: { 16 | borderColor: 'yellow', 17 | }, 18 | tags: '', 19 | isStartPlugin: false, 20 | pType: '', 21 | requiresVersion: '2.11.01', 22 | sidebarPosition: -1, 23 | icon: 'faHand', 24 | inputs: [], 25 | outputs: [ 26 | { 27 | number: 1, 28 | tooltip: 'Continue to next plugin', 29 | }, 30 | ], 31 | }); 32 | 33 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 34 | const plugin = (args: IpluginInputArgs): IpluginOutputArgs => { 35 | const lib = require('../../../../../methods/lib')(); 36 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 37 | args.inputs = lib.loadDefaultValues(args.inputs, details); 38 | 39 | return { 40 | outputFileObj: args.inputFileObj, 41 | outputNumber: 1, 42 | variables: args.variables, 43 | }; 44 | }; 45 | export { 46 | details, 47 | plugin, 48 | }; 49 | -------------------------------------------------------------------------------- /FlowPluginsTs/CommunityFlowPlugins/tools/resetFlowError/1.0.0/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IpluginDetails, 3 | IpluginInputArgs, 4 | IpluginOutputArgs, 5 | } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; 6 | 7 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 8 | const details = ():IpluginDetails => ({ 9 | name: 'Reset Flow Error', 10 | description: `After a flow error occurs, this plugin will reset the flow 11 | error so that the flow will not go to error status at the end of the flow.`, 12 | style: { 13 | borderColor: 'red', 14 | }, 15 | tags: '', 16 | isStartPlugin: false, 17 | pType: '', 18 | requiresVersion: '2.11.01', 19 | sidebarPosition: -1, 20 | icon: 'faUndo', 21 | inputs: [], 22 | outputs: [ 23 | { 24 | number: 1, 25 | tooltip: 'Continue to next plugin', 26 | }, 27 | ], 28 | }); 29 | 30 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 31 | const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { 32 | const lib = require('../../../../../methods/lib')(); 33 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 34 | args.inputs = lib.loadDefaultValues(args.inputs, details); 35 | 36 | // eslint-disable-next-line no-param-reassign 37 | args.variables.flowFailed = false; 38 | 39 | return { 40 | outputFileObj: args.inputFileObj, 41 | outputNumber: 1, 42 | variables: args.variables, 43 | }; 44 | }; 45 | export { 46 | details, 47 | plugin, 48 | }; 49 | -------------------------------------------------------------------------------- /FlowPluginsTs/CommunityFlowPlugins/tools/runMkvPropEdit/1.0.0/index.ts: -------------------------------------------------------------------------------- 1 | import { CLI } from '../../../../FlowHelpers/1.0.0/cliUtils'; 2 | import { 3 | IpluginDetails, 4 | IpluginInputArgs, 5 | IpluginOutputArgs, 6 | } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; 7 | 8 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 9 | const details = ():IpluginDetails => ({ 10 | name: 'Run MKVPropEdit', 11 | description: 'Run MKVPropEdit on a file to update metadata which' 12 | + ' FFmpeg doesn\'t typically update such as stream bitrate.', 13 | style: { 14 | borderColor: 'green', 15 | }, 16 | tags: '', 17 | isStartPlugin: false, 18 | pType: '', 19 | requiresVersion: '2.11.01', 20 | sidebarPosition: -1, 21 | icon: '', 22 | inputs: [], 23 | outputs: [ 24 | { 25 | number: 1, 26 | tooltip: 'Continue to next plugin', 27 | }, 28 | ], 29 | }); 30 | 31 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 32 | const plugin = async (args:IpluginInputArgs):Promise => { 33 | const lib = require('../../../../../methods/lib')(); 34 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 35 | args.inputs = lib.loadDefaultValues(args.inputs, details); 36 | 37 | const cliArgs = [ 38 | '--add-track-statistics-tags', 39 | args.inputFileObj._id, 40 | ]; 41 | 42 | const cli = new CLI({ 43 | cli: args.mkvpropeditPath, 44 | spawnArgs: cliArgs, 45 | spawnOpts: {}, 46 | jobLog: args.jobLog, 47 | outputFilePath: '', 48 | inputFileObj: args.inputFileObj, 49 | logFullCliOutput: args.logFullCliOutput, 50 | updateWorker: args.updateWorker, 51 | args, 52 | }); 53 | 54 | const res = await cli.runCli(); 55 | 56 | if (res.cliExitCode !== 0) { 57 | args.jobLog('Running MKVPropEdit failed'); 58 | throw new Error('Running MKVPropEdit failed'); 59 | } 60 | 61 | return { 62 | outputFileObj: args.inputFileObj, 63 | outputNumber: 1, 64 | variables: args.variables, 65 | }; 66 | }; 67 | export { 68 | details, 69 | plugin, 70 | }; 71 | -------------------------------------------------------------------------------- /FlowPluginsTs/CommunityFlowPlugins/video/check10Bit/1.0.0/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IpluginDetails, 3 | IpluginInputArgs, 4 | IpluginOutputArgs, 5 | } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; 6 | 7 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 8 | const details = (): IpluginDetails => ({ 9 | name: 'Check 10 Bit Video', 10 | description: 'Check if a file is 10 bit video', 11 | style: { 12 | borderColor: 'orange', 13 | }, 14 | tags: 'video', 15 | isStartPlugin: false, 16 | pType: '', 17 | requiresVersion: '2.11.01', 18 | sidebarPosition: -1, 19 | icon: 'faQuestion', 20 | inputs: [], 21 | outputs: [ 22 | { 23 | number: 1, 24 | tooltip: 'File is 10 bit video', 25 | }, 26 | { 27 | number: 2, 28 | tooltip: 'File is not 10 bit video', 29 | }, 30 | ], 31 | }); 32 | 33 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 34 | const plugin = (args: IpluginInputArgs): IpluginOutputArgs => { 35 | const lib = require('../../../../../methods/lib')(); 36 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 37 | args.inputs = lib.loadDefaultValues(args.inputs, details); 38 | 39 | let is10Bit = false; 40 | 41 | if (Array.isArray(args?.inputFileObj?.ffProbeData?.streams)) { 42 | for (let i = 0; i < args.inputFileObj.ffProbeData.streams.length; i += 1) { 43 | const stream = args.inputFileObj.ffProbeData.streams[i]; 44 | if ( 45 | stream.codec_type === 'video' 46 | && ( 47 | stream.bits_per_raw_sample === 10 48 | || stream.pix_fmt === 'yuv420p10le' 49 | ) 50 | ) { 51 | is10Bit = true; 52 | } 53 | } 54 | } else { 55 | throw new Error('File has not stream data'); 56 | } 57 | 58 | return { 59 | outputFileObj: args.inputFileObj, 60 | outputNumber: is10Bit ? 1 : 2, 61 | variables: args.variables, 62 | }; 63 | }; 64 | export { 65 | details, 66 | plugin, 67 | }; 68 | -------------------------------------------------------------------------------- /FlowPluginsTs/CommunityFlowPlugins/video/checkHdr/1.0.0/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IpluginDetails, 3 | IpluginInputArgs, 4 | IpluginOutputArgs, 5 | } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; 6 | 7 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 8 | const details = (): IpluginDetails => ({ 9 | name: 'Check HDR Video', 10 | description: 'Check if video is HDR', 11 | style: { 12 | borderColor: 'orange', 13 | }, 14 | tags: 'video', 15 | isStartPlugin: false, 16 | pType: '', 17 | requiresVersion: '2.11.01', 18 | sidebarPosition: -1, 19 | icon: 'faQuestion', 20 | inputs: [], 21 | outputs: [ 22 | { 23 | number: 1, 24 | tooltip: 'File is HDR', 25 | }, 26 | { 27 | number: 2, 28 | tooltip: 'File is not HDR', 29 | }, 30 | ], 31 | }); 32 | 33 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 34 | const plugin = (args: IpluginInputArgs): IpluginOutputArgs => { 35 | const lib = require('../../../../../methods/lib')(); 36 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 37 | args.inputs = lib.loadDefaultValues(args.inputs, details); 38 | 39 | let isHdr = false; 40 | 41 | if (Array.isArray(args?.inputFileObj?.ffProbeData?.streams)) { 42 | for (let i = 0; i < args.inputFileObj.ffProbeData.streams.length; i += 1) { 43 | const stream = args.inputFileObj.ffProbeData.streams[i]; 44 | if ( 45 | stream.codec_type === 'video' 46 | && stream.color_transfer === 'smpte2084' 47 | && stream.color_primaries === 'bt2020' 48 | && stream.color_range === 'tv' 49 | ) { 50 | isHdr = true; 51 | } 52 | } 53 | } else { 54 | throw new Error('File has not stream data'); 55 | } 56 | 57 | return { 58 | outputFileObj: args.inputFileObj, 59 | outputNumber: isHdr ? 1 : 2, 60 | variables: args.variables, 61 | }; 62 | }; 63 | export { 64 | details, 65 | plugin, 66 | }; 67 | -------------------------------------------------------------------------------- /FlowPluginsTs/CommunityFlowPlugins/video/transcodeVideo/1.0.0/index.ts: -------------------------------------------------------------------------------- 1 | import { promises as fsp } from 'fs'; 2 | import { 3 | IpluginDetails, 4 | IpluginInputArgs, 5 | IpluginOutputArgs, 6 | } from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; 7 | import { fileExists } from '../../../../FlowHelpers/1.0.0/fileUtils'; 8 | 9 | /* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ 10 | const details = ():IpluginDetails => ({ 11 | name: 'Transcode Video File', 12 | description: 'Transcode a video file using ffmpeg. GPU transcoding will be used if possible.', 13 | style: { 14 | borderColor: '#6efefc', 15 | opacity: 0.5, 16 | }, 17 | tags: '', 18 | 19 | isStartPlugin: false, 20 | pType: '', 21 | requiresVersion: '2.11.01', 22 | sidebarPosition: -1, 23 | icon: '', 24 | inputs: [ 25 | { 26 | label: 'Target Codec', 27 | name: 'target_codec', 28 | type: 'string', 29 | defaultValue: 'hevc', 30 | inputUI: { 31 | type: 'dropdown', 32 | options: [ 33 | 'hevc', 34 | // 'vp9', 35 | 'h264', 36 | // 'vp8', 37 | ], 38 | }, 39 | tooltip: 'Specify the codec to use', 40 | }, 41 | ], 42 | outputs: [ 43 | { 44 | number: 1, 45 | tooltip: 'Continue to next plugin', 46 | }, 47 | ], 48 | }); 49 | 50 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 51 | const plugin = async (args:IpluginInputArgs):Promise => { 52 | const lib = require('../../../../../methods/lib')(); 53 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 54 | args.inputs = lib.loadDefaultValues(args.inputs, details); 55 | 56 | const oldFile = args.inputFileObj._id; 57 | const newFile = `${args.inputFileObj._id}.tmp`; 58 | 59 | if (await fileExists(newFile)) { 60 | await fsp.unlink(newFile); 61 | } 62 | 63 | await fsp.copyFile(oldFile, newFile); 64 | 65 | return { 66 | outputFileObj: { _id: newFile }, 67 | outputNumber: 1, 68 | variables: args.variables, 69 | }; 70 | }; 71 | 72 | export { 73 | details, 74 | plugin, 75 | }; 76 | -------------------------------------------------------------------------------- /FlowPluginsTs/CommunityFlowPlugins/video/transcodeVideo/1.0.0/test.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HaveAGitGat/Tdarr_Plugins/2a68217292ebf35cd796151f561947a7e7655aab/FlowPluginsTs/CommunityFlowPlugins/video/transcodeVideo/1.0.0/test.ts -------------------------------------------------------------------------------- /FlowPluginsTs/FlowHelpers/1.0.0/hardwareUtils.test.ts: -------------------------------------------------------------------------------- 1 | import { getEncoder, IgetEncoder } from './hardwareUtils'; 2 | 3 | const baseInput = { 4 | targetCodec: 'h264', 5 | hardwareEncoding: true, 6 | hardwareType: 'auto', 7 | args: { 8 | workerType: 'transcodegpu', 9 | ffmpegPath: 'ffmpeg', 10 | jobLog: () => { 11 | // 12 | }, 13 | }, 14 | }; 15 | 16 | interface IcheckHardware { 17 | targetCodec:string | undefined, 18 | hardwareEncoding:boolean | undefined, 19 | hardwareType:string | undefined, 20 | ffmpegPath:string | undefined, 21 | } 22 | 23 | const checkHardware = async (settings:IcheckHardware):Promise => { 24 | const input = JSON.parse(JSON.stringify(baseInput)); 25 | 26 | input.args.jobLog = () => { 27 | // eslint-disable-next-line no-console 28 | // console.log(t); 29 | }; 30 | 31 | if (settings.targetCodec) { 32 | input.targetCodec = settings.targetCodec; 33 | } 34 | 35 | if (settings.hardwareEncoding) { 36 | input.hardwareEncoding = settings.hardwareEncoding; 37 | } 38 | 39 | if (settings.hardwareType) { 40 | input.hardwareType = settings.hardwareType; 41 | } 42 | 43 | if (settings.ffmpegPath) { 44 | input.args.ffmpegPath = settings.ffmpegPath; 45 | } 46 | 47 | const encoderProperties = await getEncoder(input); 48 | 49 | return encoderProperties; 50 | }; 51 | 52 | export default checkHardware; 53 | -------------------------------------------------------------------------------- /FlowPluginsTs/FlowHelpers/1.0.0/interfaces/flowUtils.ts: -------------------------------------------------------------------------------- 1 | import { IpluginInputArgs } from './interfaces'; 2 | 3 | // eslint-disable-next-line import/prefer-default-export 4 | export const checkFfmpegCommandInit = (args: IpluginInputArgs): void => { 5 | if (!args?.variables?.ffmpegCommand?.init) { 6 | throw new Error( 7 | 'FFmpeg command plugins not used correctly.' 8 | + ' Please use the "Begin Command" plugin before using this plugin.' 9 | + ' Afterwards, use the "Execute" plugin to execute the built FFmpeg command.' 10 | + ' Once the "Execute" plugin has been used, you need to use a new "Begin Command"' 11 | + ' plugin to start a new FFmpeg command.', 12 | ); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /FlowPluginsTs/FlowHelpers/1.0.0/interfaces/synced/jobInterface.ts: -------------------------------------------------------------------------------- 1 | interface Ijob { 2 | version: string, 3 | footprintId: string, 4 | jobId: string, 5 | start: number, 6 | type: string, 7 | fileId: string 8 | } 9 | 10 | export default Ijob; 11 | -------------------------------------------------------------------------------- /FlowPluginsTs/FlowHelpers/1.0.0/normJoinPath.ts: -------------------------------------------------------------------------------- 1 | const formatWindowsRootFolder = (path: string): string => { 2 | // Remove '.' from end of Windows root folder mapping e.g. 'E:.' 3 | if ( 4 | path.length === 3 5 | && path.charAt(1) === ':' 6 | && path.charAt(2) === '.' 7 | ) { 8 | // eslint-disable-next-line no-param-reassign 9 | path = path.slice(0, -1); 10 | } 11 | 12 | return path; 13 | }; 14 | 15 | const normJoinPath = ({ 16 | upath, 17 | paths, 18 | }:{ 19 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 20 | upath: any, 21 | paths: string[] 22 | }):string => { 23 | let path = upath.joinSafe(...paths); 24 | path = formatWindowsRootFolder(path); 25 | return path; 26 | }; 27 | 28 | export default normJoinPath; 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | 6 | # Tdarr_Plugins 7 | 8 | Visit the docs for more info: 9 | https://docs.tdarr.io/docs/plugins/basics 10 | 11 | 12 | ### Development 13 | 14 | Make sure NodeJS v16 is installed 15 | 16 | Install dependencies: 17 | 18 | `npm install` 19 | 20 | Run ESLint: 21 | 22 | `npm run lint:fix` 23 | 24 | Check plugins using some extra custom rules: 25 | 26 | `npm run checkPlugins` 27 | 28 | Run tests: 29 | 30 | `npm run test` 31 | 32 | 33 | # Steps to write a Tdarr Flow plugin: 34 | 35 | 1. Clone this repo 36 | 2. Set env variable `pluginsDir` to the location of the plugins repo and run Tdarr Server and Node. E.g. `export pluginsDir=C:/Tdarr_Plugins` 37 | 3. Browse the typescript plugins here https://github.com/HaveAGitGat/Tdarr_Plugins/tree/master/FlowPluginsTs/CommunityFlowPlugins and make edits locally or create a new one locally: 38 | 4. Make sure typescript is intalled with `npm i -g typescript` then run `tsc` to compile the changes. 39 | 5. Refresh the browser and Tdarr will pick up the changes 40 | 41 | Note, `pluginsDir` directories that contain a `.git` folder (such as when cloning this repo) will cause Tdarr to skip plugin updates to prevent overwriting development changes. 42 | 43 | 44 | -------------------------------------------------------------------------------- /examples/Tdarr_Plugin_a9he_New_file_size_check.js: -------------------------------------------------------------------------------- 1 | const details = () => ({ 2 | id: 'Tdarr_Plugin_a9he_New_file_size_check', 3 | Stage: 'Pre-processing', 4 | Name: 'New File Size Check', 5 | Type: 'Video', 6 | Operation: 'Transcode', 7 | Description: 'Give an error if new file is larger than the original \n\n', 8 | Version: '1.00', 9 | Tags: '', 10 | Inputs: [], 11 | }); 12 | 13 | const plugin = (file, librarySettings, inputs, otherArguments) => { 14 | const lib = require('../methods/lib')(); 15 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 16 | inputs = lib.loadDefaultValues(inputs, details); 17 | // Must return this object at some point in the function else plugin will fail. 18 | const response = { 19 | processFile: false, 20 | preset: '', 21 | handBrakeMode: false, 22 | FFmpegMode: true, 23 | reQueueAfter: true, 24 | infoLog: '', 25 | }; 26 | 27 | const newSize = file.file_size; 28 | const oldSize = otherArguments.originalLibraryFile.file_size; 29 | if (newSize > oldSize) { 30 | // Item will be errored in UI 31 | throw new Error(`Error! New file has size ${newSize} which is larger than original file ${oldSize}`); 32 | } else if (newSize < oldSize) { 33 | response.infoLog += `New file has size ${newSize} which is smaller than original file ${oldSize}`; 34 | } 35 | // if file sizes are exactly the same then file has not been transcoded yet 36 | 37 | return response; 38 | }; 39 | 40 | module.exports.details = details; 41 | module.exports.plugin = plugin; 42 | -------------------------------------------------------------------------------- /examples/Tdarr_Plugin_f001_Filter_Example.js: -------------------------------------------------------------------------------- 1 | const details = () => ({ 2 | id: 'Tdarr_Plugin_f001_Filter_Example', 3 | Stage: 'Pre-processing', 4 | Name: 'Filter Keywords', 5 | Type: 'Video', 6 | Operation: 'Filter', 7 | Description: 'This plugin prevents processing files which contain keywords \n\n', 8 | Version: '1.00', 9 | Tags: '', 10 | Inputs: [], 11 | }); 12 | 13 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 14 | const plugin = (file, librarySettings, inputs, otherArguments) => { 15 | const lib = require('../methods/lib')(); 16 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 17 | inputs = lib.loadDefaultValues(inputs, details); 18 | // Must return this object at some point in the function else plugin will fail. 19 | 20 | const response = { 21 | processFile: true, 22 | infoLog: '', 23 | }; 24 | 25 | const keywords = [ 26 | 'Low quality', 27 | ]; 28 | 29 | for (let i = 0; i < keywords.length; i += 1) { 30 | if (file.file.includes(keywords[i])) { 31 | response.processFile = false; 32 | response.infoLog += `Filter preventing processing. File title contains keyword ${keywords[i]}`; 33 | break; 34 | } 35 | } 36 | 37 | return response; 38 | }; 39 | module.exports.details = details; 40 | module.exports.plugin = plugin; 41 | -------------------------------------------------------------------------------- /examples/Tdarr_Plugin_f002_Filter_Example.js: -------------------------------------------------------------------------------- 1 | const details = () => ({ 2 | id: 'Tdarr_Plugin_f002_Filter_Example', 3 | Stage: 'Pre-processing', 4 | Name: 'Filter Resolutions', 5 | Type: 'Video', 6 | Operation: 'Filter', 7 | Description: 'This plugin prevents processing files with specified resolutions \n\n', 8 | Version: '1.00', 9 | Tags: '', 10 | Inputs: [], 11 | }); 12 | 13 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 14 | const plugin = (file, librarySettings, inputs, otherArguments) => { 15 | const lib = require('../methods/lib')(); 16 | // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign 17 | inputs = lib.loadDefaultValues(inputs, details); 18 | const response = { 19 | processFile: true, 20 | infoLog: '', 21 | }; 22 | 23 | const resolutionsToSkip = [ 24 | '1080p', 25 | '4KUHD', 26 | ]; 27 | 28 | for (let i = 0; i < resolutionsToSkip.length; i += 1) { 29 | if (file.video_resolution === resolutionsToSkip[i]) { 30 | response.processFile = false; 31 | response.infoLog += `Filter preventing processing. File has resolution ${resolutionsToSkip[i]}`; 32 | break; 33 | } 34 | } 35 | 36 | return response; 37 | }; 38 | 39 | module.exports.details = details; 40 | module.exports.plugin = plugin; 41 | -------------------------------------------------------------------------------- /examples/force_transcode_filter_notes: -------------------------------------------------------------------------------- 1 | // add COPYRIGHT=processed during video transcoding 2 | // -map 0 -c copy -c:v libx265 -metadata:s:v:0 COPYRIGHT=processed 3 | 4 | // check COPYRIGHT=processed metadata to see if file has been transcoded or not 5 | if(file.ffProbeData.streams[0]?.tags?.COPYRIGHT === 'processed'){ 6 | response.infoLog += 'File has already been transcoded \n'; 7 | response.processFile = false; 8 | return response; 9 | } -------------------------------------------------------------------------------- /methods/actions.js: -------------------------------------------------------------------------------- 1 | const importFresh = require('./node_modules/import-fresh'); 2 | 3 | module.exports.remuxContainer = importFresh( 4 | './library/actions/remuxContainer.js', 5 | ); 6 | module.exports.transcodeStandardiseAudioCodecs = importFresh( 7 | './library/actions/transcodeStandardiseAudioCodecs.js', 8 | ); 9 | module.exports.transcodeAddAudioStream = importFresh( 10 | './library/actions/transcodeAddAudioStream.js', 11 | ); 12 | module.exports.transcodeKeepOneAudioStream = importFresh( 13 | './library/actions/transcodeKeepOneAudioStream.js', 14 | ); 15 | -------------------------------------------------------------------------------- /methods/filters.js: -------------------------------------------------------------------------------- 1 | const importFresh = require('./node_modules/import-fresh'); 2 | 3 | module.exports.filterByAge = importFresh('./library/filters/filterByAge.js'); 4 | module.exports.filterByCodec = importFresh( 5 | './library/filters/filterByCodec.js', 6 | ); 7 | module.exports.filterByMedium = importFresh( 8 | './library/filters/filterByMedium.js', 9 | ); 10 | module.exports.filterByResolution = importFresh( 11 | './library/filters/filterByResolution.js', 12 | ); 13 | module.exports.filterBySize = importFresh('./library/filters/filterBySize.js'); 14 | module.exports.filterByBitrate = importFresh('./library/filters/filterByBitrate.js'); 15 | -------------------------------------------------------------------------------- /methods/lib.js: -------------------------------------------------------------------------------- 1 | const importFresh = require('./node_modules/import-fresh'); 2 | // load library modules fresh so no Tdarr Server restart required between plugin updates 3 | module.exports = () => importFresh('./library.js'); 4 | -------------------------------------------------------------------------------- /methods/library.js: -------------------------------------------------------------------------------- 1 | const importFresh = require('./node_modules/import-fresh'); 2 | 3 | module.exports.filters = importFresh('./filters.js'); 4 | module.exports.actions = importFresh('./actions.js'); 5 | module.exports.loadDefaultValues = importFresh('./loadDefaultValues.js'); 6 | -------------------------------------------------------------------------------- /methods/library/actions/remuxContainer.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | function remuxContainer(file, container) { 3 | try { 4 | if (file.container != container) { 5 | var response = { 6 | processFile: true, 7 | note: `File is not in ${container} \n`, 8 | }; 9 | return response; 10 | } else { 11 | var response = { 12 | processFile: false, 13 | note: `File is already in ${container} \n`, 14 | }; 15 | return response; 16 | } 17 | } catch (err) { 18 | var response = { 19 | processFile: false, 20 | note: `library.actions.remuxContainer error: ${err} \n`, 21 | }; 22 | return response; 23 | } 24 | } 25 | 26 | module.exports = remuxContainer; 27 | -------------------------------------------------------------------------------- /methods/library/actions/transcodeStandardiseAudioCodecs.js: -------------------------------------------------------------------------------- 1 | module.exports = (file, audioEncoder) => { 2 | // Function required responses 3 | // preset 4 | // processFile 5 | // note 6 | 7 | try { 8 | let audioIdx = -1; 9 | let hasNonSpecifiedAudioCodecStream = false; 10 | let ffmpegCommandInsert = ''; 11 | let audioCodec = audioEncoder; 12 | 13 | if (audioEncoder === 'dca') { 14 | audioCodec = 'dts'; 15 | } 16 | 17 | if (audioEncoder === 'libmp3lame') { 18 | audioCodec = 'mp3'; 19 | } 20 | 21 | for (let i = 0; i < file.ffProbeData.streams.length; i += 1) { 22 | try { 23 | if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'audio') { 24 | audioIdx += 1; 25 | } 26 | } catch (err) { 27 | // err 28 | } 29 | 30 | try { 31 | if ( 32 | file.ffProbeData.streams[i].codec_type.toLowerCase() === 'audio' 33 | && file.ffProbeData.streams[i].codec_name !== audioCodec 34 | ) { 35 | ffmpegCommandInsert += ` -c:a:${audioIdx} ${audioEncoder}`; 36 | hasNonSpecifiedAudioCodecStream = true; 37 | } 38 | } catch (err) { 39 | // err 40 | } 41 | } 42 | 43 | if (hasNonSpecifiedAudioCodecStream === true) { 44 | if (['dca', 'truehd'].includes(audioEncoder)) { 45 | ffmpegCommandInsert += ' -strict -2'; 46 | } 47 | return { 48 | preset: `,-map 0:v -map 0:a -map 0:s? -map 0:d? -c copy ${ffmpegCommandInsert}`, 49 | processFile: true, 50 | note: `File has audio streams which aren't in ${audioCodec} \n`, 51 | }; 52 | } 53 | 54 | return { 55 | preset: '', 56 | processFile: false, 57 | note: `File does not have any audio streams which aren't in ${audioCodec} \n`, 58 | }; 59 | } catch (err) { 60 | return { 61 | preset: '', 62 | processFile: false, 63 | note: `library.actions.transcodeStandardiseAudioCodecs error: ${err} \n`, 64 | }; 65 | } 66 | }; 67 | -------------------------------------------------------------------------------- /methods/library/filters/filterByAge.js: -------------------------------------------------------------------------------- 1 | const filterByAge = (file, ageCutOff_Seconds, type) => { 2 | try { 3 | const timeNow = new Date(); 4 | const dateCreated = new Date(file.statSync.birthtime); 5 | const fileAge = Math.round((timeNow - dateCreated) / 1000); 6 | 7 | if ((type === 'exclude' && fileAge > ageCutOff_Seconds) || (type === 'include' && fileAge < ageCutOff_Seconds)) { 8 | const response = { 9 | outcome: false, 10 | note: 'File creation date is not within specified requirement. Wont process. \n', 11 | }; 12 | return response; 13 | } 14 | 15 | const response = { 16 | outcome: true, 17 | note: 'File creation date is within specified requirement. Will process. \n', 18 | }; 19 | return response; 20 | } catch (err) { 21 | const response = { 22 | outcome: false, 23 | note: `library.filters.filterByAge error: ${err} \n`, 24 | }; 25 | return response; 26 | } 27 | }; 28 | 29 | module.exports = filterByAge; 30 | -------------------------------------------------------------------------------- /methods/library/filters/filterByBitrate.js: -------------------------------------------------------------------------------- 1 | const filterByBitrate = (file, lowerBound, upperBound) => { 2 | try { 3 | if ( 4 | file.bit_rate >= lowerBound 5 | && file.bit_rate <= upperBound 6 | ) { 7 | const response = { 8 | outcome: true, 9 | note: '☑File bitrate is within filter limits. \n', 10 | }; 11 | return response; 12 | } 13 | const response = { 14 | outcome: false, 15 | note: '☒File bitrate is not within filter limits. \n', 16 | }; 17 | return response; 18 | } catch (err) { 19 | const response = { 20 | outcome: false, 21 | note: `library.filters.filterByBitrate error: ${err} \n`, 22 | }; 23 | return response; 24 | } 25 | }; 26 | 27 | module.exports = filterByBitrate; 28 | -------------------------------------------------------------------------------- /methods/library/filters/filterByCodec.js: -------------------------------------------------------------------------------- 1 | const filterByCodec = (file, mode, codecs) => { 2 | try { 3 | // console.log(file,mode,codecs) 4 | 5 | const allCodecs = file.ffProbeData.streams.map((row) => row.codec_name); 6 | 7 | let included = false; 8 | 9 | for (let i = 0; i < allCodecs.length; i += 1) { 10 | if (codecs.toLowerCase().includes(allCodecs[i])) { 11 | included = true; 12 | } 13 | } 14 | 15 | if (mode === 'include') { 16 | if (included) { 17 | const response = { 18 | outcome: true, 19 | note: '☑Codec included \n', 20 | }; 21 | return response; 22 | } 23 | const response = { 24 | outcome: false, 25 | note: '☒Codec excluded \n', 26 | }; 27 | return response; 28 | } if (mode === 'exclude') { 29 | if (included) { 30 | const response = { 31 | outcome: false, 32 | note: '☒Codec excluded \n', 33 | }; 34 | return response; 35 | } 36 | const response = { 37 | outcome: true, 38 | note: '☑Codec not excluded \n', 39 | }; 40 | return response; 41 | } 42 | 43 | const response = { 44 | outcome: false, 45 | note: 'library.filters.filterByCodec error, no include/exclude specified \n', 46 | }; 47 | return response; 48 | } catch (err) { 49 | // eslint-disable-next-line no-console 50 | console.log(err); 51 | const response = { 52 | outcome: false, 53 | note: `Filter error hello! ${err}\n`, 54 | }; 55 | return response; 56 | } 57 | }; 58 | 59 | module.exports = filterByCodec; 60 | -------------------------------------------------------------------------------- /methods/library/filters/filterByMedium.js: -------------------------------------------------------------------------------- 1 | const filterByMedium = (file, medium) => { 2 | try { 3 | if (file.fileMedium !== medium) { 4 | const response = { 5 | outcome: false, 6 | note: `☒File is not ${medium} \n`, 7 | }; 8 | return response; 9 | } 10 | const response = { 11 | outcome: true, 12 | note: `☑File is ${medium} \n`, 13 | }; 14 | return response; 15 | } catch (err) { 16 | const response = { 17 | outcome: false, 18 | note: `library.filters.filterByMedium error: ${err} \n`, 19 | }; 20 | return response; 21 | } 22 | }; 23 | 24 | module.exports = filterByMedium; 25 | -------------------------------------------------------------------------------- /methods/library/filters/filterByResolution.js: -------------------------------------------------------------------------------- 1 | const filterByResolution = (file, mode, resolution) => { 2 | try { 3 | if (mode === 'exclude') { 4 | if ( 5 | resolution.toLowerCase().includes(file.video_resolution.toLowerCase()) 6 | ) { 7 | const response = { 8 | outcome: false, 9 | note: '☒File is in excluded resolution. \n', 10 | }; 11 | return response; 12 | } 13 | const response = { 14 | outcome: true, 15 | note: '☑File is not in excluded resolution. \n', 16 | }; 17 | return response; 18 | } if (mode === 'include') { 19 | if ( 20 | resolution.toLowerCase().includes(file.video_resolution.toLowerCase()) 21 | ) { 22 | const response = { 23 | outcome: true, 24 | note: '☑File is in included resolution. \n', 25 | }; 26 | return response; 27 | } 28 | const response = { 29 | outcome: false, 30 | note: '☒File is not in included resolution. \n', 31 | }; 32 | return response; 33 | } 34 | } catch (err) { 35 | const response = { 36 | outcome: false, 37 | note: `library.filters.filterByResolution error: ${err} \n`, 38 | }; 39 | return response; 40 | } 41 | 42 | throw new Error('Plugin error, no filter mode specified'); 43 | }; 44 | 45 | module.exports = filterByResolution; 46 | -------------------------------------------------------------------------------- /methods/library/filters/filterBySize.js: -------------------------------------------------------------------------------- 1 | const filterBySize = (file, lowerBound, upperBound) => { 2 | try { 3 | if ( 4 | file.file_size / 1000 >= lowerBound 5 | && file.file_size / 1000 <= upperBound 6 | ) { 7 | const response = { 8 | outcome: true, 9 | note: '☑File size is within filter limits. \n', 10 | }; 11 | return response; 12 | } 13 | const response = { 14 | outcome: false, 15 | note: '☒File size is not within filter limits. \n', 16 | }; 17 | return response; 18 | } catch (err) { 19 | const response = { 20 | outcome: false, 21 | note: `library.filters.filterBySize error: ${err} \n`, 22 | }; 23 | return response; 24 | } 25 | }; 26 | 27 | module.exports = filterBySize; 28 | -------------------------------------------------------------------------------- /methods/loadDefaultValues.js: -------------------------------------------------------------------------------- 1 | /* eslint no-param-reassign: 0 */ // --> OFF 2 | 3 | const loadDefaultValues = (inputs, details) => { 4 | if (!inputs) { 5 | inputs = {}; 6 | } 7 | 8 | const dets = details(); 9 | const defaultInputs = dets.Inputs || dets.inputs || []; 10 | for (let i = 0; i < defaultInputs.length; i += 1) { 11 | if (typeof inputs[defaultInputs[i].name] === 'string') { 12 | inputs[defaultInputs[i].name] = inputs[defaultInputs[i].name].trim(); 13 | } 14 | 15 | if (inputs[defaultInputs[i].name] === undefined 16 | || inputs[defaultInputs[i].name] === '' 17 | ) { 18 | inputs[defaultInputs[i].name] = defaultInputs[i].defaultValue; 19 | } 20 | 21 | // convert string to boolean else false 22 | if (defaultInputs[i].type === 'boolean') { 23 | inputs[defaultInputs[i].name] = !!(inputs[defaultInputs[i].name] === 'true' 24 | || inputs[defaultInputs[i].name] === true); 25 | } 26 | 27 | // convert string to number else 0 28 | if (defaultInputs[i].type === 'number') { 29 | inputs[defaultInputs[i].name] = Number(inputs[defaultInputs[i].name]); 30 | // eslint-disable-next-line no-restricted-globals 31 | if (isNaN(inputs[defaultInputs[i].name])) { 32 | inputs[defaultInputs[i].name] = 0; 33 | } 34 | } 35 | } 36 | return inputs; 37 | }; 38 | 39 | module.exports = loadDefaultValues; 40 | -------------------------------------------------------------------------------- /methods/node_modules/callsites/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const callsites = () => { 4 | const _prepareStackTrace = Error.prepareStackTrace; 5 | Error.prepareStackTrace = (_, stack) => stack; 6 | const stack = new Error().stack.slice(1); 7 | Error.prepareStackTrace = _prepareStackTrace; 8 | return stack; 9 | }; 10 | 11 | module.exports = callsites; 12 | // TODO: Remove this for the next major release 13 | module.exports.default = callsites; 14 | -------------------------------------------------------------------------------- /methods/node_modules/callsites/license: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Sindre Sorhus (sindresorhus.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /methods/node_modules/callsites/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_from": "callsites@^3.0.0", 3 | "_id": "callsites@3.1.0", 4 | "_inBundle": false, 5 | "_integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 6 | "_location": "/callsites", 7 | "_phantomChildren": {}, 8 | "_requested": { 9 | "type": "range", 10 | "registry": true, 11 | "raw": "callsites@^3.0.0", 12 | "name": "callsites", 13 | "escapedName": "callsites", 14 | "rawSpec": "^3.0.0", 15 | "saveSpec": null, 16 | "fetchSpec": "^3.0.0" 17 | }, 18 | "_requiredBy": [ 19 | "/parent-module" 20 | ], 21 | "_resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 22 | "_shasum": "b3630abd8943432f54b3f0519238e33cd7df2f73", 23 | "_spec": "callsites@^3.0.0", 24 | "_where": "C:\\Users\\H\\Documents\\GitHub\\Tdarr_Plugins\\methods\\node_modules\\parent-module", 25 | "author": { 26 | "name": "Sindre Sorhus", 27 | "email": "sindresorhus@gmail.com", 28 | "url": "sindresorhus.com" 29 | }, 30 | "bugs": { 31 | "url": "https://github.com/sindresorhus/callsites/issues" 32 | }, 33 | "bundleDependencies": false, 34 | "deprecated": false, 35 | "description": "Get callsites from the V8 stack trace API", 36 | "devDependencies": { 37 | "ava": "^1.4.1", 38 | "tsd": "^0.7.2", 39 | "xo": "^0.24.0" 40 | }, 41 | "engines": { 42 | "node": ">=6" 43 | }, 44 | "files": [ 45 | "index.js", 46 | "index.d.ts" 47 | ], 48 | "homepage": "https://github.com/sindresorhus/callsites#readme", 49 | "keywords": [ 50 | "stacktrace", 51 | "v8", 52 | "callsite", 53 | "callsites", 54 | "stack", 55 | "trace", 56 | "function", 57 | "file", 58 | "line", 59 | "debug" 60 | ], 61 | "license": "MIT", 62 | "name": "callsites", 63 | "repository": { 64 | "type": "git", 65 | "url": "git+https://github.com/sindresorhus/callsites.git" 66 | }, 67 | "scripts": { 68 | "test": "xo && ava && tsd" 69 | }, 70 | "version": "3.1.0" 71 | } 72 | -------------------------------------------------------------------------------- /methods/node_modules/callsites/readme.md: -------------------------------------------------------------------------------- 1 | # callsites [![Build Status](https://travis-ci.org/sindresorhus/callsites.svg?branch=master)](https://travis-ci.org/sindresorhus/callsites) 2 | 3 | > Get callsites from the [V8 stack trace API](https://v8.dev/docs/stack-trace-api) 4 | 5 | 6 | ## Install 7 | 8 | ``` 9 | $ npm install callsites 10 | ``` 11 | 12 | 13 | ## Usage 14 | 15 | ```js 16 | const callsites = require('callsites'); 17 | 18 | function unicorn() { 19 | console.log(callsites()[0].getFileName()); 20 | //=> '/Users/sindresorhus/dev/callsites/test.js' 21 | } 22 | 23 | unicorn(); 24 | ``` 25 | 26 | 27 | ## API 28 | 29 | Returns an array of callsite objects with the following methods: 30 | 31 | - `getThis`: returns the value of `this`. 32 | - `getTypeName`: returns the type of `this` as a string. This is the name of the function stored in the constructor field of `this`, if available, otherwise the object's `[[Class]]` internal property. 33 | - `getFunction`: returns the current function. 34 | - `getFunctionName`: returns the name of the current function, typically its `name` property. If a name property is not available an attempt will be made to try to infer a name from the function's context. 35 | - `getMethodName`: returns the name of the property of `this` or one of its prototypes that holds the current function. 36 | - `getFileName`: if this function was defined in a script returns the name of the script. 37 | - `getLineNumber`: if this function was defined in a script returns the current line number. 38 | - `getColumnNumber`: if this function was defined in a script returns the current column number 39 | - `getEvalOrigin`: if this function was created using a call to `eval` returns a string representing the location where `eval` was called. 40 | - `isToplevel`: is this a top-level invocation, that is, is this the global object? 41 | - `isEval`: does this call take place in code defined by a call to `eval`? 42 | - `isNative`: is this call in native V8 code? 43 | - `isConstructor`: is this a constructor call? 44 | 45 | 46 | ## License 47 | 48 | MIT © [Sindre Sorhus](https://sindresorhus.com) 49 | -------------------------------------------------------------------------------- /methods/node_modules/import-fresh/index.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | Import a module while bypassing the cache. 3 | 4 | @example 5 | ``` 6 | // foo.js 7 | let i = 0; 8 | module.exports = () => ++i; 9 | 10 | // index.js 11 | import importFresh = require('import-fresh'); 12 | 13 | require('./foo')(); 14 | //=> 1 15 | 16 | require('./foo')(); 17 | //=> 2 18 | 19 | importFresh('./foo')(); 20 | //=> 1 21 | 22 | importFresh('./foo')(); 23 | //=> 1 24 | 25 | const foo = importFresh('./foo'); 26 | ``` 27 | */ 28 | declare function importFresh(moduleId: string): T; 29 | 30 | export = importFresh; 31 | -------------------------------------------------------------------------------- /methods/node_modules/import-fresh/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const path = require('path'); 3 | const resolveFrom = require('resolve-from'); 4 | const parentModule = require('parent-module'); 5 | 6 | module.exports = moduleId => { 7 | if (typeof moduleId !== 'string') { 8 | throw new TypeError('Expected a string'); 9 | } 10 | 11 | const parentPath = parentModule(__filename); 12 | 13 | const cwd = parentPath ? path.dirname(parentPath) : __dirname; 14 | const filePath = resolveFrom(cwd, moduleId); 15 | 16 | const oldModule = require.cache[filePath]; 17 | // Delete itself from module parent 18 | if (oldModule && oldModule.parent) { 19 | let i = oldModule.parent.children.length; 20 | 21 | while (i--) { 22 | if (oldModule.parent.children[i].id === filePath) { 23 | oldModule.parent.children.splice(i, 1); 24 | } 25 | } 26 | } 27 | 28 | delete require.cache[filePath]; // Delete module from cache 29 | 30 | const parent = require.cache[parentPath]; // If `filePath` and `parentPath` are the same, cache will already be deleted so we won't get a memory leak in next step 31 | 32 | return parent === undefined ? require(filePath) : parent.require(filePath); // In case cache doesn't have parent, fall back to normal require 33 | }; 34 | -------------------------------------------------------------------------------- /methods/node_modules/import-fresh/license: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Sindre Sorhus (https://sindresorhus.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /methods/node_modules/import-fresh/readme.md: -------------------------------------------------------------------------------- 1 | # import-fresh 2 | 3 | > Import a module while bypassing the [cache](https://nodejs.org/api/modules.html#modules_caching) 4 | 5 | Useful for testing purposes when you need to freshly import a module. 6 | 7 | ## Install 8 | 9 | ``` 10 | $ npm install import-fresh 11 | ``` 12 | 13 | ## Usage 14 | 15 | ```js 16 | // foo.js 17 | let i = 0; 18 | module.exports = () => ++i; 19 | ``` 20 | 21 | ```js 22 | const importFresh = require('import-fresh'); 23 | 24 | require('./foo')(); 25 | //=> 1 26 | 27 | require('./foo')(); 28 | //=> 2 29 | 30 | importFresh('./foo')(); 31 | //=> 1 32 | 33 | importFresh('./foo')(); 34 | //=> 1 35 | ``` 36 | 37 | ## import-fresh for enterprise 38 | 39 | Available as part of the Tidelift Subscription. 40 | 41 | The maintainers of import-fresh and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-import-fresh?utm_source=npm-import-fresh&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) 42 | 43 | ## Related 44 | 45 | - [clear-module](https://github.com/sindresorhus/clear-module) - Clear a module from the import cache 46 | - [import-from](https://github.com/sindresorhus/import-from) - Import a module from a given path 47 | - [import-cwd](https://github.com/sindresorhus/import-cwd) - Import a module from the current working directory 48 | - [import-lazy](https://github.com/sindresorhus/import-lazy) - Import modules lazily 49 | -------------------------------------------------------------------------------- /methods/node_modules/parent-module/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const callsites = require('callsites'); 3 | 4 | module.exports = filepath => { 5 | const stacks = callsites(); 6 | 7 | if (!filepath) { 8 | return stacks[2].getFileName(); 9 | } 10 | 11 | let seenVal = false; 12 | 13 | // Skip the first stack as it's this function 14 | stacks.shift(); 15 | 16 | for (const stack of stacks) { 17 | const parentFilepath = stack.getFileName(); 18 | 19 | if (typeof parentFilepath !== 'string') { 20 | continue; 21 | } 22 | 23 | if (parentFilepath === filepath) { 24 | seenVal = true; 25 | continue; 26 | } 27 | 28 | // Skip native modules 29 | if (parentFilepath === 'module.js') { 30 | continue; 31 | } 32 | 33 | if (seenVal && parentFilepath !== filepath) { 34 | return parentFilepath; 35 | } 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /methods/node_modules/parent-module/license: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Sindre Sorhus (sindresorhus.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /methods/node_modules/parent-module/readme.md: -------------------------------------------------------------------------------- 1 | # parent-module [![Build Status](https://travis-ci.org/sindresorhus/parent-module.svg?branch=master)](https://travis-ci.org/sindresorhus/parent-module) 2 | 3 | > Get the path of the parent module 4 | 5 | Node.js exposes `module.parent`, but it only gives you the first cached parent, which is not necessarily the actual parent. 6 | 7 | 8 | ## Install 9 | 10 | ``` 11 | $ npm install parent-module 12 | ``` 13 | 14 | 15 | ## Usage 16 | 17 | ```js 18 | // bar.js 19 | const parentModule = require('parent-module'); 20 | 21 | module.exports = () => { 22 | console.log(parentModule()); 23 | //=> '/Users/sindresorhus/dev/unicorn/foo.js' 24 | }; 25 | ``` 26 | 27 | ```js 28 | // foo.js 29 | const bar = require('./bar'); 30 | 31 | bar(); 32 | ``` 33 | 34 | 35 | ## API 36 | 37 | ### parentModule([filepath]) 38 | 39 | By default, it will return the path of the immediate parent. 40 | 41 | #### filepath 42 | 43 | Type: `string`
44 | Default: [`__filename`](https://nodejs.org/api/globals.html#globals_filename) 45 | 46 | Filepath of the module of which to get the parent path. 47 | 48 | Useful if you want it to work [multiple module levels down](https://github.com/sindresorhus/parent-module/tree/master/fixtures/filepath). 49 | 50 | 51 | ## Tip 52 | 53 | Combine it with [`read-pkg-up`](https://github.com/sindresorhus/read-pkg-up) to read the package.json of the parent module. 54 | 55 | ```js 56 | const path = require('path'); 57 | const readPkgUp = require('read-pkg-up'); 58 | const parentModule = require('parent-module'); 59 | 60 | console.log(readPkgUp.sync({cwd: path.dirname(parentModule())}).pkg); 61 | //=> {name: 'chalk', version: '1.0.0', …} 62 | ``` 63 | 64 | 65 | ## License 66 | 67 | MIT © [Sindre Sorhus](https://sindresorhus.com) 68 | -------------------------------------------------------------------------------- /methods/node_modules/resolve-from/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const path = require('path'); 3 | const Module = require('module'); 4 | const fs = require('fs'); 5 | 6 | const resolveFrom = (fromDir, moduleId, silent) => { 7 | if (typeof fromDir !== 'string') { 8 | throw new TypeError(`Expected \`fromDir\` to be of type \`string\`, got \`${typeof fromDir}\``); 9 | } 10 | 11 | if (typeof moduleId !== 'string') { 12 | throw new TypeError(`Expected \`moduleId\` to be of type \`string\`, got \`${typeof moduleId}\``); 13 | } 14 | 15 | try { 16 | fromDir = fs.realpathSync(fromDir); 17 | } catch (err) { 18 | if (err.code === 'ENOENT') { 19 | fromDir = path.resolve(fromDir); 20 | } else if (silent) { 21 | return null; 22 | } else { 23 | throw err; 24 | } 25 | } 26 | 27 | const fromFile = path.join(fromDir, 'noop.js'); 28 | 29 | const resolveFileName = () => Module._resolveFilename(moduleId, { 30 | id: fromFile, 31 | filename: fromFile, 32 | paths: Module._nodeModulePaths(fromDir) 33 | }); 34 | 35 | if (silent) { 36 | try { 37 | return resolveFileName(); 38 | } catch (err) { 39 | return null; 40 | } 41 | } 42 | 43 | return resolveFileName(); 44 | }; 45 | 46 | module.exports = (fromDir, moduleId) => resolveFrom(fromDir, moduleId); 47 | module.exports.silent = (fromDir, moduleId) => resolveFrom(fromDir, moduleId, true); 48 | -------------------------------------------------------------------------------- /methods/node_modules/resolve-from/license: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Sindre Sorhus (sindresorhus.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /methods/node_modules/resolve-from/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_from": "resolve-from@^4.0.0", 3 | "_id": "resolve-from@4.0.0", 4 | "_inBundle": false, 5 | "_integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 6 | "_location": "/resolve-from", 7 | "_phantomChildren": {}, 8 | "_requested": { 9 | "type": "range", 10 | "registry": true, 11 | "raw": "resolve-from@^4.0.0", 12 | "name": "resolve-from", 13 | "escapedName": "resolve-from", 14 | "rawSpec": "^4.0.0", 15 | "saveSpec": null, 16 | "fetchSpec": "^4.0.0" 17 | }, 18 | "_requiredBy": [ 19 | "/import-fresh" 20 | ], 21 | "_resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 22 | "_shasum": "4abcd852ad32dd7baabfe9b40e00a36db5f392e6", 23 | "_spec": "resolve-from@^4.0.0", 24 | "_where": "C:\\Users\\H\\Documents\\GitHub\\Tdarr_Plugins\\methods\\node_modules\\import-fresh", 25 | "author": { 26 | "name": "Sindre Sorhus", 27 | "email": "sindresorhus@gmail.com", 28 | "url": "sindresorhus.com" 29 | }, 30 | "bugs": { 31 | "url": "https://github.com/sindresorhus/resolve-from/issues" 32 | }, 33 | "bundleDependencies": false, 34 | "deprecated": false, 35 | "description": "Resolve the path of a module like `require.resolve()` but from a given path", 36 | "devDependencies": { 37 | "ava": "*", 38 | "xo": "*" 39 | }, 40 | "engines": { 41 | "node": ">=4" 42 | }, 43 | "files": [ 44 | "index.js" 45 | ], 46 | "homepage": "https://github.com/sindresorhus/resolve-from#readme", 47 | "keywords": [ 48 | "require", 49 | "resolve", 50 | "path", 51 | "module", 52 | "from", 53 | "like", 54 | "import" 55 | ], 56 | "license": "MIT", 57 | "name": "resolve-from", 58 | "repository": { 59 | "type": "git", 60 | "url": "git+https://github.com/sindresorhus/resolve-from.git" 61 | }, 62 | "scripts": { 63 | "test": "xo && ava" 64 | }, 65 | "version": "4.0.0" 66 | } 67 | -------------------------------------------------------------------------------- /methods/node_modules/resolve-from/readme.md: -------------------------------------------------------------------------------- 1 | # resolve-from [![Build Status](https://travis-ci.org/sindresorhus/resolve-from.svg?branch=master)](https://travis-ci.org/sindresorhus/resolve-from) 2 | 3 | > Resolve the path of a module like [`require.resolve()`](https://nodejs.org/api/globals.html#globals_require_resolve) but from a given path 4 | 5 | 6 | ## Install 7 | 8 | ``` 9 | $ npm install resolve-from 10 | ``` 11 | 12 | 13 | ## Usage 14 | 15 | ```js 16 | const resolveFrom = require('resolve-from'); 17 | 18 | // There is a file at `./foo/bar.js` 19 | 20 | resolveFrom('foo', './bar'); 21 | //=> '/Users/sindresorhus/dev/test/foo/bar.js' 22 | ``` 23 | 24 | 25 | ## API 26 | 27 | ### resolveFrom(fromDir, moduleId) 28 | 29 | Like `require()`, throws when the module can't be found. 30 | 31 | ### resolveFrom.silent(fromDir, moduleId) 32 | 33 | Returns `null` instead of throwing when the module can't be found. 34 | 35 | #### fromDir 36 | 37 | Type: `string` 38 | 39 | Directory to resolve from. 40 | 41 | #### moduleId 42 | 43 | Type: `string` 44 | 45 | What you would use in `require()`. 46 | 47 | 48 | ## Tip 49 | 50 | Create a partial using a bound function if you want to resolve from the same `fromDir` multiple times: 51 | 52 | ```js 53 | const resolveFromFoo = resolveFrom.bind(null, 'foo'); 54 | 55 | resolveFromFoo('./bar'); 56 | resolveFromFoo('./baz'); 57 | ``` 58 | 59 | 60 | ## Related 61 | 62 | - [resolve-cwd](https://github.com/sindresorhus/resolve-cwd) - Resolve the path of a module from the current working directory 63 | - [import-from](https://github.com/sindresorhus/import-from) - Import a module from a given path 64 | - [import-cwd](https://github.com/sindresorhus/import-cwd) - Import a module from the current working directory 65 | - [resolve-pkg](https://github.com/sindresorhus/resolve-pkg) - Resolve the path of a package regardless of it having an entry point 66 | - [import-lazy](https://github.com/sindresorhus/import-lazy) - Import a module lazily 67 | - [resolve-global](https://github.com/sindresorhus/resolve-global) - Resolve the path of a globally installed module 68 | 69 | 70 | ## License 71 | 72 | MIT © [Sindre Sorhus](https://sindresorhus.com) 73 | -------------------------------------------------------------------------------- /methods/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "methods", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "callsites": { 8 | "version": "3.1.0", 9 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 10 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" 11 | }, 12 | "import-fresh": { 13 | "version": "3.3.0", 14 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", 15 | "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", 16 | "requires": { 17 | "parent-module": "^1.0.0", 18 | "resolve-from": "^4.0.0" 19 | } 20 | }, 21 | "parent-module": { 22 | "version": "1.0.1", 23 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 24 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 25 | "requires": { 26 | "callsites": "^3.0.0" 27 | } 28 | }, 29 | "resolve-from": { 30 | "version": "4.0.0", 31 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 32 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /methods/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "methods", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "library.js", 6 | "scripts": {}, 7 | "author": "", 8 | "license": "ISC", 9 | "dependencies": { 10 | "import-fresh": "^3.3.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /methods/utils.js: -------------------------------------------------------------------------------- 1 | const strHasValue = (inputsArr, value, exactMatch) => { 2 | let contains = false; 3 | 4 | for (let j = 0; j < inputsArr.length; j += 1) { 5 | try { 6 | if ( 7 | (exactMatch && inputsArr[j] === String(value)) 8 | || (!exactMatch && String(value).includes(inputsArr[j]))) { 9 | contains = true; 10 | break; 11 | } 12 | } catch (err) { 13 | // eslint-disable-next-line no-console 14 | console.log(err); 15 | } 16 | } 17 | 18 | return contains; 19 | }; 20 | 21 | module.exports = { 22 | strHasValue, 23 | }; 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tdarr_plugins", 3 | "version": "1.0.0", 4 | "description": "Tdar Plugins Repo", 5 | "main": "", 6 | "dependencies": { 7 | "chalk": "^4.1.2", 8 | "import-fresh": "^3.3.0", 9 | "lodash": "^4.17.21" 10 | }, 11 | "devDependencies": { 12 | "@types/node": "^20.5.1", 13 | "@typescript-eslint/eslint-plugin": "^4.14.1", 14 | "@typescript-eslint/parser": "^4.14.1", 15 | "chai": "^4.3.6", 16 | "eslint": "^7.14.0", 17 | "eslint-config-airbnb-base": "^14.2.1", 18 | "eslint-plugin-import": "^2.22.1", 19 | "eslint-plugin-jsx-a11y": "^6.4.1", 20 | "eslint-plugin-prefer-arrow-functions": "^3.1.4", 21 | "eslint_d": "^11.1.1" 22 | }, 23 | "scripts": { 24 | "test": "node ./tests/runTests.js", 25 | "lint": "eslint_d FlowPluginsTs Community methods examples tests --ext js,ts", 26 | "lint:fix": "eslint_d FlowPluginsTs Community methods examples tests --ext js,ts --fix", 27 | "checkPlugins": "node ./tests/checkPlugins.js" 28 | }, 29 | "repository": { 30 | "type": "git", 31 | "url": "git+https://github.com/HaveAGitGat/Tdarr_Plugins.git" 32 | }, 33 | "author": "", 34 | "license": "ISC", 35 | "bugs": { 36 | "url": "https://github.com/HaveAGitGat/Tdarr_Plugins/issues" 37 | }, 38 | "homepage": "https://github.com/HaveAGitGat/Tdarr_Plugins#readme" 39 | } 40 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_00td_action_remux_container.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const run = require('../helpers/run'); 3 | 4 | const tests = [ 5 | { 6 | input: { 7 | file: require('../sampleData/media/sampleH264_1.json'), 8 | librarySettings: {}, 9 | inputs: {}, 10 | otherArguments: {}, 11 | }, 12 | output: { 13 | processFile: true, 14 | preset: ', -map 0 -c copy', 15 | container: '.mkv', 16 | handBrakeMode: false, 17 | FFmpegMode: true, 18 | reQueueAfter: true, 19 | infoLog: 'File is not in mkv \n', 20 | handbrakeMode: false, 21 | }, 22 | }, 23 | { 24 | input: { 25 | file: require('../sampleData/media/sampleH264_1.json'), 26 | librarySettings: {}, 27 | inputs: { 28 | container: 'mkv', 29 | }, 30 | otherArguments: {}, 31 | }, 32 | output: { 33 | processFile: true, 34 | preset: ', -map 0 -c copy', 35 | container: '.mkv', 36 | handBrakeMode: false, 37 | FFmpegMode: true, 38 | reQueueAfter: true, 39 | infoLog: 'File is not in mkv \n', 40 | handbrakeMode: false, 41 | }, 42 | }, 43 | { 44 | input: { 45 | file: require('../sampleData/media/sampleH264_1.json'), 46 | librarySettings: {}, 47 | inputs: { 48 | container: 'mp4', 49 | }, 50 | otherArguments: {}, 51 | }, 52 | output: { 53 | processFile: false, 54 | preset: ', -map 0 -c copy', 55 | container: '.mp4', 56 | handBrakeMode: false, 57 | FFmpegMode: true, 58 | reQueueAfter: true, 59 | infoLog: 'File is already in mp4 \n', 60 | handbrakeMode: false, 61 | }, 62 | }, 63 | ]; 64 | 65 | void run(tests); 66 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_00td_action_standardise_audio_stream_codecs.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const run = require('../helpers/run'); 3 | 4 | const tests = [ 5 | { 6 | input: { 7 | file: require('../sampleData/media/sampleH264_1.json'), 8 | librarySettings: {}, 9 | inputs: {}, 10 | otherArguments: {}, 11 | }, 12 | output: { 13 | processFile: false, 14 | preset: '', 15 | container: '.mp4', 16 | handBrakeMode: false, 17 | FFmpegMode: false, 18 | reQueueAfter: true, 19 | infoLog: "File does not have any audio streams which aren't in aac \n", 20 | handbrakeMode: false, 21 | ffmpegMode: true, 22 | }, 23 | }, 24 | { 25 | input: { 26 | file: require('../sampleData/media/sampleH264_2.json'), 27 | librarySettings: {}, 28 | inputs: {}, 29 | otherArguments: {}, 30 | }, 31 | output: { 32 | processFile: true, 33 | preset: ',-map 0:v -map 0:a -map 0:s? -map 0:d? -c copy -c:a:0 aac -c:a:1 aac -c:a:2 aac', 34 | container: '.mkv', 35 | handBrakeMode: false, 36 | FFmpegMode: false, 37 | reQueueAfter: true, 38 | infoLog: "File has audio streams which aren't in aac \n", 39 | handbrakeMode: false, 40 | ffmpegMode: true, 41 | }, 42 | }, 43 | { 44 | input: { 45 | file: require('../sampleData/media/sampleH264_2.json'), 46 | librarySettings: {}, 47 | inputs: { 48 | audioCodec: 'eac3', 49 | }, 50 | otherArguments: {}, 51 | }, 52 | output: { 53 | processFile: true, 54 | preset: ',-map 0:v -map 0:a -map 0:s? -map 0:d? -c copy -c:a:0 eac3 -c:a:1 eac3 -c:a:3 eac3 -c:a:4 eac3', 55 | container: '.mkv', 56 | handBrakeMode: false, 57 | FFmpegMode: false, 58 | reQueueAfter: true, 59 | infoLog: "File has audio streams which aren't in eac3 \n", 60 | handbrakeMode: false, 61 | ffmpegMode: true, 62 | }, 63 | }, 64 | ]; 65 | 66 | void run(tests); 67 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_00td_filter_break_stack_if_processed.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: { 12 | originalLibraryFile: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')), 13 | }, 14 | }, 15 | output: { 16 | processFile: true, 17 | infoLog: 'File has not been processed yet. Continuing to next plugin.', 18 | }, 19 | }, 20 | { 21 | input: { 22 | file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')), 23 | librarySettings: {}, 24 | inputs: {}, 25 | otherArguments: { 26 | originalLibraryFile: (() => { 27 | const file = _.cloneDeep(require('../sampleData/media/sampleH265_1.json')); 28 | file.file += 'test'; 29 | return file; 30 | })(), 31 | }, 32 | }, 33 | output: { 34 | processFile: false, 35 | infoLog: 'File has been processed, breaking out of plugin stack.', 36 | }, 37 | }, 38 | ]; 39 | 40 | void run(tests); 41 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_00td_filter_by_bitrate.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const run = require('../helpers/run'); 3 | 4 | const tests = [ 5 | { 6 | input: { 7 | file: require('../sampleData/media/sampleH264_2.json'), 8 | librarySettings: {}, 9 | inputs: {}, 10 | otherArguments: {}, 11 | }, 12 | output: { 13 | processFile: true, 14 | infoLog: '☑File bitrate is within filter limits. Moving to next plugin.', 15 | }, 16 | }, 17 | { 18 | input: { 19 | file: require('../sampleData/media/sampleH264_1.json'), 20 | librarySettings: {}, 21 | inputs: { 22 | upperBound: 500, 23 | lowerBound: 0, 24 | }, 25 | otherArguments: {}, 26 | }, 27 | output: { 28 | processFile: false, 29 | infoLog: '☒File bitrate is not within filter limits. Breaking out of plugin stack.\n', 30 | }, 31 | }, 32 | { 33 | input: { 34 | file: require('../sampleData/media/sampleH264_1.json'), 35 | librarySettings: {}, 36 | inputs: { 37 | upperBound: 10000, 38 | lowerBound: 0, 39 | }, 40 | otherArguments: {}, 41 | }, 42 | output: { 43 | processFile: true, 44 | infoLog: '☑File bitrate is within filter limits. Moving to next plugin.', 45 | }, 46 | }, 47 | { 48 | input: { 49 | file: require('../sampleData/media/sampleH264_1.json'), 50 | librarySettings: {}, 51 | inputs: { 52 | upperBound: 10000, 53 | lowerBound: 9000, 54 | }, 55 | otherArguments: {}, 56 | }, 57 | output: { 58 | processFile: false, 59 | infoLog: '☒File bitrate is not within filter limits. Breaking out of plugin stack.\n', 60 | }, 61 | }, 62 | ]; 63 | 64 | void run(tests); 65 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_00td_filter_by_codec.js: -------------------------------------------------------------------------------- 1 | const run = require('../helpers/run'); 2 | 3 | const tests = [ 4 | { 5 | input: { 6 | file: require('../sampleData/media/sampleH264_1.json'), 7 | librarySettings: {}, 8 | inputs: {}, 9 | otherArguments: {}, 10 | }, 11 | output: { processFile: false, infoLog: '' }, 12 | }, 13 | { 14 | input: { 15 | file: require('../sampleData/media/sampleH264_1.json'), 16 | librarySettings: {}, 17 | inputs: { 18 | codecsToProcess: 'h264', 19 | }, 20 | otherArguments: {}, 21 | }, 22 | output: { processFile: true, infoLog: 'File is in codecsToProcess. Moving to next plugin.' }, 23 | }, 24 | { 25 | input: { 26 | file: require('../sampleData/media/sampleH264_1.json'), 27 | librarySettings: {}, 28 | inputs: { 29 | codecsToProcess: 'h265', 30 | }, 31 | otherArguments: {}, 32 | }, 33 | output: { processFile: false, infoLog: 'File is not in codecsToProcess. Breaking out of plugin stack.' }, 34 | }, 35 | { 36 | input: { 37 | file: require('../sampleData/media/sampleH264_1.json'), 38 | librarySettings: {}, 39 | inputs: { 40 | codecsToNotProcess: 'h264', 41 | }, 42 | otherArguments: {}, 43 | }, 44 | output: { processFile: false, infoLog: 'File is in codecsToNotProcess. Breaking out of plugin stack.' }, 45 | }, 46 | { 47 | input: { 48 | file: require('../sampleData/media/sampleH264_1.json'), 49 | librarySettings: {}, 50 | inputs: { 51 | codecsToNotProcess: 'h265', 52 | }, 53 | otherArguments: {}, 54 | }, 55 | output: { processFile: true, infoLog: 'File is not in codecsToNotProcess. Moving to next plugin.' }, 56 | }, 57 | ]; 58 | 59 | void run(tests); 60 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_00td_filter_by_codec_tag_string.js: -------------------------------------------------------------------------------- 1 | const run = require('../helpers/run'); 2 | 3 | const tests = [ 4 | { 5 | input: { 6 | file: require('../sampleData/media/sampleH264_1.json'), 7 | librarySettings: {}, 8 | inputs: {}, 9 | otherArguments: {}, 10 | }, 11 | output: { processFile: false, infoLog: '' }, 12 | }, 13 | { 14 | input: { 15 | file: require('../sampleData/media/sampleH264_1.json'), 16 | librarySettings: {}, 17 | inputs: { 18 | codecTagStringsToProcess: 'avc1,rand', 19 | }, 20 | otherArguments: {}, 21 | }, 22 | output: { processFile: true, infoLog: 'File is in codecTagStringsToProcess. Moving to next plugin.' }, 23 | }, 24 | { 25 | input: { 26 | file: require('../sampleData/media/sampleH264_1.json'), 27 | librarySettings: {}, 28 | inputs: { 29 | codecTagStringsToNotProcess: 'avc1,rand', 30 | }, 31 | otherArguments: {}, 32 | }, 33 | output: { processFile: false, infoLog: 'File is in codecTagStringsToNotProcess. Breaking out of plugin stack.' }, 34 | }, 35 | { 36 | input: { 37 | file: require('../sampleData/media/sampleH265_1.json'), 38 | librarySettings: {}, 39 | inputs: { 40 | codecTagStringsToProcess: 'avc1,rand', 41 | }, 42 | otherArguments: {}, 43 | }, 44 | output: { processFile: false, infoLog: 'File is not in codecTagStringsToProcess. Breaking out of plugin stack.' }, 45 | }, 46 | { 47 | input: { 48 | file: require('../sampleData/media/sampleH265_1.json'), 49 | librarySettings: {}, 50 | inputs: { 51 | codecTagStringsToNotProcess: 'avc1,rand', 52 | }, 53 | otherArguments: {}, 54 | }, 55 | output: { processFile: true, infoLog: 'File is not in codecTagStringsToNotProcess. Moving to next plugin.' }, 56 | }, 57 | ]; 58 | 59 | void run(tests); 60 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_00td_filter_by_resolution.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const run = require('../helpers/run'); 3 | 4 | const tests = [ 5 | { 6 | input: { 7 | file: require('../sampleData/media/sampleH264_1.json'), 8 | librarySettings: {}, 9 | inputs: {}, 10 | otherArguments: {}, 11 | }, 12 | output: { 13 | processFile: false, infoLog: '', 14 | }, 15 | }, 16 | { 17 | input: { 18 | file: require('../sampleData/media/sampleH264_1.json'), 19 | librarySettings: {}, 20 | inputs: { 21 | resolutionsToProcess: '480p,720p', 22 | }, 23 | otherArguments: {}, 24 | }, 25 | output: { 26 | processFile: true, 27 | infoLog: 'File is in resolutionsToProcess. Moving to next plugin.', 28 | }, 29 | }, 30 | { 31 | input: { 32 | file: require('../sampleData/media/sampleH264_1.json'), 33 | librarySettings: {}, 34 | inputs: { 35 | resolutionsToProcess: '480p,1080p', 36 | }, 37 | otherArguments: {}, 38 | }, 39 | output: { 40 | processFile: false, 41 | infoLog: 'File is not in resolutionsToProcess. Breaking out of plugin stack.', 42 | }, 43 | }, 44 | { 45 | input: { 46 | file: require('../sampleData/media/sampleH264_1.json'), 47 | librarySettings: {}, 48 | inputs: { 49 | resolutionsToNotProcess: '480p,720p', 50 | }, 51 | otherArguments: {}, 52 | }, 53 | output: { 54 | processFile: false, 55 | infoLog: 'File is in resolutionsToNotProcess. Breaking out of plugin stack.', 56 | }, 57 | }, 58 | { 59 | input: { 60 | file: require('../sampleData/media/sampleH264_1.json'), 61 | librarySettings: {}, 62 | inputs: { 63 | resolutionsToNotProcess: '480p,1080p', 64 | }, 65 | otherArguments: {}, 66 | }, 67 | output: { 68 | processFile: true, 69 | infoLog: 'File is not in resolutionsToNotProcess. Moving to next plugin.', 70 | }, 71 | }, 72 | ]; 73 | 74 | void run(tests); 75 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_00td_filter_by_size.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const run = require('../helpers/run'); 3 | 4 | const tests = [ 5 | { 6 | input: { 7 | file: require('../sampleData/media/sampleH264_1.json'), 8 | librarySettings: {}, 9 | inputs: {}, 10 | otherArguments: {}, 11 | }, 12 | output: { 13 | processFile: true, 14 | infoLog: 'File is within lower and upper bound size limits. Moving to next plugin.', 15 | }, 16 | }, 17 | { 18 | input: { 19 | file: require('../sampleData/media/sampleH264_1.json'), 20 | librarySettings: {}, 21 | inputs: { 22 | upperBound: 0.5, 23 | lowerBound: 0, 24 | }, 25 | otherArguments: {}, 26 | }, 27 | output: { 28 | processFile: false, 29 | infoLog: 'File is not within lower and upper bound size limits. Breaking out of plugin stack.', 30 | }, 31 | }, 32 | { 33 | input: { 34 | file: require('../sampleData/media/sampleH264_1.json'), 35 | librarySettings: {}, 36 | inputs: { 37 | upperBound: 2, 38 | lowerBound: 0, 39 | }, 40 | otherArguments: {}, 41 | }, 42 | output: { 43 | processFile: true, 44 | infoLog: 'File is within lower and upper bound size limits. Moving to next plugin.', 45 | }, 46 | }, 47 | { 48 | input: { 49 | file: require('../sampleData/media/sampleH264_1.json'), 50 | librarySettings: {}, 51 | inputs: { 52 | upperBound: 4, 53 | lowerBound: 2, 54 | }, 55 | otherArguments: {}, 56 | }, 57 | output: { 58 | processFile: false, 59 | infoLog: 'File is not within lower and upper bound size limits. Breaking out of plugin stack.', 60 | }, 61 | }, 62 | ]; 63 | 64 | void run(tests); 65 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_075a_FFMPEG_HEVC_Generic.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const run = require('../helpers/run'); 3 | 4 | const tests = [ 5 | { 6 | input: { 7 | file: require('../sampleData/media/sampleH264_1.json'), 8 | librarySettings: {}, 9 | inputs: {}, 10 | otherArguments: {}, 11 | }, 12 | output: { 13 | processFile: true, 14 | preset: ',-map 0:v -map 0:a -map 0:s? -map 0:d? -c copy -c:v:0 libx265 -max_muxing_queue_size 9999', 15 | container: '.mkv', 16 | handBrakeMode: false, 17 | FFmpegMode: true, 18 | reQueueAfter: true, 19 | infoLog: '☑File is a video! \n☒File is not hevc! \n', 20 | }, 21 | }, 22 | { 23 | input: { 24 | file: require('../sampleData/media/sampleH265_1.json'), 25 | librarySettings: {}, 26 | inputs: {}, 27 | otherArguments: {}, 28 | }, 29 | output: { 30 | processFile: false, 31 | preset: '', 32 | container: '.mp4', 33 | handBrakeMode: false, 34 | FFmpegMode: false, 35 | reQueueAfter: false, 36 | infoLog: '☑File is a video! \n☑File is already in hevc! \n', 37 | }, 38 | }, 39 | ]; 40 | 41 | void run(tests); 42 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_075b_FFMPEG_HEVC_Generic_Video_Audio_Only.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const run = require('../helpers/run'); 3 | 4 | const tests = [ 5 | { 6 | input: { 7 | file: require('../sampleData/media/sampleH264_1.json'), 8 | librarySettings: {}, 9 | inputs: {}, 10 | otherArguments: {}, 11 | }, 12 | output: { 13 | processFile: true, 14 | preset: ',-map 0:v -map 0:a -c copy -c:v:0 libx265 -max_muxing_queue_size 9999', 15 | container: '.mkv', 16 | handBrakeMode: false, 17 | FFmpegMode: true, 18 | reQueueAfter: true, 19 | infoLog: '☑File is a video! \n☒File is not hevc! \n', 20 | }, 21 | }, 22 | { 23 | input: { 24 | file: require('../sampleData/media/sampleH265_1.json'), 25 | librarySettings: {}, 26 | inputs: {}, 27 | otherArguments: {}, 28 | }, 29 | output: { 30 | processFile: false, 31 | preset: '', 32 | container: '.mp4', 33 | handBrakeMode: false, 34 | FFmpegMode: false, 35 | reQueueAfter: false, 36 | infoLog: '☑File is a video! \n☑File is already in hevc! \n', 37 | }, 38 | }, 39 | ]; 40 | 41 | void run(tests); 42 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_075c_FFMPEG_HEVC_Generic_Video_Audio_Only_CRF20.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const run = require('../helpers/run'); 3 | 4 | const tests = [ 5 | { 6 | input: { 7 | file: require('../sampleData/media/sampleH264_1.json'), 8 | librarySettings: {}, 9 | inputs: {}, 10 | otherArguments: {}, 11 | }, 12 | output: { 13 | processFile: true, 14 | preset: ',-map 0:v -map 0:a -c copy -c:v:0 libx265 -crf 20', 15 | container: '.mkv', 16 | handBrakeMode: false, 17 | FFmpegMode: true, 18 | reQueueAfter: true, 19 | infoLog: '☑File is a video! \n☒File is not hevc! \n', 20 | }, 21 | }, 22 | { 23 | input: { 24 | file: require('../sampleData/media/sampleH265_1.json'), 25 | librarySettings: {}, 26 | inputs: {}, 27 | otherArguments: {}, 28 | }, 29 | output: { 30 | processFile: false, 31 | preset: '', 32 | container: '.mp4', 33 | handBrakeMode: false, 34 | FFmpegMode: false, 35 | reQueueAfter: false, 36 | infoLog: '☑File is a video! \n☑File is already in hevc! \n', 37 | }, 38 | }, 39 | ]; 40 | 41 | void run(tests); 42 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_075d_FFMPEG_HEVC_GPU_Generic_Video_Audio_Only_CRF20.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const run = require('../helpers/run'); 3 | 4 | const tests = [ 5 | { 6 | input: { 7 | file: require('../sampleData/media/sampleH264_1.json'), 8 | librarySettings: {}, 9 | inputs: {}, 10 | otherArguments: {}, 11 | }, 12 | output: { 13 | processFile: true, 14 | preset: ',-map 0:v -map 0:a -c copy -c:v:0 hevc_nvenc -crf 20', 15 | container: '.mkv', 16 | handBrakeMode: false, 17 | FFmpegMode: true, 18 | reQueueAfter: true, 19 | infoLog: '☑File is a video! \n☒File is not hevc! \n', 20 | }, 21 | }, 22 | { 23 | input: { 24 | file: require('../sampleData/media/sampleH265_1.json'), 25 | librarySettings: {}, 26 | inputs: {}, 27 | otherArguments: {}, 28 | }, 29 | output: { 30 | processFile: false, 31 | preset: '', 32 | container: '.mp4', 33 | handBrakeMode: false, 34 | FFmpegMode: false, 35 | reQueueAfter: false, 36 | infoLog: '☑File is a video! \n☑File is already in hevc! \n', 37 | }, 38 | }, 39 | ]; 40 | 41 | void run(tests); 42 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_076b_re_order_subtitle_streams.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const run = require('../helpers/run'); 3 | 4 | const tests = [ 5 | { 6 | input: { 7 | file: require('../sampleData/media/sampleH264_2.json'), 8 | librarySettings: {}, 9 | inputs: {}, 10 | otherArguments: {}, 11 | }, 12 | output: { 13 | processFile: false, 14 | preset: '', 15 | container: '.mp4', 16 | handBrakeMode: false, 17 | FFmpegMode: false, 18 | reQueueAfter: false, 19 | infoLog: '☒ No subtitle tracks in desired language! \n', 20 | }, 21 | }, 22 | { 23 | input: { 24 | file: require('../sampleData/media/sampleH264_2.json'), 25 | librarySettings: {}, 26 | inputs: { 27 | preferred_language: 'fre', 28 | }, 29 | otherArguments: {}, 30 | }, 31 | output: { 32 | processFile: false, 33 | preset: '', 34 | container: '.mp4', 35 | handBrakeMode: false, 36 | FFmpegMode: false, 37 | reQueueAfter: false, 38 | infoLog: '☑ Preferred language is already first subtitle track! \n', 39 | }, 40 | 41 | }, { 42 | input: { 43 | file: require('../sampleData/media/sampleH264_3.json'), 44 | librarySettings: {}, 45 | inputs: { 46 | preferred_language: 'fre', 47 | }, 48 | otherArguments: {}, 49 | }, 50 | output: { 51 | processFile: true, 52 | preset: ', -c copy -map 0:v -map 0:a -map 0:s:1 -disposition:s:0 default -map 0:s:0 -disposition:s:1 0 -map 0:d? -map 0:t? ', 53 | container: '.mkv', 54 | handBrakeMode: false, 55 | FFmpegMode: true, 56 | reQueueAfter: true, 57 | infoLog: '☒ Desired subtitle lang is not first subtitle stream, moving! \n', 58 | }, 59 | }, 60 | ]; 61 | 62 | void run(tests); 63 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_077b_HandBrake_NVENC_264_Configurable.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const run = require('../helpers/run'); 3 | 4 | const tests = [ 5 | { 6 | input: { 7 | file: require('../sampleData/media/sampleH264_1.json'), 8 | librarySettings: {}, 9 | inputs: {}, 10 | otherArguments: {}, 11 | }, 12 | output: { 13 | processFile: false, 14 | preset: '', 15 | container: '.mp4', 16 | handBrakeMode: false, 17 | FFmpegMode: false, 18 | reQueueAfter: false, 19 | infoLog: '☑ File is already in h264, no need to transcode! \n', 20 | }, 21 | }, 22 | { 23 | input: { 24 | file: require('../sampleData/media/sampleH265_1.json'), 25 | librarySettings: {}, 26 | inputs: { 27 | handbrake_preset: 'Very Fast 1080p30', 28 | output_container: '.mp4', 29 | }, 30 | otherArguments: {}, 31 | }, 32 | output: { 33 | processFile: true, 34 | preset: '-Z "Very Fast 1080p30" -e nvenc_h264 --all-audio --all-subtitles', 35 | container: '.mp4', 36 | handBrakeMode: true, 37 | FFmpegMode: false, 38 | reQueueAfter: true, 39 | infoLog: '☒ File is not in h264, transcoding! \n', 40 | }, 41 | }, 42 | ]; 43 | 44 | void run(tests); 45 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_Greg_MP3_FFMPEG_CPU.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: {}, 12 | }, 13 | output: { 14 | preset: ', -map_metadata 0 -id3v2_version 3 -b:a 320k', 15 | container: '.mp3', 16 | handbrakeMode: false, 17 | ffmpegMode: true, 18 | processFile: false, 19 | reQueueAfter: true, 20 | infoLog: 'undefined☒Codec excluded \n ☑Codec not excluded \n', 21 | }, 22 | }, 23 | { 24 | input: { 25 | file: _.cloneDeep(require('../sampleData/media/sampleAAC_1.json')), 26 | librarySettings: {}, 27 | inputs: {}, 28 | otherArguments: {}, 29 | }, 30 | output: { 31 | preset: ', -map_metadata 0 -id3v2_version 3 -b:a 320k', 32 | container: '.mp3', 33 | handbrakeMode: false, 34 | ffmpegMode: true, 35 | processFile: false, 36 | reQueueAfter: true, 37 | infoLog: 'undefined☒Codec excluded \n ☑Codec not excluded \n', 38 | }, 39 | }, 40 | { 41 | input: { 42 | file: _.cloneDeep(require('../sampleData/media/sampleMP3_1.json')), 43 | librarySettings: {}, 44 | inputs: {}, 45 | otherArguments: {}, 46 | }, 47 | output: { 48 | preset: ', -map_metadata 0 -id3v2_version 3 -b:a 320k', 49 | container: '.mp3', 50 | handbrakeMode: false, 51 | ffmpegMode: true, 52 | processFile: false, 53 | reQueueAfter: true, 54 | infoLog: 'undefined☒Codec excluded \n ☒Codec excluded \n', 55 | }, 56 | }, 57 | ]; 58 | 59 | void run(tests); 60 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_MC93_Migz1Remux.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: {}, 12 | }, 13 | output: { 14 | processFile: true, 15 | preset: ', -map 0 -c copy -max_muxing_queue_size 9999 ', 16 | handBrakeMode: false, 17 | FFmpegMode: true, 18 | reQueueAfter: true, 19 | infoLog: '☒File is mp4 but requested to be mkv container. Remuxing. \n', 20 | container: '.mkv', 21 | }, 22 | }, 23 | { 24 | input: { 25 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 26 | librarySettings: {}, 27 | inputs: { 28 | container: 'mp4', 29 | force_conform: 'true', 30 | }, 31 | otherArguments: {}, 32 | }, 33 | output: { 34 | processFile: false, 35 | preset: '', 36 | handBrakeMode: false, 37 | FFmpegMode: true, 38 | reQueueAfter: true, 39 | infoLog: '☑File is already in mp4 container. \n', 40 | container: '.mp4', 41 | }, 42 | }, 43 | ]; 44 | 45 | void run(tests); 46 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_MC93_Migz2CleanTitle.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: {}, 12 | }, 13 | output: { 14 | processFile: true, 15 | preset: ', -metadata title= -c copy -map 0 -max_muxing_queue_size 9999', 16 | container: '.mp4', 17 | handBrakeMode: false, 18 | FFmpegMode: true, 19 | reQueueAfter: true, 20 | infoLog: '☒File has title metadata. Removing \n', 21 | }, 22 | }, 23 | { 24 | input: { 25 | file: (() => { 26 | const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json')); 27 | file.meta.Title = undefined; 28 | return file; 29 | })(), 30 | librarySettings: {}, 31 | inputs: {}, 32 | otherArguments: {}, 33 | }, 34 | output: { 35 | processFile: false, 36 | preset: '', 37 | container: '.mp4', 38 | handBrakeMode: false, 39 | FFmpegMode: true, 40 | reQueueAfter: false, 41 | infoLog: '☑File has no title metadata \n', 42 | }, 43 | }, 44 | ]; 45 | 46 | void run(tests); 47 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_MC93_MigzImageRemoval.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: {}, 12 | }, 13 | output: { 14 | processFile: false, 15 | preset: '', 16 | handBrakeMode: false, 17 | container: '.mp4', 18 | FFmpegMode: true, 19 | reQueueAfter: true, 20 | infoLog: "☑File doesn't contain any unwanted image format streams.\n", 21 | }, 22 | }, 23 | { 24 | input: { 25 | file: (() => { 26 | const file = _.cloneDeep(require('../sampleData/media/sampleH264_2.json')); 27 | file.ffProbeData.streams[0].codec_name = 'mjpeg'; 28 | return file; 29 | })(), 30 | librarySettings: {}, 31 | inputs: {}, 32 | otherArguments: {}, 33 | }, 34 | output: { 35 | processFile: true, 36 | preset: ',-map 0 -c copy -max_muxing_queue_size 9999 -map -v:0 ', 37 | handBrakeMode: false, 38 | container: '.mkv', 39 | FFmpegMode: true, 40 | reQueueAfter: true, 41 | infoLog: '☒File has image format stream, removing. \n', 42 | }, 43 | }, 44 | ]; 45 | 46 | void run(tests); 47 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_MP01_MichPasCleanSubsAndAudioCodecs.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: {}, 12 | }, 13 | output: { 14 | processFile: false, 15 | preset: '', 16 | container: '.mp4', 17 | handBrakeMode: false, 18 | FFmpegMode: true, 19 | reQueueAfter: false, 20 | infoLog: "☑File doesn't contain subtitle or audio codecs which were unwanted or that require tagging.\n", 21 | }, 22 | }, 23 | { 24 | input: { 25 | file: _.cloneDeep(require('../sampleData/media/sampleH264_2.json')), 26 | librarySettings: {}, 27 | inputs: { 28 | tag_subtitle_codecs: 'subrip', 29 | tag_audio_codecs: 'aac', 30 | }, 31 | otherArguments: {}, 32 | }, 33 | output: { 34 | processFile: true, 35 | preset: ', -map 0 -map -0:a:3 -map -0:a:4 -map -0:s:0 -c copy -max_muxing_queue_size 4096', 36 | container: '.mkv', 37 | handBrakeMode: false, 38 | FFmpegMode: true, 39 | reQueueAfter: true, 40 | infoLog: '☒audio stream detected as unwanted. removing audio stream 0:a:3 - Français E-AC3 2.0 - aac \n' 41 | + '☒audio stream detected as unwanted. removing audio stream 0:a:4 - Anglais E-AC3 2.0 - aac \n' 42 | + '☒Subtitle stream detected as unwanted. removing subtitle stream 0:s:0 - Français - subrip. \n', 43 | }, 44 | }, 45 | ]; 46 | 47 | void run(tests); 48 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_a37x_Drawmonster_MP4_No_Title_Meta.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: {}, 12 | }, 13 | output: { 14 | processFile: true, 15 | preset: ',-map_metadata -1 -map 0 -c copy', 16 | container: '.mp4', 17 | handBrakeMode: false, 18 | FFmpegMode: true, 19 | reQueueAfter: true, 20 | infoLog: '☒File has title metadata \n', 21 | }, 22 | }, 23 | { 24 | input: { 25 | file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')), 26 | librarySettings: {}, 27 | inputs: {}, 28 | otherArguments: {}, 29 | }, 30 | output: { 31 | processFile: false, 32 | preset: '', 33 | container: '.mp4', 34 | handBrakeMode: false, 35 | FFmpegMode: true, 36 | reQueueAfter: false, 37 | infoLog: '☑File has no title metadata \n☑File meets conditions! \n', 38 | }, 39 | }, 40 | ]; 41 | 42 | void run(tests); 43 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_a8hc_HaveAGitGat_HandBrake_H264_VeryFast1080p30.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: {}, 12 | }, 13 | output: { 14 | processFile: true, 15 | preset: ',-map_metadata -1 -map 0 -c copy', 16 | container: '.mp4', 17 | handBrakeMode: false, 18 | FFmpegMode: true, 19 | reQueueAfter: true, 20 | infoLog: '☑File is already in h264! \n☑File has no subs \n☒File has title metadata \n', 21 | }, 22 | }, 23 | { 24 | input: { 25 | file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')), 26 | librarySettings: {}, 27 | inputs: {}, 28 | otherArguments: {}, 29 | }, 30 | output: { 31 | processFile: true, 32 | preset: '-Z "Very Fast 1080p30"', 33 | container: '.mp4', 34 | handBrakeMode: true, 35 | FFmpegMode: false, 36 | reQueueAfter: true, 37 | infoLog: '☒File is not in h264! \n', 38 | }, 39 | }, 40 | { 41 | input: { 42 | file: (() => { 43 | const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json')); 44 | file.meta.Title = undefined; 45 | return file; 46 | })(), 47 | librarySettings: {}, 48 | inputs: {}, 49 | otherArguments: {}, 50 | }, 51 | output: { 52 | processFile: false, 53 | preset: '', 54 | container: '.mp4', 55 | handBrakeMode: false, 56 | FFmpegMode: false, 57 | reQueueAfter: false, 58 | infoLog: '☑File is already in h264! \n' 59 | + '☑File has no subs \n' 60 | + '☑File has no title metadata☑File has aac track \n' 61 | + '☑File meets conditions! \n', 62 | }, 63 | }, 64 | ]; 65 | 66 | void run(tests); 67 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_a9hc_HaveAGitGat_HandBrake_H264_Fast1080p30.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: {}, 12 | }, 13 | output: { 14 | processFile: true, 15 | preset: ',-map_metadata -1 -map 0 -c copy', 16 | container: '.mp4', 17 | handBrakeMode: false, 18 | FFmpegMode: true, 19 | reQueueAfter: true, 20 | infoLog: '☑File is already in h264! \n☑File has no subs \n☒File has title metadata \n', 21 | }, 22 | }, 23 | { 24 | input: { 25 | file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')), 26 | librarySettings: {}, 27 | inputs: {}, 28 | otherArguments: {}, 29 | }, 30 | output: { 31 | processFile: true, 32 | preset: '-Z "Fast 1080p30"', 33 | container: '.mp4', 34 | handBrakeMode: true, 35 | FFmpegMode: false, 36 | reQueueAfter: true, 37 | infoLog: '☒File is not in h264! \n', 38 | }, 39 | }, 40 | { 41 | input: { 42 | file: (() => { 43 | const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json')); 44 | file.meta.Title = undefined; 45 | return file; 46 | })(), 47 | librarySettings: {}, 48 | inputs: {}, 49 | otherArguments: {}, 50 | }, 51 | output: { 52 | processFile: false, 53 | preset: '', 54 | container: '.mp4', 55 | handBrakeMode: false, 56 | FFmpegMode: false, 57 | reQueueAfter: false, 58 | infoLog: '☑File is already in h264! \n' 59 | + '☑File has no subs \n' 60 | + '☑File has no title metadata☑File has aac track \n' 61 | + '☑File meets conditions! \n', 62 | }, 63 | }, 64 | ]; 65 | 66 | void run(tests); 67 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_a9hd_FFMPEG_Transcode_Specific_Audio_Stream_Codecs.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const run = require('../helpers/run'); 3 | 4 | const tests = [ 5 | { 6 | input: { 7 | file: require('../sampleData/media/sampleH264_1.json'), 8 | librarySettings: {}, 9 | inputs: {}, 10 | otherArguments: {}, 11 | }, 12 | output: { 13 | processFile: false, 14 | preset: '', 15 | container: '.mp4', 16 | handBrakeMode: false, 17 | FFmpegMode: false, 18 | reQueueAfter: false, 19 | infoLog: '☑ File does not have any streams that need to be transcoded! \n', 20 | }, 21 | }, 22 | { 23 | input: { 24 | file: require('../sampleData/media/sampleH264_1.json'), 25 | librarySettings: {}, 26 | inputs: { 27 | codecs_to_transcode: 'aac', 28 | codec: 'eac3', 29 | bitrate: '640k', 30 | }, 31 | otherArguments: {}, 32 | }, 33 | output: { 34 | processFile: true, 35 | preset: ', -c copy -map 0:v -map 0:1 -c:1 eac3 -b:a 640k -map 0:s? -map 0:d? -max_muxing_queue_size 9999', 36 | container: '.mp4', 37 | handBrakeMode: false, 38 | FFmpegMode: true, 39 | reQueueAfter: true, 40 | infoLog: "☒ File has streams which aren't in desired codec! \n", 41 | }, 42 | }, 43 | ]; 44 | 45 | void run(tests); 46 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_b38x_Nosirus_h265_aac_no_meta.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: {}, 12 | }, 13 | output: { 14 | processFile: true, 15 | preset: ', -map 0 -c copy -c:v:0 libx265 -preset:v slow -pix_fmt yuv420p10le -x265-params "crf=22:aq-mode=3"', 16 | container: '.mkv', 17 | handBrakeMode: false, 18 | FFmpegMode: true, 19 | reQueueAfter: true, 20 | infoLog: '☒File is not in hevc! \n', 21 | }, 22 | }, 23 | { 24 | input: { 25 | file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')), 26 | librarySettings: {}, 27 | inputs: {}, 28 | otherArguments: {}, 29 | }, 30 | output: { 31 | processFile: false, 32 | preset: '', 33 | container: '.mp4', 34 | handBrakeMode: false, 35 | FFmpegMode: false, 36 | reQueueAfter: false, 37 | infoLog: '☑File is already in hevc! \n' 38 | + '☑ All audio streams are in aac! \n' 39 | + '☑File has no title metadata \n' 40 | + '☑File meets conditions! \n', 41 | }, 42 | }, 43 | ]; 44 | 45 | void run(tests); 46 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_b39x_the1poet_surround_sound_to_ac3.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: {}, 12 | }, 13 | output: { 14 | processFile: true, 15 | preset: ',-map 0 -c:v copy -c:a copy -c:a:0 ac3 -c:s copy -c:d copy', 16 | container: '.mp4', 17 | handBrakeMode: false, 18 | FFmpegMode: true, 19 | reQueueAfter: true, 20 | infoLog: '☒ File has surround audio which is NOT in ac3! \n', 21 | }, 22 | }, 23 | { 24 | input: { 25 | file: _.cloneDeep(require('../sampleData/media/sampleH264_2.json')), 26 | librarySettings: {}, 27 | inputs: {}, 28 | otherArguments: {}, 29 | }, 30 | output: { 31 | processFile: false, 32 | preset: '', 33 | container: '.mp4', 34 | handBrakeMode: false, 35 | FFmpegMode: false, 36 | reQueueAfter: false, 37 | infoLog: '☑ All surround audio streams are in ac3! \n☑File meets conditions! \n', 38 | }, 39 | }, 40 | { 41 | input: { 42 | file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')), 43 | librarySettings: {}, 44 | inputs: {}, 45 | otherArguments: {}, 46 | }, 47 | output: { 48 | processFile: false, 49 | preset: '', 50 | container: '.mp4', 51 | handBrakeMode: false, 52 | FFmpegMode: false, 53 | reQueueAfter: false, 54 | infoLog: '☑ All surround audio streams are in ac3! \n☑File meets conditions! \n', 55 | }, 56 | }, 57 | ]; 58 | 59 | void run(tests); 60 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_c0r1_SetDefaultAudioStream.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: {}, 12 | }, 13 | output: { 14 | processFile: false, 15 | preset: '', 16 | container: '.mp4', 17 | handBrakeMode: false, 18 | FFmpegMode: true, 19 | infoLog: '☑ No 2 channel audio stream exists. \n ', 20 | }, 21 | }, 22 | { 23 | input: { 24 | file: (() => { 25 | const file = _.cloneDeep(require('../sampleData/media/sampleH264_2.json')); 26 | 27 | file.ffProbeData.streams[1].channels = 6; 28 | return file; 29 | })(), 30 | librarySettings: {}, 31 | inputs: { 32 | channels: '6', 33 | }, 34 | otherArguments: {}, 35 | }, 36 | output: { 37 | processFile: true, 38 | preset: ',-map 0 -c copy -disposition:1 default -disposition:2 0 -disposition:3 0 -disposition:4 0 -disposition:5 0 ', 39 | container: '.mkv', 40 | handBrakeMode: false, 41 | FFmpegMode: true, 42 | infoLog: '☒ Matching audio stream is not set to default. \n' 43 | + '☒ Setting 6 channel matching audio stream to default. Remove default from all other audio streams \n', 44 | reQueueAfter: true, 45 | }, 46 | }, 47 | ]; 48 | 49 | void run(tests); 50 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_d5d3_iiDrakeii_FFMPEG_NVENC_Tiered_MKV.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: {}, 12 | }, 13 | output: { 14 | processFile: true, 15 | preset: '-c:v h264_cuvid,-map 0 -dn -c:v hevc_nvenc -pix_fmt p010le -qmin 0 -cq:v 30 -b:v 964k -maxrate:v 2964k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -a53cc 0 -c:a copy -c:s copy', 16 | container: '.mkv', 17 | handBrakeMode: false, 18 | FFmpegMode: true, 19 | reQueueAfter: true, 20 | infoLog: '☑File is a video! \n' 21 | + '☒File is 720p!\n' 22 | + '☒File is not hevc!\n' 23 | + '☒File bitrate is 1205kb!\n' 24 | + 'File bitrate is LOWER than the Default Target Bitrate!\n' 25 | + '☒Target Bitrate set to 964kb!\n' 26 | + 'File is being transcoded!\n', 27 | maxmux: false, 28 | }, 29 | }, 30 | { 31 | input: { 32 | file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')), 33 | librarySettings: {}, 34 | inputs: {}, 35 | otherArguments: {}, 36 | }, 37 | output: { 38 | processFile: false, 39 | preset: '', 40 | container: '.mkv', 41 | handBrakeMode: false, 42 | FFmpegMode: false, 43 | reQueueAfter: true, 44 | infoLog: '☑File is a video! \n☑File is already in hevc! \n', 45 | maxmux: false, 46 | }, 47 | }, 48 | ]; 49 | 50 | void run(tests); 51 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_d5d4_iiDrakeii_Not_A_Video_Mjpeg_Fix.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: {}, 12 | }, 13 | output: { 14 | processFile: false, 15 | preset: '', 16 | container: '.mp4', 17 | handBrakeMode: false, 18 | FFmpegMode: false, 19 | reQueueAfter: true, 20 | infoLog: '☑File is a video Without Mjpeg! \n', 21 | }, 22 | }, 23 | { 24 | input: { 25 | file: (() => { 26 | const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json')); 27 | file.ffProbeData.streams[0].codec_name = 'mjpeg'; 28 | return file; 29 | })(), 30 | librarySettings: {}, 31 | inputs: {}, 32 | otherArguments: {}, 33 | }, 34 | output: { 35 | processFile: false, 36 | preset: ',-map 0 -map -0:v:1 -c:v copy -c:a copy -c:s copy', 37 | container: '.mp4', 38 | handBrakeMode: false, 39 | FFmpegMode: false, 40 | reQueueAfter: true, 41 | infoLog: '☒File is not a video but has Mjpeg Stream! \n' 42 | + '☑File is a video With Mjpeg! \n', 43 | }, 44 | }, 45 | ]; 46 | 47 | void run(tests); 48 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_da11_Dallas_FFmpeg_Presets_H264_MP4.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: {}, 12 | }, 13 | output: { 14 | processFile: true, 15 | preset: ', -map_metadata -1 -map 0:v -map 0:a -c:v copy -c:a copy -c:s mov_text', 16 | container: '.mp4', 17 | handBrakeMode: false, 18 | FFmpegMode: true, 19 | reQueueAfter: true, 20 | infoLog: '☑ File is already in h264!\n☒ File has title metadata\n', 21 | }, 22 | }, 23 | { 24 | input: { 25 | file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')), 26 | librarySettings: {}, 27 | inputs: { 28 | }, 29 | otherArguments: {}, 30 | }, 31 | output: { 32 | processFile: true, 33 | preset: ', -map_metadata -1 -map 0:V -map 0:a -c:v libx264 -preset medium -c:a aac -strict -2 -c:s mov_text', 34 | container: '.mp4', 35 | handBrakeMode: false, 36 | FFmpegMode: true, 37 | reQueueAfter: true, 38 | infoLog: '☒ File is not in h264!\n', 39 | }, 40 | }, 41 | { 42 | input: { 43 | file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')), 44 | librarySettings: {}, 45 | inputs: { 46 | FFmpeg_preset: 'fast', 47 | }, 48 | otherArguments: {}, 49 | }, 50 | output: { 51 | processFile: true, 52 | preset: ', -map_metadata -1 -map 0:V -map 0:a -c:v libx264 -preset fast -c:a aac -strict -2 -c:s mov_text', 53 | container: '.mp4', 54 | handBrakeMode: false, 55 | FFmpegMode: true, 56 | reQueueAfter: true, 57 | infoLog: '☒ File is not in h264!\n', 58 | }, 59 | }, 60 | ]; 61 | 62 | void run(tests); 63 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_fd5T_Sparticus_4K_AC3_No_Subs.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: {}, 12 | }, 13 | output: { 14 | processFile: false, 15 | preset: '', 16 | container: '.mp4', 17 | handBrakeMode: false, 18 | FFmpegMode: true, 19 | reQueueAfter: false, 20 | infoLog: '☒File is not a 4K video \n', 21 | }, 22 | }, 23 | { 24 | input: { 25 | file: (() => { 26 | const file = _.cloneDeep(require('../sampleData/media/sampleH264_2.json')); 27 | file.video_resolution = '4KUHD'; 28 | return file; 29 | })(), 30 | librarySettings: {}, 31 | inputs: {}, 32 | otherArguments: {}, 33 | }, 34 | output: { 35 | processFile: true, 36 | preset: ',-sn -map 0 -c copy', 37 | container: '.mkv', 38 | handBrakeMode: false, 39 | FFmpegMode: true, 40 | reQueueAfter: true, 41 | infoLog: '☑File does not have only AC3 track commentaries! \n' 42 | + '☑File has AC3 track! \n' 43 | + '☒File has subs! \n', 44 | }, 45 | }, 46 | ]; 47 | 48 | void run(tests); 49 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_hk75_Drawmonster_MP4_AAC_No_Subs_No_metaTitle.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: {}, 12 | }, 13 | output: { 14 | processFile: true, 15 | preset: ',-map_metadata -1 -c:v copy -c:a copy', 16 | container: '.mp4', 17 | handBrakeMode: false, 18 | FFmpegMode: true, 19 | reQueueAfter: true, 20 | infoLog: '☒File has title metadata \n', 21 | }, 22 | }, 23 | { 24 | input: { 25 | file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')), 26 | librarySettings: {}, 27 | inputs: {}, 28 | otherArguments: {}, 29 | }, 30 | output: { 31 | processFile: true, 32 | preset: ', -c:v copy -c:a copy', 33 | container: '.mp4', 34 | handBrakeMode: false, 35 | FFmpegMode: true, 36 | reQueueAfter: true, 37 | infoLog: '☑File has no title metadata \n' 38 | + '☑File has no subs \n' 39 | + '☒File is not in mp4 container! \n', 40 | }, 41 | }, 42 | { 43 | input: { 44 | file: (() => { 45 | const file = _.cloneDeep(require('../sampleData/media/sampleH265_1.json')); 46 | file.container = 'mp4'; 47 | return file; 48 | })(), 49 | librarySettings: {}, 50 | inputs: {}, 51 | otherArguments: {}, 52 | }, 53 | output: { 54 | processFile: false, 55 | preset: '', 56 | container: '.mp4', 57 | handBrakeMode: false, 58 | FFmpegMode: true, 59 | reQueueAfter: false, 60 | infoLog: '☑File has no title metadata \n' 61 | + '☑File has no subs \n' 62 | + '☑File is in mp4 container! \n' 63 | + '☑File meets conditions! \n', 64 | }, 65 | }, 66 | ]; 67 | 68 | void run(tests); 69 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_hk76_GilbN_MP4_AAC_No_metaTitle.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: {}, 12 | }, 13 | output: { 14 | processFile: true, 15 | preset: ',-map_metadata -1 -map 0 -c copy', 16 | container: '.mp4', 17 | handBrakeMode: false, 18 | FFmpegMode: true, 19 | reQueueAfter: true, 20 | infoLog: '☑File is in mp4 container! \n☒File has title metadata \n', 21 | }, 22 | }, 23 | { 24 | input: { 25 | file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')), 26 | librarySettings: {}, 27 | inputs: {}, 28 | otherArguments: {}, 29 | }, 30 | output: { 31 | processFile: true, 32 | preset: ', -map 0 -c copy', 33 | container: '.mp4', 34 | handBrakeMode: false, 35 | FFmpegMode: true, 36 | reQueueAfter: true, 37 | infoLog: '☒File is not in mp4 container! \n', 38 | }, 39 | }, 40 | { 41 | input: { 42 | file: (() => { 43 | const file = _.cloneDeep(require('../sampleData/media/sampleH265_1.json')); 44 | file.container = 'mp4'; 45 | return file; 46 | })(), 47 | librarySettings: {}, 48 | inputs: {}, 49 | otherArguments: {}, 50 | }, 51 | output: { 52 | processFile: false, 53 | preset: '', 54 | container: '.mp4', 55 | handBrakeMode: false, 56 | FFmpegMode: true, 57 | reQueueAfter: false, 58 | infoLog: '☑File is in mp4 container! \n' 59 | + '☑File has no title metadata \n' 60 | + '☑File has aac track \n' 61 | + '☑File meets conditions! \n', 62 | }, 63 | }, 64 | ]; 65 | 66 | void run(tests); 67 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_jeons001_Downmix_to_stereo_and_apply_DRC.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: {}, 12 | }, 13 | output: { 14 | processFile: true, 15 | preset: '-sn -vcodec copy -scodec copy -acodec aac -filter:a "dynaudnorm,pan=stereo|FL < 1.0*FL + 0.707*FC + 0.707*BL|FR < 1.0*FR + 0.707*FC + 0.707*BR"', 16 | handBrakeMode: false, 17 | FFmpegMode: true, 18 | reQueueAfter: true, 19 | infoLog: 'File matches requirements for processing. Downmixing and applying DRC!', 20 | container: '.mp4', 21 | }, 22 | }, 23 | { 24 | input: { 25 | file: _.cloneDeep(require('../sampleData/media/sampleH264_2.json')), 26 | librarySettings: {}, 27 | inputs: {}, 28 | otherArguments: {}, 29 | }, 30 | output: { 31 | processFile: false, 32 | preset: '', 33 | handBrakeMode: false, 34 | FFmpegMode: true, 35 | reQueueAfter: true, 36 | infoLog: 'File has more than 1 audio track - not processing', 37 | container: '.mkv', 38 | }, 39 | }, 40 | ]; 41 | 42 | void run(tests); 43 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_lmg1_Reorder_Streams.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: {}, 12 | }, 13 | output: { 14 | processFile: false, 15 | preset: '', 16 | container: '.mp4', 17 | handBrakeMode: false, 18 | FFmpegMode: true, 19 | reQueueAfter: false, 20 | infoLog: 'File has video in first stream\n File meets conditions!\n', 21 | }, 22 | }, 23 | { 24 | input: { 25 | file: (() => { 26 | const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json')); 27 | 28 | const audio = file.ffProbeData.streams[1]; 29 | // eslint-disable-next-line prefer-destructuring 30 | file.ffProbeData.streams[1] = file.ffProbeData.streams[0]; 31 | file.ffProbeData.streams[0] = audio; 32 | return file; 33 | })(), 34 | librarySettings: {}, 35 | inputs: {}, 36 | otherArguments: {}, 37 | }, 38 | output: { 39 | processFile: true, 40 | preset: ',-map 0:v? -map 0:a? -map 0:s? -map 0:d? -map 0:t? -c copy', 41 | container: '.mp4', 42 | handBrakeMode: false, 43 | FFmpegMode: true, 44 | reQueueAfter: true, 45 | infoLog: 'Video is not in the first stream', 46 | }, 47 | }, 48 | ]; 49 | 50 | void run(tests); 51 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_nc7x_Drawmonster_No_Title_Meta.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: {}, 12 | }, 13 | output: { 14 | processFile: true, 15 | preset: ',-map_metadata -1 -map 0 -c copy', 16 | container: '.mp4', 17 | handBrakeMode: false, 18 | FFmpegMode: true, 19 | reQueueAfter: true, 20 | infoLog: '☒File has title metadata \n', 21 | }, 22 | }, 23 | { 24 | input: { 25 | file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')), 26 | librarySettings: {}, 27 | inputs: {}, 28 | otherArguments: {}, 29 | }, 30 | output: { 31 | processFile: false, 32 | preset: '', 33 | container: '.mkv', 34 | handBrakeMode: false, 35 | FFmpegMode: true, 36 | reQueueAfter: false, 37 | infoLog: '☑File has no title metadata \n☑File meets conditions! \n', 38 | }, 39 | }, 40 | ]; 41 | 42 | void run(tests); 43 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_r002_rootuser_FFMPEG_HQ_HEVC_MKV_Animation.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: {}, 12 | }, 13 | output: { 14 | processFile: true, 15 | preset: ',-map 0 -c:s copy -movflags use_metadata_tags -c:a aac -b:a 512k -c:v:0 libx265 -preset medium -x265-params crf=18:tune=animation:qcomp=0.7:aq-strength=1.1 -pix_fmt yuv420p10le -f matroska', 16 | container: '.mkv', 17 | handBrakeMode: false, 18 | FFmpegMode: true, 19 | reQueueAfter: true, 20 | infoLog: '☑File is a video! \n' 21 | + '☒File is 720p but is not hevc!\n' 22 | + '☒File will be transcoded!\n', 23 | }, 24 | }, 25 | { 26 | input: { 27 | file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')), 28 | librarySettings: {}, 29 | inputs: {}, 30 | otherArguments: {}, 31 | }, 32 | output: { 33 | processFile: false, 34 | preset: '', 35 | container: '.mkv', 36 | handBrakeMode: false, 37 | FFmpegMode: true, 38 | reQueueAfter: true, 39 | infoLog: '☑File is a video! \n☑File is already in hevc! \n', 40 | }, 41 | }, 42 | ]; 43 | 44 | void run(tests); 45 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_raf4_Floorpie_FFmpeg_Tiered_HEVC_MKV.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: {}, 12 | }, 13 | output: { 14 | processFile: true, 15 | preset: ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 25', 16 | container: '.mkv', 17 | handBrakeMode: false, 18 | FFmpegMode: true, 19 | reQueueAfter: true, 20 | infoLog: '☑File is a video! \n' 21 | + '☒File is 720p but is not hevc!\n' 22 | + '☒File will be transcoded!\n', 23 | }, 24 | }, 25 | { 26 | input: { 27 | file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')), 28 | librarySettings: {}, 29 | inputs: {}, 30 | otherArguments: {}, 31 | }, 32 | output: { 33 | processFile: false, 34 | preset: '', 35 | container: '.mkv', 36 | handBrakeMode: false, 37 | FFmpegMode: true, 38 | reQueueAfter: true, 39 | infoLog: '☑File is a video! \n☑File is already in hevc! \n', 40 | }, 41 | }, 42 | { 43 | input: { 44 | file: (() => { 45 | const file = _.cloneDeep(require('../sampleData/media/sampleH264_2.json')); 46 | file.video_resolution = '480p'; 47 | return file; 48 | })(), 49 | librarySettings: {}, 50 | inputs: {}, 51 | otherArguments: {}, 52 | }, 53 | output: { 54 | processFile: true, 55 | preset: ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 27', 56 | container: '.mkv', 57 | handBrakeMode: false, 58 | FFmpegMode: true, 59 | reQueueAfter: true, 60 | infoLog: '☑File is a video! \n' 61 | + '☒File is 480p but is not hevc!\n' 62 | + '☒File will be transcoded!\n', 63 | }, 64 | }, 65 | ]; 66 | 67 | void run(tests); 68 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_s7x8_winsome_h265.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: {}, 12 | }, 13 | output: { 14 | processFile: true, 15 | preset: '-Z "H.265 MKV 2160p60" --all-audio --all-subtitles', 16 | container: '.mkv', 17 | handBrakeMode: true, 18 | FFmpegMode: false, 19 | reQueueAfter: true, 20 | infoLog: "☒File isn't in hevc! \n", 21 | }, 22 | }, 23 | { 24 | input: { 25 | file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')), 26 | librarySettings: {}, 27 | inputs: {}, 28 | otherArguments: {}, 29 | }, 30 | output: { 31 | processFile: true, 32 | preset: ',-map 0:v -map 0:a:0 -map 0:a -map 0:s? -map 0:d? -c copy -c:a:0 ac3 -b:a:0 192k -ac 2', 33 | container: '.mkv', 34 | handBrakeMode: false, 35 | FFmpegMode: true, 36 | reQueueAfter: true, 37 | infoLog: '☒File has no language track in ac3,eac3,dts. No eng track marked so transcoding audio track 1 into ac3! \n', 38 | }, 39 | }, 40 | { 41 | input: { 42 | file: (() => { 43 | const file = _.cloneDeep(require('../sampleData/media/sampleH265_1.json')); 44 | file.ffProbeData.streams[1].codec_name = 'ac3'; 45 | return file; 46 | })(), 47 | librarySettings: {}, 48 | inputs: {}, 49 | otherArguments: {}, 50 | }, 51 | output: { 52 | processFile: false, 53 | preset: '', 54 | container: '.mkv', 55 | handBrakeMode: false, 56 | FFmpegMode: true, 57 | reQueueAfter: false, 58 | infoLog: '☑File is in mkv container! \n', 59 | }, 60 | }, 61 | ]; 62 | 63 | void run(tests); 64 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_s7x9_winsome_h265_10bit.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: {}, 12 | }, 13 | output: { 14 | processFile: true, 15 | preset: '-Z "H.265 MKV 2160p60" -e x265_10bit --all-audio --all-subtitles', 16 | container: '.mkv', 17 | handBrakeMode: true, 18 | FFmpegMode: false, 19 | reQueueAfter: true, 20 | infoLog: "☒File isn't in hevc! \n", 21 | }, 22 | }, 23 | { 24 | input: { 25 | file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')), 26 | librarySettings: {}, 27 | inputs: {}, 28 | otherArguments: {}, 29 | }, 30 | output: { 31 | processFile: true, 32 | preset: ',-map 0:v -map 0:a:0 -map 0:a -map 0:s? -map 0:d? -c copy -c:a:0 ac3 -b:a:0 192k -ac 2', 33 | container: '.mkv', 34 | handBrakeMode: false, 35 | FFmpegMode: true, 36 | reQueueAfter: true, 37 | infoLog: '☒File has no language track in ac3,eac3,dts. No eng track marked so transcoding audio track 1 into ac3! \n', 38 | }, 39 | }, 40 | { 41 | input: { 42 | file: (() => { 43 | const file = _.cloneDeep(require('../sampleData/media/sampleH265_1.json')); 44 | file.ffProbeData.streams[1].codec_name = 'ac3'; 45 | return file; 46 | })(), 47 | librarySettings: {}, 48 | inputs: {}, 49 | otherArguments: {}, 50 | }, 51 | output: { 52 | processFile: false, 53 | preset: '', 54 | container: '.mkv', 55 | handBrakeMode: false, 56 | FFmpegMode: true, 57 | reQueueAfter: false, 58 | infoLog: '☑File is in mkv container! \n', 59 | }, 60 | }, 61 | ]; 62 | 63 | void run(tests); 64 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_s7x9_winsome_h265_nvenc.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: {}, 12 | }, 13 | output: { 14 | processFile: true, 15 | preset: '-Z "H.265 MKV 2160p60" --all-audio --all-subtitles -e nvenc_h265', 16 | container: '.mkv', 17 | handBrakeMode: true, 18 | FFmpegMode: false, 19 | reQueueAfter: true, 20 | infoLog: "☒File isn't in hevc! \n", 21 | }, 22 | }, 23 | { 24 | input: { 25 | file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')), 26 | librarySettings: {}, 27 | inputs: {}, 28 | otherArguments: {}, 29 | }, 30 | output: { 31 | processFile: true, 32 | preset: ',-map 0:v -map 0:a:0 -map 0:a -map 0:s? -map 0:d? -c copy -c:a:0 ac3 -b:a:0 192k -ac 2', 33 | container: '.mkv', 34 | handBrakeMode: false, 35 | FFmpegMode: true, 36 | reQueueAfter: true, 37 | infoLog: '☒File has no language track in ac3,eac3,dts. No eng track marked so transcoding audio track 1 into ac3! \n', 38 | }, 39 | }, 40 | { 41 | input: { 42 | file: (() => { 43 | const file = _.cloneDeep(require('../sampleData/media/sampleH265_1.json')); 44 | file.ffProbeData.streams[1].codec_name = 'ac3'; 45 | return file; 46 | })(), 47 | librarySettings: {}, 48 | inputs: {}, 49 | otherArguments: {}, 50 | }, 51 | output: { 52 | processFile: false, 53 | preset: '', 54 | container: '.mkv', 55 | handBrakeMode: false, 56 | FFmpegMode: true, 57 | reQueueAfter: false, 58 | infoLog: '☑File is in mkv container! \n', 59 | }, 60 | }, 61 | ]; 62 | 63 | void run(tests); 64 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_sdd3_Remove_Commentary_Tracks.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: {}, 12 | }, 13 | output: { 14 | processFile: false, 15 | preset: '', 16 | container: '.mp4', 17 | handBrakeMode: false, 18 | FFmpegMode: false, 19 | reQueueAfter: false, 20 | infoLog: '☑File is a video! \n' 21 | + "☑File doesn't contain commentary tracks! \n" 22 | + '☑File meets conditions! \n', 23 | }, 24 | }, 25 | { 26 | input: { 27 | file: (() => { 28 | const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json')); 29 | file.ffProbeData.streams[1].tags.title = 'commentary'; 30 | return file; 31 | })(), 32 | librarySettings: {}, 33 | inputs: {}, 34 | otherArguments: {}, 35 | }, 36 | output: { 37 | processFile: true, 38 | preset: ', -map 0 -map -0:a:0 -c copy', 39 | container: '.mp4', 40 | handBrakeMode: false, 41 | FFmpegMode: true, 42 | reQueueAfter: true, 43 | infoLog: '☑File is a video! \n☒File contains commentary tracks. Removing! \n', 44 | }, 45 | }, 46 | ]; 47 | 48 | void run(tests); 49 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_sdf5_Thierrrrry_Remove_Non_English_Audio.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: {}, 12 | }, 13 | output: { 14 | processFile: false, 15 | preset: '', 16 | container: '.mp4', 17 | handBrakeMode: false, 18 | FFmpegMode: true, 19 | reQueueAfter: false, 20 | infoLog: "☑File doesn't contain tracks which are not english or undefined! \n", 21 | }, 22 | }, 23 | { 24 | input: { 25 | file: (() => { 26 | const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json')); 27 | file.ffProbeData.streams[1].tags.language = 'fre'; 28 | return file; 29 | })(), 30 | librarySettings: {}, 31 | inputs: {}, 32 | otherArguments: {}, 33 | }, 34 | output: { 35 | processFile: false, 36 | preset: '', 37 | container: '.mp4', 38 | handBrakeMode: false, 39 | FFmpegMode: true, 40 | reQueueAfter: false, 41 | infoLog: "☑File doesn't contain tracks which are not english or undefined! \n", 42 | }, 43 | }, 44 | { 45 | input: { 46 | file: _.cloneDeep(require('../sampleData/media/sampleH264_2.json')), 47 | librarySettings: {}, 48 | inputs: {}, 49 | otherArguments: {}, 50 | }, 51 | output: { 52 | processFile: true, 53 | preset: ', -map 0 -map -0:a:3 -c copy', 54 | container: '.mkv', 55 | handBrakeMode: false, 56 | FFmpegMode: true, 57 | reQueueAfter: true, 58 | infoLog: '☒File contains tracks which are not english or undefined. Removing! \n', 59 | }, 60 | }, 61 | ]; 62 | 63 | void run(tests); 64 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_tsld_filter_modified_date.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const run = require('../helpers/run'); 3 | 4 | const tests = [ 5 | { 6 | input: { 7 | file: require('../sampleData/media/sampleH264_2.json'), 8 | librarySettings: {}, 9 | inputs: { 10 | minModifiedDaysOld: 1, 11 | }, 12 | otherArguments: {}, 13 | }, 14 | output: { 15 | processFile: true, 16 | infoLog: 'File modified date old enough. Moving to next plugin.', 17 | }, 18 | }, 19 | { 20 | input: { 21 | file: require('../sampleData/media/sampleH264_1.json'), 22 | librarySettings: {}, 23 | inputs: { 24 | minModifiedDaysOld: 9999, 25 | }, 26 | otherArguments: {}, 27 | }, 28 | output: { 29 | processFile: false, 30 | infoLog: 'Skipping, file modified date not old enough', 31 | }, 32 | }, 33 | { 34 | input: { 35 | file: require('../sampleData/media/sampleH264_1.json'), 36 | librarySettings: {}, 37 | inputs: { 38 | minModifiedDaysOld: 1, 39 | }, 40 | otherArguments: {}, 41 | }, 42 | output: { 43 | processFile: true, 44 | infoLog: 'File modified date old enough. Moving to next plugin.', 45 | }, 46 | }, 47 | { 48 | input: { 49 | file: require('../sampleData/media/sampleH264_1.json'), 50 | librarySettings: {}, 51 | inputs: { 52 | minModifiedDaysOld: 9999, 53 | }, 54 | otherArguments: {}, 55 | }, 56 | output: { 57 | processFile: false, 58 | infoLog: 'Skipping, file modified date not old enough', 59 | }, 60 | }, 61 | ]; 62 | 63 | void run(tests); 64 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_vdka_Remove_DataStreams.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: {}, 12 | }, 13 | output: { 14 | processFile: false, 15 | preset: '', 16 | container: '.mkv', 17 | handBrakeMode: false, 18 | FFmpegMode: true, 19 | reQueueAfter: false, 20 | infoLog: '☑File has no data streams! \n☑File meets conditions! \n', 21 | }, 22 | }, 23 | { 24 | input: { 25 | file: (() => { 26 | const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json')); 27 | file.ffProbeData.streams[1].codec_type = 'data'; 28 | return file; 29 | })(), 30 | librarySettings: {}, 31 | inputs: {}, 32 | otherArguments: {}, 33 | }, 34 | output: { 35 | processFile: true, 36 | preset: ',-map 0 -c copy -dn -map_chapters -1', 37 | container: '.mkv', 38 | handBrakeMode: false, 39 | FFmpegMode: true, 40 | reQueueAfter: true, 41 | infoLog: '☒File has data streams \n', 42 | }, 43 | }, 44 | ]; 45 | 46 | void run(tests); 47 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_x7ab_Remove_Subs.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: {}, 12 | }, 13 | output: { 14 | processFile: false, 15 | preset: '', 16 | container: '.mp4', 17 | handBrakeMode: false, 18 | FFmpegMode: true, 19 | reQueueAfter: false, 20 | infoLog: '☑File has no subs \n☑File meets conditions! \n', 21 | }, 22 | }, 23 | { 24 | input: { 25 | file: _.cloneDeep(require('../sampleData/media/sampleH264_2.json')), 26 | librarySettings: {}, 27 | inputs: {}, 28 | otherArguments: {}, 29 | }, 30 | output: { 31 | processFile: true, 32 | preset: ',-sn -map 0 -c copy', 33 | container: '.mkv', 34 | handBrakeMode: false, 35 | FFmpegMode: true, 36 | reQueueAfter: true, 37 | infoLog: '☒File has subs \n', 38 | }, 39 | }, 40 | ]; 41 | 42 | void run(tests); 43 | -------------------------------------------------------------------------------- /tests/Community/Tdarr_Plugin_x7ac_Remove_Closed_Captions.js: -------------------------------------------------------------------------------- 1 | /* eslint max-len: 0 */ 2 | const _ = require('lodash'); 3 | const run = require('../helpers/run'); 4 | 5 | const tests = [ 6 | { 7 | input: { 8 | file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')), 9 | librarySettings: {}, 10 | inputs: {}, 11 | otherArguments: {}, 12 | }, 13 | output: { 14 | processFile: false, 15 | preset: ',-map 0 -codec copy -bsf:v "filter_units=remove_types=6"', 16 | container: '.mp4', 17 | handBrakeMode: false, 18 | FFmpegMode: true, 19 | reQueueAfter: true, 20 | infoLog: '☑Closed captions have not been detected on this file \n', 21 | }, 22 | }, 23 | { 24 | input: { 25 | file: (() => { 26 | const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json')); 27 | file.hasClosedCaptions = true; 28 | return file; 29 | })(), 30 | librarySettings: {}, 31 | inputs: {}, 32 | otherArguments: {}, 33 | }, 34 | output: { 35 | processFile: true, 36 | preset: ',-map 0 -codec copy -bsf:v "filter_units=remove_types=6"', 37 | container: '.mp4', 38 | handBrakeMode: false, 39 | FFmpegMode: true, 40 | reQueueAfter: true, 41 | infoLog: '☒This file has closed captions \n', 42 | }, 43 | }, 44 | { 45 | input: { 46 | file: (() => { 47 | const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json')); 48 | file.ffProbeData.streams[0].closed_captions = 1; 49 | return file; 50 | })(), 51 | librarySettings: {}, 52 | inputs: {}, 53 | otherArguments: {}, 54 | }, 55 | output: { 56 | processFile: true, 57 | preset: ',-map 0 -codec copy -bsf:v "filter_units=remove_types=6"', 58 | container: '.mp4', 59 | handBrakeMode: false, 60 | FFmpegMode: true, 61 | reQueueAfter: true, 62 | infoLog: '☒This file has burnt closed captions \n', 63 | }, 64 | }, 65 | ]; 66 | 67 | void run(tests); 68 | -------------------------------------------------------------------------------- /tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": [] 4 | } --------------------------------------------------------------------------------