├── .editorconfig ├── .gitattributes ├── .github └── workflows │ ├── docker-image.yml │ ├── publish-sdk.yml │ └── test.yml ├── .gitignore ├── LICENSE ├── README.md ├── applications ├── build.gradle.kts ├── minimize │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── kotlin │ │ └── io │ │ └── github │ │ └── shoaky │ │ └── sourcedownloader │ │ └── application │ │ └── minimize │ │ ├── ApplicationConfig.kt │ │ ├── ApplicationContext.kt │ │ ├── EnhancedHttpHandler.kt │ │ ├── SourceDownloaderConfig.kt │ │ └── SourceDownloaderMinimizeApplication.kt ├── spring │ ├── Dockerfile │ ├── build.gradle.kts │ └── src │ │ ├── main │ │ ├── kotlin │ │ │ └── io │ │ │ │ └── github │ │ │ │ └── shoaky │ │ │ │ └── sourcedownloader │ │ │ │ └── application │ │ │ │ └── spring │ │ │ │ ├── ComponentFailureAnalyzer.kt │ │ │ │ ├── SourceDownloaderSpringApplication.kt │ │ │ │ ├── SpringObjectWrapperContainer.kt │ │ │ │ ├── SpringSourceDownloaderProperties.kt │ │ │ │ ├── api │ │ │ │ ├── ApplicationController.kt │ │ │ │ ├── ComponentController.kt │ │ │ │ ├── ControllerExceptionHandler.kt │ │ │ │ ├── ProcessingContentController.kt │ │ │ │ ├── ProcessorController.kt │ │ │ │ ├── SpaController.kt │ │ │ │ └── TargetPathController.kt │ │ │ │ ├── component │ │ │ │ └── SpringWebFrameworkAdapter.kt │ │ │ │ ├── config │ │ │ │ ├── ApplicationConfiguration.kt │ │ │ │ ├── ExposedConfiguration.kt │ │ │ │ ├── StorageConfiguration.kt │ │ │ │ └── WebConfiguration.kt │ │ │ │ └── converter │ │ │ │ └── ComponentsConverter.kt │ │ └── resources │ │ │ ├── META-INF │ │ │ └── spring.factories │ │ │ └── application.yaml │ │ └── test │ │ ├── kotlin │ │ └── io │ │ │ └── github │ │ │ └── shoaky │ │ │ └── sourcedownloader │ │ │ └── application │ │ │ └── spring │ │ │ └── api │ │ │ ├── ApplicationControllerTest.kt │ │ │ ├── ComponentControllerTest.kt │ │ │ ├── ProcessingContentControllerTest.kt │ │ │ ├── ProcessorControllerTest.kt │ │ │ └── TestConfiguration.kt │ │ └── resources │ │ └── config.yaml └── vertx │ ├── build.gradle.kts │ └── src │ └── main │ └── kotlin │ └── io │ └── github │ └── shoaky │ └── sourcedownloader │ └── application │ └── vertx │ ├── ApplicationConfig.kt │ ├── ApplicationContext.kt │ ├── HttpServerVerticle.kt │ ├── SourceDownloaderConfig.kt │ ├── SourceDownloaderVertxApplication.kt │ ├── VertxWebhookAdapter.kt │ └── handlers │ ├── ComponentEndpointHandlers.kt │ ├── CoreApplicationHandlers.kt │ ├── FailureDetailHandler.kt │ ├── ProblemDetail.kt │ ├── ProblemDetailFailureHandler.kt │ ├── ProcessingContentHandlers.kt │ ├── ProcessorEndpointHandlers.kt │ └── TargetPathHandlers.kt ├── build.gradle.kts ├── common ├── build.gradle.kts └── src │ └── main │ └── kotlin │ └── io │ └── github │ └── shoaky │ └── common │ └── Generated.kt ├── core ├── build.gradle.kts └── src │ ├── main │ ├── kotlin │ │ └── io │ │ │ └── github │ │ │ └── shoaky │ │ │ └── sourcedownloader │ │ │ ├── CoreApplication.kt │ │ │ ├── component │ │ │ ├── Composites.kt │ │ │ ├── DeleteEmptyDirectory.kt │ │ │ ├── ExpressionFileFilter.kt │ │ │ ├── ExpressionItemContentFilter.kt │ │ │ ├── ExpressionItemFilter.kt │ │ │ ├── FileDirectoryExistsDetector.kt │ │ │ ├── FileReplacementDecider.kt │ │ │ ├── FileSizeReplacementDecider.kt │ │ │ ├── ForceTrimmer.kt │ │ │ ├── GeneralFileMover.kt │ │ │ ├── HardlinkFileMover.kt │ │ │ ├── KeywordIntegration.kt │ │ │ ├── MappedFileTagger.kt │ │ │ ├── RegexTrimmer.kt │ │ │ ├── RunCommand.kt │ │ │ ├── SendHttpRequest.kt │ │ │ ├── SimpleFileExistsDetector.kt │ │ │ ├── TouchItemDirectory.kt │ │ │ ├── downloader │ │ │ │ ├── HttpDownloader.kt │ │ │ │ ├── MockDownloader.kt │ │ │ │ ├── NoneDownloader.kt │ │ │ │ └── UrlDownloader.kt │ │ │ ├── provider │ │ │ │ ├── RegexVariableProvider.kt │ │ │ │ └── SequenceVariableProvider.kt │ │ │ ├── replacer │ │ │ │ ├── FullWidthReplacer.kt │ │ │ │ ├── RegexVariableReplacer.kt │ │ │ │ └── WindowsPathReplacer.kt │ │ │ ├── resolver │ │ │ │ ├── SystemFileResolver.kt │ │ │ │ └── UrlFileResolver.kt │ │ │ ├── source │ │ │ │ ├── FixedSource.kt │ │ │ │ ├── SystemFileSource.kt │ │ │ │ └── UriSource.kt │ │ │ ├── supplier │ │ │ │ ├── AlwaysReplaceSupplier.kt │ │ │ │ ├── CompositeItemFileResolverSupplier.kt │ │ │ │ ├── CronTriggerSupplier.kt │ │ │ │ ├── DeleteEmptyDirectorySupplier.kt │ │ │ │ ├── ExpressionFileFilterSupplier.kt │ │ │ │ ├── ExpressionItemContentFilterSupplier.kt │ │ │ │ ├── ExpressionItemFilterSupplier.kt │ │ │ │ ├── FileSizeReplacementDeciderSupplier.kt │ │ │ │ ├── FixedScheduleTriggerSupplier.kt │ │ │ │ ├── FixedSourceSupplier.kt │ │ │ │ ├── ForceTrimmerSupplier.kt │ │ │ │ ├── FullWidthReplacerSupplier.kt │ │ │ │ ├── GeneralFileMoverSupplier.kt │ │ │ │ ├── HardlinkFileMoverSupplier.kt │ │ │ │ ├── HttpDownloaderSupplier.kt │ │ │ │ ├── ItemDirectoryExistsDetectorSupplier.kt │ │ │ │ ├── KeywordIntegrationSupplier.kt │ │ │ │ ├── MappedFileTaggerSupplier.kt │ │ │ │ ├── MockDownloaderSupplier.kt │ │ │ │ ├── NeverReplaceSupplier.kt │ │ │ │ ├── NoneDownloaderSupplier.kt │ │ │ │ ├── RegexTrimmerSupplier.kt │ │ │ │ ├── RegexVariableProviderSupplier.kt │ │ │ │ ├── RegexVariableReplacerSupplier.kt │ │ │ │ ├── RunCommandSupplier.kt │ │ │ │ ├── SendHttpRequestSupplier.kt │ │ │ │ ├── SequenceVariableProviderSupplier.kt │ │ │ │ ├── SystemFileResolverSupplier.kt │ │ │ │ ├── SystemFileSourceSupplier.kt │ │ │ │ ├── TouchItemDirectorySupplier.kt │ │ │ │ ├── UriSourceSupplier.kt │ │ │ │ ├── UrlDownloaderSupplier.kt │ │ │ │ ├── UrlFileResolverSupplier.kt │ │ │ │ ├── WebhookTriggerSupplier.kt │ │ │ │ └── WindowsPathReplacerSupplier.kt │ │ │ └── trigger │ │ │ │ ├── CronTrigger.kt │ │ │ │ ├── FixedScheduleTrigger.kt │ │ │ │ ├── HoldingTaskTrigger.kt │ │ │ │ └── WebhookTrigger.kt │ │ │ ├── config │ │ │ └── SourceDownloaderProperties.kt │ │ │ ├── core │ │ │ ├── AllDeclaredConfig.kt │ │ │ ├── CachedVariableProvider.kt │ │ │ ├── DefaultCoreContext.kt │ │ │ ├── DefaultPluginContext.kt │ │ │ ├── InstanceConfig.kt │ │ │ ├── ObjectWrapperContainer.kt │ │ │ ├── PersistentPointer.kt │ │ │ ├── PluginLoader.kt │ │ │ ├── PluginManager.kt │ │ │ ├── ProcessingContent.kt │ │ │ ├── ProcessingStorage.kt │ │ │ ├── ProcessorConfig.kt │ │ │ ├── ProcessorConfigStorage.kt │ │ │ ├── ProcessorSourceState.kt │ │ │ ├── SimpleObjectWrapperContainer.kt │ │ │ ├── VariableReplacerConfig.kt │ │ │ ├── VariableReplacerConfigDeserializer.kt │ │ │ ├── YamlConfigOperator.kt │ │ │ ├── component │ │ │ │ ├── ComponentConfig.kt │ │ │ │ ├── ComponentConfigStorage.kt │ │ │ │ ├── ComponentFailureType.kt │ │ │ │ ├── ComponentId.kt │ │ │ │ ├── ComponentManager.kt │ │ │ │ ├── ComponentWrapper.kt │ │ │ │ ├── ConfigOperator.kt │ │ │ │ ├── DefaultComponentManager.kt │ │ │ │ ├── DefaultComponents.kt │ │ │ │ ├── DefaultInstanceManager.kt │ │ │ │ ├── InstanceConfigStorage.kt │ │ │ │ ├── ListenerConfig.kt │ │ │ │ ├── ListenerConfigDeserializer.kt │ │ │ │ ├── NamedProcessListener.kt │ │ │ │ ├── ObjectWrapper.kt │ │ │ │ ├── ProcessorWrapper.kt │ │ │ │ └── SourceHashingItemFilter.kt │ │ │ ├── expression │ │ │ │ ├── CelCompiledExpression.kt │ │ │ │ ├── CelCompiledExpressionFactory.kt │ │ │ │ ├── CelLibrary.kt │ │ │ │ ├── CompiledExpression.kt │ │ │ │ ├── CompiledExpressionFactory.kt │ │ │ │ ├── ExpressionType.kt │ │ │ │ ├── Expressions.kt │ │ │ │ └── VariableType.kt │ │ │ ├── file │ │ │ │ ├── CoreFileContent.kt │ │ │ │ ├── CoreItemContent.kt │ │ │ │ ├── CorePathPattern.kt │ │ │ │ ├── FileContentStatus.kt │ │ │ │ ├── IncludingTargetPathsFileMover.kt │ │ │ │ ├── ReadonlyFileMover.kt │ │ │ │ ├── RenameVariables.kt │ │ │ │ ├── Renamer.kt │ │ │ │ └── VariableErrorStrategy.kt │ │ │ └── processor │ │ │ │ ├── CoreProcessContext.kt │ │ │ │ ├── DefaultProcessorManager.kt │ │ │ │ ├── DirectDownloader.kt │ │ │ │ ├── DownloadStatus.kt │ │ │ │ ├── DryRunOptions.kt │ │ │ │ ├── KeyFilterVariableReplacer.kt │ │ │ │ ├── ListenerMode.kt │ │ │ │ ├── ProcessStage.kt │ │ │ │ ├── ProcessStat.kt │ │ │ │ ├── ProcessingTargetPath.kt │ │ │ │ ├── ProcessorManager.kt │ │ │ │ ├── ProcessorOptions.kt │ │ │ │ ├── ProcessorRuntime.kt │ │ │ │ ├── ProcessorSafeRunner.kt │ │ │ │ ├── SourceFilePartition.kt │ │ │ │ ├── SourceItemPartition.kt │ │ │ │ ├── SourceProcessor.kt │ │ │ │ ├── TargetPathRelationSupport.kt │ │ │ │ ├── VariableConflictStrategy.kt │ │ │ │ └── VariableProvidersAggregation.kt │ │ │ ├── nativeimage │ │ │ └── CoreFeature.kt │ │ │ ├── repo │ │ │ ├── ProcessingQuery.kt │ │ │ └── exposed │ │ │ │ ├── EnumColumnType.kt │ │ │ │ ├── ExposedProcessingStorage.kt │ │ │ │ ├── JsonColumnType.kt │ │ │ │ ├── Processing.kt │ │ │ │ ├── ProcessorSourceStates.kt │ │ │ │ └── TargetPaths.kt │ │ │ ├── service │ │ │ ├── ComponentCreateBody.kt │ │ │ ├── ComponentInfo.kt │ │ │ ├── ComponentService.kt │ │ │ ├── EventItem.kt │ │ │ ├── NotFoundException.kt │ │ │ ├── PageRequest.kt │ │ │ ├── ProcessingContentService.kt │ │ │ ├── ProcessorInfo.kt │ │ │ ├── ProcessorService.kt │ │ │ ├── ProcessorState.kt │ │ │ ├── Scroll.kt │ │ │ └── UpdateProcessingContent.kt │ │ │ └── util │ │ │ ├── Commons.kt │ │ │ ├── EnumValue.kt │ │ │ ├── Events.kt │ │ │ ├── JsonComparator.kt │ │ │ ├── NoLock.kt │ │ │ ├── RestorableConfigOperator.kt │ │ │ ├── StopWatch.kt │ │ │ └── jackson │ │ │ └── PathPatternDeserializer.kt │ └── resources │ │ ├── META-INF │ │ └── native-image │ │ │ └── io │ │ │ └── github │ │ │ └── shoaky │ │ │ └── source-downloader │ │ │ └── core │ │ │ └── native-image.properties │ │ ├── db │ │ └── migration │ │ │ ├── V1__initialise_database.sql │ │ │ ├── V2__update_index.sql │ │ │ ├── V3__change_col_name.sql │ │ │ ├── V4__add_processing_index.sql │ │ │ ├── V5_0_1__add_index.sql │ │ │ └── V5__change_col_name.sql │ │ └── default-component.yaml │ └── test │ ├── kotlin │ └── io │ │ └── github │ │ └── shoaky │ │ └── sourcedownloader │ │ ├── Tests.kt │ │ ├── component │ │ ├── DeleteEmptyDirectoryTest.kt │ │ ├── ExpressionFileFilterTest.kt │ │ ├── ExpressionItemContentFilterTest.kt │ │ ├── ExpressionItemFilterTest.kt │ │ ├── FileSizeReplacementDeciderTest.kt │ │ ├── FixedScheduleTriggerTest.kt │ │ ├── ForceTrimmerTest.kt │ │ ├── GeneralFileMoverTest.kt │ │ ├── HardlinkFileMoverTest.kt │ │ ├── ItemDirectoryExistsDetectorTest.kt │ │ ├── KeywordIntegrationTest.kt │ │ ├── RunCommandTest.kt │ │ ├── SendHttpRequestTest.kt │ │ ├── SystemFileSourceTest.kt │ │ ├── UrlDownloaderTest.kt │ │ ├── provider │ │ │ └── RegexVariableProviderTest.kt │ │ └── replacer │ │ │ └── FullWidthReplacerTest.kt │ │ ├── core │ │ ├── CorePathPatternTest.kt │ │ ├── DefaultInstanceManagerTest.kt │ │ ├── PersistentPointerTest.kt │ │ ├── ProcessorSourceStateTest.kt │ │ ├── RegexVariableReplacerSupplierTest.kt │ │ ├── WindowsPathReplacerTest.kt │ │ ├── YamlConfigOperatorTest.kt │ │ ├── component │ │ │ ├── DefaultComponentManagerTest.kt │ │ │ ├── ListenerConfigDeserializerTest.kt │ │ │ └── VariableReplacerConfigDeserializerTest.kt │ │ ├── expression │ │ │ └── CelLibraryTest.kt │ │ └── processor │ │ │ ├── NameReplacePatternVariablesTest.kt │ │ │ ├── RenamerTest.kt │ │ │ └── VariableProvidersAggregationTest.kt │ │ ├── integration │ │ ├── SourceProcessorTest.kt │ │ └── support │ │ │ ├── DelayItemDownloader.kt │ │ │ ├── DelayItemDownloaderSupplier.kt │ │ │ ├── Item2ReplaceDeciderSupplier.kt │ │ │ ├── SourceProcessorTest.kt │ │ │ ├── TestDirErrorDownloader.kt │ │ │ └── TestDirErrorDownloaderSupplier.kt │ │ └── util │ │ ├── EnumValueTest.kt │ │ └── JsonComparatorTest.kt │ └── resources │ ├── config-test2.yaml │ ├── config.yaml │ ├── mockito-extensions │ └── org.mockito.plugins.MockMaker │ └── script │ ├── test.ps1 │ └── test.sh ├── docs └── api.md ├── examples ├── anime-local.yaml ├── anime-remote.yaml ├── config-example.yaml ├── fanbox.yaml ├── mikan-bangumi.yaml ├── pixiv.yaml └── telegram.yaml ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── plugin-maven-example ├── pom.xml └── src │ └── main │ ├── java │ └── io │ │ └── github │ │ └── shoaky │ │ └── sourcedownloader │ │ └── maven │ │ └── JavaPlugin.java │ ├── kotlin │ └── io │ │ └── github │ │ └── shoaky │ │ └── sourcedownloader │ │ └── maven │ │ └── KotlinPlugin.kt │ └── resources │ └── META-INF │ └── services │ └── io.github.shoaky.sourcedownloader.sdk.Plugin ├── plugins ├── build.gradle.kts ├── common-plugin │ ├── build.gradle.kts │ └── src │ │ ├── main │ │ ├── kotlin │ │ │ └── io │ │ │ │ └── github │ │ │ │ └── shoaky │ │ │ │ └── sourcedownloader │ │ │ │ ├── common │ │ │ │ ├── CommonManualSource.kt │ │ │ │ ├── CommonPlugin.kt │ │ │ │ ├── EmbyImageTagger.kt │ │ │ │ ├── EpisodeVariableProvider.kt │ │ │ │ ├── HtmlFileResolver.kt │ │ │ │ ├── LanguageVariableProvider.kt │ │ │ │ ├── MediaTypeExistsDetector.kt │ │ │ │ ├── ResolutionVariableProvider.kt │ │ │ │ ├── SeasonVariableProvider.kt │ │ │ │ ├── WebdavFileMover.kt │ │ │ │ ├── ai │ │ │ │ │ └── AiVariableProvider.kt │ │ │ │ ├── anime │ │ │ │ │ ├── AnimeFileFilter.kt │ │ │ │ │ ├── AnimeReplacementDecider.kt │ │ │ │ │ ├── AnimeTagger.kt │ │ │ │ │ ├── AnimeTitleVariableProvider.kt │ │ │ │ │ ├── AnimeVariableProvider.kt │ │ │ │ │ ├── BgmTvClientInstanceFactory.kt │ │ │ │ │ ├── BgmTvVariableProvider.kt │ │ │ │ │ ├── ChiiVariableProvider.kt │ │ │ │ │ ├── MikanClient.kt │ │ │ │ │ ├── MikanSource.kt │ │ │ │ │ ├── MikanSupportFactory.kt │ │ │ │ │ ├── MikanVariableProvider.kt │ │ │ │ │ ├── Titles.kt │ │ │ │ │ ├── TmdbVariableProvider.kt │ │ │ │ │ └── extractor │ │ │ │ │ │ ├── AllBracketTitleExtractor.kt │ │ │ │ │ │ ├── AniTitleExtractor.kt │ │ │ │ │ │ ├── DefaultTitleExtractor.kt │ │ │ │ │ │ ├── Extractor.kt │ │ │ │ │ │ └── SeparateTitleExtractor.kt │ │ │ │ ├── anitom │ │ │ │ │ └── AnitomVariableProvider.kt │ │ │ │ ├── bilibili │ │ │ │ │ ├── BbDownIntegration.kt │ │ │ │ │ ├── BilibiliPointer.kt │ │ │ │ │ ├── BilibiliSource.kt │ │ │ │ │ └── MediaItemPointer.kt │ │ │ │ ├── dlsite │ │ │ │ │ ├── DlsiteVariableProvider.kt │ │ │ │ │ └── DoujinTitleTrimmer.kt │ │ │ │ ├── fanbox │ │ │ │ │ ├── CreatorPointer.kt │ │ │ │ │ ├── FanboxIntegration.kt │ │ │ │ │ └── FanboxPointer.kt │ │ │ │ ├── getchu │ │ │ │ │ ├── GetchuClient.kt │ │ │ │ │ ├── GetchuDetailItem.kt │ │ │ │ │ ├── GetchuSearchItem.kt │ │ │ │ │ └── GetchuVariableProvider.kt │ │ │ │ ├── patreon │ │ │ │ │ ├── CampaignPointer.kt │ │ │ │ │ ├── PatreonIntegration.kt │ │ │ │ │ └── PatreonPointer.kt │ │ │ │ ├── pixiv │ │ │ │ │ ├── BookmarkPointer.kt │ │ │ │ │ ├── IllustrationPointer.kt │ │ │ │ │ ├── PixivIntegration.kt │ │ │ │ │ └── PixivPointer.kt │ │ │ │ ├── rss │ │ │ │ │ ├── JackettSource.kt │ │ │ │ │ ├── RssConfig.kt │ │ │ │ │ └── RssSource.kt │ │ │ │ ├── supplier │ │ │ │ │ ├── AiVariableProviderSupplier.kt │ │ │ │ │ ├── AnimeFileFilterSupplier.kt │ │ │ │ │ ├── AnimeReplacementDeciderSupplier.kt │ │ │ │ │ ├── AnimeTaggerSupplier.kt │ │ │ │ │ ├── AnimeTitleVariableProviderSupplier.kt │ │ │ │ │ ├── AnimeVariableProviderSupplier.kt │ │ │ │ │ ├── AnitomVariableProviderSupplier.kt │ │ │ │ │ ├── BbDownIntegrationSupplier.kt │ │ │ │ │ ├── BgmTvVariableProviderSupplier.kt │ │ │ │ │ ├── BilibiliSourceSupplier.kt │ │ │ │ │ ├── ChiiVariableProviderSupplier.kt │ │ │ │ │ ├── CommonManualSourceSupplier.kt │ │ │ │ │ ├── DlsiteVariableProviderSupplier.kt │ │ │ │ │ ├── DoujinTitleTrimmerSupplier.kt │ │ │ │ │ ├── EmbyImageTaggerSupplier.kt │ │ │ │ │ ├── EpisodeVariableProviderSupplier.kt │ │ │ │ │ ├── FanboxIntegrationSupplier.kt │ │ │ │ │ ├── GetchuVariableProviderSupplier.kt │ │ │ │ │ ├── HtmlFileResolverSupplier.kt │ │ │ │ │ ├── JackettSourceSupplier.kt │ │ │ │ │ ├── LanguageVariableProviderSupplier.kt │ │ │ │ │ ├── MediaTypeExistsDetectorSupplier.kt │ │ │ │ │ ├── MikanSourceSupplier.kt │ │ │ │ │ ├── MikanVariableProviderSupplier.kt │ │ │ │ │ ├── PatreonIntegrationSupplier.kt │ │ │ │ │ ├── PixivIntegrationSupplier.kt │ │ │ │ │ ├── QbittorrentDownloaderSupplier.kt │ │ │ │ │ ├── ResolutionVariableProviderSupplier.kt │ │ │ │ │ ├── RssSourceSupplier.kt │ │ │ │ │ ├── SeasonVariableProviderSupplier.kt │ │ │ │ │ ├── SimpleFileTaggerSupplier.kt │ │ │ │ │ ├── TmdbVariableProviderSupplier.kt │ │ │ │ │ ├── TorrentFileResolverSupplier.kt │ │ │ │ │ ├── TransmissionDownloaderSupplier.kt │ │ │ │ │ ├── WebdavMoverSupplier.kt │ │ │ │ │ └── YoutubeDLIntegrationSupplier.kt │ │ │ │ ├── tagger │ │ │ │ │ └── SimpleFileTagger.kt │ │ │ │ ├── torrent │ │ │ │ │ ├── QbittorrentDownloader.kt │ │ │ │ │ ├── TorrentFileResolver.kt │ │ │ │ │ └── TransmissionDownloader.kt │ │ │ │ └── ydl │ │ │ │ │ └── YoutubeDLIntegration.kt │ │ │ │ ├── external │ │ │ │ ├── anilist │ │ │ │ │ ├── AnilistClient.kt │ │ │ │ │ ├── AnilistRequest.kt │ │ │ │ │ └── Media.kt │ │ │ │ ├── bangumi │ │ │ │ │ ├── BangumiRequest.kt │ │ │ │ │ ├── BgmTvApiClient.kt │ │ │ │ │ ├── GetSubjectRequest.kt │ │ │ │ │ ├── SearchSubjectBody.kt │ │ │ │ │ ├── SearchSubjectRequest.kt │ │ │ │ │ ├── SearchSubjectV0Body.kt │ │ │ │ │ ├── SearchSubjectV0Request.kt │ │ │ │ │ ├── Subject.kt │ │ │ │ │ ├── SubjectItem.kt │ │ │ │ │ └── SubjectV0Item.kt │ │ │ │ ├── bbdown │ │ │ │ │ ├── AddTask.kt │ │ │ │ │ ├── BbDownClient.kt │ │ │ │ │ ├── DeleteTask.kt │ │ │ │ │ └── GetTask.kt │ │ │ │ ├── bilibili │ │ │ │ │ ├── BilibiliClient.kt │ │ │ │ │ ├── BilibiliResponse.kt │ │ │ │ │ ├── GetFavorites.kt │ │ │ │ │ └── Media.kt │ │ │ │ ├── chii │ │ │ │ │ ├── ChiiClient.kt │ │ │ │ │ └── ChiiRequest.kt │ │ │ │ ├── dlsite │ │ │ │ │ ├── DlsiteClient.kt │ │ │ │ │ ├── DlsiteWorkInfo.kt │ │ │ │ │ └── SearchWork.kt │ │ │ │ ├── fanbox │ │ │ │ │ ├── CreatorPaginateRequest.kt │ │ │ │ │ ├── CreatorPostsRequest.kt │ │ │ │ │ ├── FanboxClient.kt │ │ │ │ │ ├── FanboxRequest.kt │ │ │ │ │ ├── FanboxResponse.kt │ │ │ │ │ ├── HomePostsRequest.kt │ │ │ │ │ ├── PostInfoRequest.kt │ │ │ │ │ ├── SupportingPostsRequest.kt │ │ │ │ │ └── SupportingRequest.kt │ │ │ │ ├── openai │ │ │ │ │ ├── AiClient.kt │ │ │ │ │ └── ChatCompletion.kt │ │ │ │ ├── patreon │ │ │ │ │ ├── PatreonClient.kt │ │ │ │ │ ├── PatreonEntity.kt │ │ │ │ │ ├── PatreonRequest.kt │ │ │ │ │ ├── PatreonResponse.kt │ │ │ │ │ ├── PledgeRequest.kt │ │ │ │ │ ├── Post.kt │ │ │ │ │ ├── PostRequest.kt │ │ │ │ │ ├── PostResponse.kt │ │ │ │ │ ├── PostTagRequest.kt │ │ │ │ │ ├── PostsRequest.kt │ │ │ │ │ └── PostsResponse.kt │ │ │ │ ├── pixiv │ │ │ │ │ ├── GetBookmarksRequest.kt │ │ │ │ │ ├── GetFollowing.kt │ │ │ │ │ ├── GetFollowingRequest.kt │ │ │ │ │ ├── GetIllustrationRequest.kt │ │ │ │ │ ├── GetIllustrationsRequest.kt │ │ │ │ │ ├── GetUgoiraMetaRequest.kt │ │ │ │ │ ├── GetUserAll.kt │ │ │ │ │ ├── GetUserAllRequest.kt │ │ │ │ │ ├── IllustrationPagesRequest.kt │ │ │ │ │ ├── PixivClient.kt │ │ │ │ │ ├── PixivResponse.kt │ │ │ │ │ └── PixivUser.kt │ │ │ │ ├── qbittorrent │ │ │ │ │ ├── AppGetDefaultSavePathRequest.kt │ │ │ │ │ ├── LoginRequest.kt │ │ │ │ │ ├── QbittorrentClient.kt │ │ │ │ │ ├── QbittorrentRequest.kt │ │ │ │ │ ├── TorrentDeleteRequest.kt │ │ │ │ │ ├── TorrentFile.kt │ │ │ │ │ ├── TorrentFilePrioRequest.kt │ │ │ │ │ ├── TorrentFilesRequest.kt │ │ │ │ │ ├── TorrentInfo.kt │ │ │ │ │ ├── TorrentInfoRequest.kt │ │ │ │ │ ├── TorrentProperties.kt │ │ │ │ │ ├── TorrentPropertiesRequest.kt │ │ │ │ │ ├── TorrentsAddRequest.kt │ │ │ │ │ ├── TorrentsRenameFileRequest.kt │ │ │ │ │ └── TorrentsSetLocationRequest.kt │ │ │ │ ├── rss │ │ │ │ │ ├── ItemExt.kt │ │ │ │ │ └── RssExtReader.kt │ │ │ │ ├── season │ │ │ │ │ ├── ContainsSeasonKeyword.kt │ │ │ │ │ ├── ExtractTitleSeasonParser.kt │ │ │ │ │ ├── GeneralSeasonParser.kt │ │ │ │ │ ├── LastStringSeasonParser.kt │ │ │ │ │ ├── SeasonParser.kt │ │ │ │ │ ├── SeasonResult.kt │ │ │ │ │ ├── SeasonSupport.kt │ │ │ │ │ ├── SpSeasonParser.kt │ │ │ │ │ └── TmdbSeasonParser.kt │ │ │ │ ├── tmdb │ │ │ │ │ ├── GetSeasonDetail.kt │ │ │ │ │ ├── GetTvShow.kt │ │ │ │ │ ├── PageResult.kt │ │ │ │ │ ├── SearchResult.kt │ │ │ │ │ ├── SearchTvShow.kt │ │ │ │ │ ├── TmdbClient.kt │ │ │ │ │ ├── TmdbSeason.kt │ │ │ │ │ └── TvShow.kt │ │ │ │ ├── transmission │ │ │ │ │ ├── Seassion.kt │ │ │ │ │ ├── SessionGet.kt │ │ │ │ │ ├── TestCsrfRequest.kt │ │ │ │ │ ├── Torrent.kt │ │ │ │ │ ├── TorrentAdd.kt │ │ │ │ │ ├── TorrentDelete.kt │ │ │ │ │ ├── TorrentGet.kt │ │ │ │ │ ├── TorrentRenamePath.kt │ │ │ │ │ ├── TorrentSet.kt │ │ │ │ │ ├── TorrentSetLocation.kt │ │ │ │ │ ├── TransmissionClient.kt │ │ │ │ │ ├── TransmissionRequest.kt │ │ │ │ │ └── TransmissionResponse.kt │ │ │ │ ├── webdav │ │ │ │ │ ├── Requests.kt │ │ │ │ │ └── WebdavClient.kt │ │ │ │ └── ydl │ │ │ │ │ ├── CancelDownload.kt │ │ │ │ │ ├── DownloadInfo.kt │ │ │ │ │ ├── DownloadsResponse.kt │ │ │ │ │ ├── FileFormate.kt │ │ │ │ │ ├── GetDownloads.kt │ │ │ │ │ ├── GetFileFormats.kt │ │ │ │ │ ├── SubmitDownload.kt │ │ │ │ │ ├── YoutubeDLClient.kt │ │ │ │ │ └── YoutubeDLResponse.kt │ │ │ │ ├── nativeimage │ │ │ │ └── CommonPluginFeature.kt │ │ │ │ └── util │ │ │ │ └── UnescapeHtmlDeserializer.kt │ │ └── resources │ │ │ ├── META-INF │ │ │ ├── native-image │ │ │ │ └── io.github.shoaky │ │ │ │ │ └── source-downloader │ │ │ │ │ ├── native-image.properties │ │ │ │ │ ├── reflect-config.json │ │ │ │ │ └── resource-config.json │ │ │ └── services │ │ │ │ └── io.github.shoaky.sourcedownloader.sdk.plugin.Plugin │ │ │ ├── custom-mimetypes.xml │ │ │ └── sd-description.yaml │ │ └── test │ │ ├── kotlin │ │ └── io │ │ │ └── github │ │ │ └── shoaky │ │ │ └── sourcedownloader │ │ │ ├── Support.kt │ │ │ ├── common │ │ │ ├── EpisodeVariableProviderTest.kt │ │ │ ├── LanguageVariableProviderTest.kt │ │ │ ├── MediaTypeExistsDetectorTest.kt │ │ │ ├── SeasonVariableProviderTest.kt │ │ │ ├── anime │ │ │ │ ├── AnimeFileFilterTest.kt │ │ │ │ ├── AnimeFileReplacerTest.kt │ │ │ │ ├── AnimeTaggerTest.kt │ │ │ │ ├── AnimeTitleVariableProviderTest.kt │ │ │ │ └── AnimeVariableProviderTest.kt │ │ │ ├── anitom │ │ │ │ └── AnitomVariableProviderTest.kt │ │ │ ├── dlsite │ │ │ │ ├── DlsiteVariableProviderTests.kt │ │ │ │ └── DoujinTitleTrimmerTest.kt │ │ │ ├── external │ │ │ │ └── season │ │ │ │ │ ├── SeasonChainTest.kt │ │ │ │ │ └── SeasonSupportTest.kt │ │ │ ├── mikan │ │ │ │ ├── MikanPointerTest.kt │ │ │ │ ├── MikanSourceTest.kt │ │ │ │ └── MikanVariableProviderTest.kt │ │ │ ├── rss │ │ │ │ ├── JackettSourceTest.kt │ │ │ │ └── RssSourceTest.kt │ │ │ ├── tagger │ │ │ │ └── SimpleFileTaggerTest.kt │ │ │ └── torrent │ │ │ │ ├── QbittorrentDownloaderTest.kt │ │ │ │ └── TransmissionDownloaderTest.kt │ │ │ └── external │ │ │ ├── anilist │ │ │ └── AnilistClientTest.kt │ │ │ ├── qbittorrent │ │ │ └── QbittorrentClientTest.kt │ │ │ ├── tmdb │ │ │ └── TmdbClientTest.kt │ │ │ └── transmission │ │ │ └── TransmissionClientTest.kt │ │ └── resources │ │ ├── RJ01042626.html │ │ ├── anime-tagger-data.csv │ │ ├── anime-title-test-data.csv │ │ ├── episode-test-data.csv │ │ ├── language │ │ ├── language-simple1.ass │ │ ├── language-simple1.srt │ │ ├── language-simple2.ass │ │ └── language-simple2.srt │ │ ├── mikan │ │ ├── 2906-data.json │ │ ├── 2976-data.json │ │ └── rss-data.json │ │ ├── raw-anime-title-data.csv │ │ ├── rss │ │ └── jackett.json │ │ ├── season-test-data.csv │ │ └── season-test.csv ├── foreign-plugin │ ├── build.gradle.kts │ └── src │ │ └── main │ │ ├── kotlin │ │ └── io │ │ │ └── github │ │ │ └── shoaky │ │ │ └── sourcedownloader │ │ │ └── foreign │ │ │ ├── ForeignIterator.kt │ │ │ ├── ForeignPatternVariables.kt │ │ │ ├── ForeignPlugin.kt │ │ │ ├── ForeignPointer.kt │ │ │ ├── ForeignStateClient.kt │ │ │ ├── component │ │ │ ├── ForeignDownloader.kt │ │ │ ├── ForeignFileContentFilter.kt │ │ │ ├── ForeignFileExistsDetector.kt │ │ │ ├── ForeignFileMover.kt │ │ │ ├── ForeignFileReplacementDecider.kt │ │ │ ├── ForeignFileTagger.kt │ │ │ ├── ForeignItemContentFilter.kt │ │ │ ├── ForeignItemFileResolver.kt │ │ │ ├── ForeignProcessListener.kt │ │ │ ├── ForeignSource.kt │ │ │ ├── ForeignSourceItemFilter.kt │ │ │ └── ForeignVariableProvider.kt │ │ │ ├── http │ │ │ └── HttpForeignStateClient.kt │ │ │ ├── methods │ │ │ ├── DownloaderForeignMethods.kt │ │ │ ├── SourceForeignMethods.kt │ │ │ └── VariableProviderMethods.kt │ │ │ └── supplier │ │ │ └── ForeignSourceSupplier.kt │ │ └── resources │ │ └── META-INF │ │ └── services │ │ └── io.github.shoaky.sourcedownloader.sdk.plugin.Plugin └── telegram4j-plugin │ ├── build.gradle.kts │ └── src │ ├── main │ ├── kotlin │ │ └── io │ │ │ └── github │ │ │ └── shoaky │ │ │ └── sourcedownloader │ │ │ └── telegram │ │ │ ├── ChatConfig.kt │ │ │ ├── ChatPointer.kt │ │ │ ├── Telegram4jPlugin.kt │ │ │ ├── TelegramClientInstanceFactory.kt │ │ │ ├── TelegramIntegration.kt │ │ │ ├── TelegramIntegrationSupplier.kt │ │ │ ├── TelegramMediaTagger.kt │ │ │ ├── TelegramMediaTaggerSupplier.kt │ │ │ ├── TelegramMessageFetcher.kt │ │ │ ├── TelegramPointer.kt │ │ │ ├── TelegramSource.kt │ │ │ ├── TelegramSourceSupplier.kt │ │ │ ├── TextStyleSupport.kt │ │ │ ├── auth │ │ │ └── Callbacks.kt │ │ │ └── util │ │ │ └── ProgressiveChannel.kt │ └── resources │ │ ├── META-INF │ │ └── services │ │ │ └── io.github.shoaky.sourcedownloader.sdk.plugin.Plugin │ │ └── sd-description.yaml │ └── test │ └── kotlin │ └── io │ └── github │ └── shoaky │ └── sourcedownloader │ └── telegram │ ├── ChatPointerTest.kt │ ├── ProgressiveChannelTest.kt │ ├── TelegramPointerTest.kt │ └── TelegramSourceTest.kt ├── sdk ├── build.gradle.kts └── src │ ├── main │ └── kotlin │ │ └── io │ │ └── github │ │ └── shoaky │ │ └── sourcedownloader │ │ └── sdk │ │ ├── Cache.kt │ │ ├── CacheLoader.kt │ │ ├── CoreContext.kt │ │ ├── DownloadOptions.kt │ │ ├── DownloadTask.kt │ │ ├── FileContent.kt │ │ ├── FileStatus.kt │ │ ├── FixedFileContent.kt │ │ ├── FixedItemContent.kt │ │ ├── InstanceFactory.kt │ │ ├── InstanceManager.kt │ │ ├── ItemContent.kt │ │ ├── ItemPointer.kt │ │ ├── MapPatternVariables.kt │ │ ├── NullPointer.kt │ │ ├── PatternVariables.kt │ │ ├── PointedItem.kt │ │ ├── ProcessContext.kt │ │ ├── ProcessingException.kt │ │ ├── ProcessorInfo.kt │ │ ├── ProcessorTask.kt │ │ ├── Properties.kt │ │ ├── SimpleItemPointer.kt │ │ ├── SourceFile.kt │ │ ├── SourceItem.kt │ │ ├── SourceItemConvertScript.kt │ │ ├── SourcePointer.kt │ │ ├── component │ │ ├── AlwaysLatestSource.kt │ │ ├── AsyncDownloader.kt │ │ ├── BatchFileMover.kt │ │ ├── BatchMoveResult.kt │ │ ├── ComponentException.kt │ │ ├── ComponentMetadata.kt │ │ ├── ComponentRule.kt │ │ ├── ComponentStateful.kt │ │ ├── ComponentSupplier.kt │ │ ├── ComponentTopType.kt │ │ ├── ComponentType.kt │ │ ├── ExpandSource.kt │ │ ├── FetchContext.kt │ │ ├── JsonSchema.kt │ │ ├── SdComponent.kt │ │ └── TorrentDownloader.kt │ │ ├── http │ │ ├── ApiClient.kt │ │ ├── BaseRequest.kt │ │ └── StatusCodes.kt │ │ ├── plugin │ │ ├── Plugin.kt │ │ ├── PluginContext.kt │ │ └── PluginDescription.kt │ │ └── util │ │ ├── Common.kt │ │ ├── ExpandIterator.kt │ │ ├── FlattenIterator.kt │ │ ├── Jackson.kt │ │ ├── TextClear.kt │ │ ├── http │ │ ├── BodyMapper.kt │ │ ├── BodyMappers.kt │ │ ├── BodyMappingException.kt │ │ ├── BodyWrapper.kt │ │ ├── CommonBodyHandler.kt │ │ ├── CommonBodyMapper.kt │ │ └── MappingInfo.kt │ │ └── masking │ │ ├── Masking.kt │ │ ├── MaskingMiddle.kt │ │ ├── StringMasking.kt │ │ └── StringMaskingSerializer.kt │ └── test │ └── kotlin │ └── io │ └── github │ └── shoaky │ └── sourcedownloader │ └── sdk │ ├── PatternVariablesTest.kt │ ├── PropertiesTest.kt │ ├── api │ └── ApiClientTest.kt │ ├── component │ ├── ComponentTypeTest.kt │ ├── ComponentsTest.kt │ └── RuleTest.kt │ └── util │ ├── ExpandIteratorTest.kt │ ├── FlattenIteratorTest.kt │ └── masking │ └── MaskingTest.kt └── settings.gradle.kts /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.{adoc,bat,groovy,html,java,js,jsp,kt,kts,md,properties,py,rb,sh,sql,svg,txt,xml,xsd,yaml,yml}] 4 | charset = utf-8 5 | 6 | [*.{groovy,java,kt,kts,xml,xsd}] 7 | indent_style = space 8 | indent_size = 4 9 | end_of_line = lf 10 | ij_any_line_comment_at_first_column = false 11 | ij_any_line_comment_add_space = true 12 | ij_any_line_comment_add_space_on_reformat = true 13 | ij_kotlin_keep_blank_lines_in_declarations = 1 14 | ij_kotlin_keep_blank_lines_in_code = 1 15 | ij_kotlin_blank_lines_after_class_header = 1 16 | 17 | [*.{yaml,yml}] 18 | indent_style = space 19 | indent_size = 2 20 | ij_any_line_comment_at_first_column = false 21 | ij_any_line_comment_add_space = true 22 | ij_any_line_comment_add_space_on_reformat = true -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # 2 | # https://help.github.com/articles/dealing-with-line-endings/ 3 | # 4 | # Linux start script should use lf 5 | /gradlew text eol=lf 6 | 7 | # These are Windows script files and should use crlf 8 | *.bat text eol=crlf 9 | **/test/resources/**/*.html linguist-vendored 10 | -------------------------------------------------------------------------------- /.github/workflows/publish-sdk.yml: -------------------------------------------------------------------------------- 1 | name: Publish SDK 2 | on: 3 | workflow_dispatch: 4 | inputs: 5 | manual_trigger: 6 | description: 'Manual trigger' 7 | required: false 8 | push: 9 | tags: 10 | - v* 11 | 12 | jobs: 13 | publish: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | - name: Set up JDK 21 18 | uses: actions/setup-java@v4 19 | with: 20 | distribution: 'zulu' 21 | java-version: '21' 22 | cache: 'gradle' 23 | - name: Build with Gradle 24 | run: ./gradlew sdk:build --no-daemon 25 | - name: Publish artifacts 26 | run: ./gradlew -PsonatypeUsername=${{ secrets.SONATYPE_USERNAME }} -PsonatypePassword=${{ secrets.SONATYPE_PASSWORD }} -PsigningKey="${{ secrets.SIGNING_KEY }}" -PsigningPassword=${{ secrets.SIGNING_PASSWORD }} sdk:publish --no-daemon -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | HELP.md 3 | target/ 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### H2 ### 8 | *.mv.db 9 | *.trace.db 10 | *.db 11 | 12 | ### STS ### 13 | .apt_generated 14 | .classpath 15 | .factorypath 16 | .project 17 | .settings 18 | .springBeans 19 | .sts4-cache 20 | 21 | ### IntelliJ IDEA ### 22 | .idea 23 | *.iws 24 | *.iml 25 | *.ipr 26 | 27 | ### NetBeans ### 28 | /nbproject/private/ 29 | /nbbuild/ 30 | /dist/ 31 | /nbdist/ 32 | /.nb-gradle/ 33 | build/ 34 | !**/src/main/**/build/ 35 | !**/src/test/**/build/ 36 | 37 | ### VS Code ### 38 | .vscode/ 39 | 40 | # Ignore Gradle project-specific cache directory 41 | .gradle 42 | 43 | # Ignore Gradle build output directory 44 | build 45 | 46 | # Ignore Kotlin compiler output directory 47 | .kotlin -------------------------------------------------------------------------------- /applications/minimize/src/main/kotlin/io/github/shoaky/sourcedownloader/application/minimize/ApplicationConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.application.minimize 2 | 3 | data class ApplicationConfig( 4 | val port: Int, 5 | val sourceDownloader: SourceDownloaderConfig, 6 | ) -------------------------------------------------------------------------------- /applications/minimize/src/main/kotlin/io/github/shoaky/sourcedownloader/application/minimize/EnhancedHttpHandler.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.application.minimize 2 | 3 | import com.sun.net.httpserver.HttpExchange 4 | import com.sun.net.httpserver.HttpHandler 5 | 6 | class EnhancedHttpHandler( 7 | private val block: (HttpExchange) -> Unit 8 | ) : HttpHandler { 9 | 10 | override fun handle(exchange: HttpExchange) { 11 | runCatching { 12 | block.invoke(exchange) 13 | }.onFailure { 14 | exchange.sendResponseHeaders(500, 0) 15 | } 16 | exchange.close() 17 | } 18 | } -------------------------------------------------------------------------------- /applications/minimize/src/main/kotlin/io/github/shoaky/sourcedownloader/application/minimize/SourceDownloaderConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.application.minimize 2 | 3 | import java.nio.file.Path 4 | import kotlin.io.path.Path 5 | 6 | data class SourceDownloaderConfig( 7 | val dataLocation: Path = run { 8 | val path = System.getenv("SOURCE_DOWNLOADER_DATA_LOCATION") ?: "" 9 | Path(path) 10 | } 11 | ) -------------------------------------------------------------------------------- /applications/spring/src/main/kotlin/io/github/shoaky/sourcedownloader/application/spring/ComponentFailureAnalyzer.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.application.spring 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentException 4 | import org.springframework.boot.diagnostics.AbstractFailureAnalyzer 5 | import org.springframework.boot.diagnostics.FailureAnalysis 6 | 7 | class ComponentFailureAnalyzer : AbstractFailureAnalyzer() { 8 | 9 | override fun analyze(rootFailure: Throwable, cause: ComponentException): FailureAnalysis { 10 | return FailureAnalysis( 11 | "Component failed to create", 12 | cause.message, 13 | cause 14 | ) 15 | } 16 | } -------------------------------------------------------------------------------- /applications/spring/src/main/kotlin/io/github/shoaky/sourcedownloader/application/spring/SpringSourceDownloaderProperties.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.application.spring 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties 4 | import java.nio.file.Path 5 | 6 | @ConfigurationProperties(prefix = "source-downloader") 7 | class SpringSourceDownloaderProperties( 8 | val dataLocation: Path 9 | ) -------------------------------------------------------------------------------- /applications/spring/src/main/kotlin/io/github/shoaky/sourcedownloader/application/spring/api/ApplicationController.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.application.spring.api 2 | 3 | import io.github.shoaky.sourcedownloader.CoreApplication 4 | import org.springframework.http.HttpStatus 5 | import org.springframework.web.bind.annotation.GetMapping 6 | import org.springframework.web.bind.annotation.RequestMapping 7 | import org.springframework.web.bind.annotation.ResponseStatus 8 | import org.springframework.web.bind.annotation.RestController 9 | 10 | /** 11 | * Application相关接口 12 | */ 13 | @RestController 14 | @RequestMapping("/api/application") 15 | class ApplicationController( 16 | private val application: CoreApplication, 17 | ) { 18 | 19 | /** 20 | * 重载应用 21 | */ 22 | @GetMapping("/reload") 23 | @ResponseStatus(HttpStatus.NO_CONTENT) 24 | fun reload() { 25 | application.reload() 26 | } 27 | 28 | } -------------------------------------------------------------------------------- /applications/spring/src/main/kotlin/io/github/shoaky/sourcedownloader/application/spring/api/SpaController.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.application.spring.api 2 | 3 | import org.springframework.stereotype.Controller 4 | import org.springframework.web.bind.annotation.GetMapping 5 | import org.springframework.web.bind.annotation.PathVariable 6 | 7 | /** 8 | * @suppress 9 | */ 10 | @Controller 11 | class SpaController { 12 | 13 | @GetMapping("/{path:[^.]*}") 14 | fun forward(@PathVariable path: String): String { 15 | return "forward:/" 16 | } 17 | } -------------------------------------------------------------------------------- /applications/spring/src/main/kotlin/io/github/shoaky/sourcedownloader/application/spring/api/TargetPathController.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.application.spring.api 2 | 3 | import io.github.shoaky.sourcedownloader.core.ProcessingStorage 4 | import org.springframework.web.bind.annotation.DeleteMapping 5 | import org.springframework.web.bind.annotation.RequestBody 6 | import org.springframework.web.bind.annotation.RequestMapping 7 | import org.springframework.web.bind.annotation.RestController 8 | 9 | /** 10 | * TargetPath相关的API 11 | */ 12 | @RestController 13 | @RequestMapping("/api/target-path") 14 | class TargetPathController( 15 | private val processingStorage: ProcessingStorage 16 | ) { 17 | 18 | /** 19 | * 删除指定TargetPath 20 | * @param paths 需要删除的TargetPath列表,支持前缀匹配删除 21 | */ 22 | @DeleteMapping 23 | fun deleteTargetPaths(@RequestBody paths: List) { 24 | processingStorage.deleteTargetPaths(paths, null) 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /applications/spring/src/main/kotlin/io/github/shoaky/sourcedownloader/application/spring/config/ExposedConfiguration.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.application.spring.config 2 | 3 | import org.jetbrains.exposed.spring.autoconfigure.ExposedAutoConfiguration 4 | import org.jetbrains.exposed.sql.DatabaseConfig 5 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration 6 | import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration 7 | import org.springframework.context.annotation.Bean 8 | import org.springframework.context.annotation.Configuration 9 | import org.springframework.context.annotation.Import 10 | 11 | @Import(value = [ExposedAutoConfiguration::class]) 12 | @EnableAutoConfiguration(exclude = [DataSourceTransactionManagerAutoConfiguration::class]) 13 | @Configuration 14 | private class ExposedConfiguration { 15 | 16 | @Bean 17 | fun databaseConfig() = DatabaseConfig { 18 | } 19 | } -------------------------------------------------------------------------------- /applications/spring/src/main/kotlin/io/github/shoaky/sourcedownloader/application/spring/config/WebConfiguration.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.application.spring.config 2 | 3 | import org.springframework.context.annotation.Configuration 4 | import org.springframework.context.annotation.Profile 5 | import org.springframework.web.servlet.config.annotation.CorsRegistry 6 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer 7 | 8 | @Profile("dev", "localhost", "development") 9 | @Configuration 10 | class WebConfiguration : WebMvcConfigurer { 11 | 12 | override fun addCorsMappings(registry: CorsRegistry) { 13 | registry.addMapping("/**") 14 | .allowedOrigins("*") 15 | .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") 16 | .allowedHeaders("*") 17 | .maxAge(3600) 18 | } 19 | } -------------------------------------------------------------------------------- /applications/spring/src/main/kotlin/io/github/shoaky/sourcedownloader/application/spring/converter/ComponentsConverter.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.application.spring.converter 2 | 3 | import io.github.shoaky.sourcedownloader.core.component.ComponentFailureType 4 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentTopType 5 | import io.github.shoaky.sourcedownloader.throwComponentException 6 | import org.springframework.core.convert.converter.Converter 7 | 8 | class ComponentsConverter : Converter { 9 | 10 | override fun convert(source: String): ComponentTopType { 11 | return ComponentTopType.fromName(source) ?: throwComponentException( 12 | "未知组件类型:$source", 13 | ComponentFailureType.UNKNOWN_TYPE 14 | ) 15 | } 16 | } -------------------------------------------------------------------------------- /applications/spring/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.diagnostics.FailureAnalyzer=\ 2 | io.github.shoaky.sourcedownloader.application.spring.ComponentFailureAnalyzer -------------------------------------------------------------------------------- /applications/spring/src/test/kotlin/io/github/shoaky/sourcedownloader/application/spring/api/ProcessingContentControllerTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.application.spring.api 2 | 3 | import org.springframework.beans.factory.annotation.Autowired 4 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc 5 | import org.springframework.boot.test.context.SpringBootTest 6 | import org.springframework.test.context.ActiveProfiles 7 | import org.springframework.test.web.servlet.MockMvc 8 | 9 | @SpringBootTest 10 | @ActiveProfiles("integration-test") 11 | @AutoConfigureMockMvc 12 | class ProcessingContentControllerTest { 13 | 14 | @Autowired 15 | private lateinit var mockMvc: MockMvc 16 | 17 | } -------------------------------------------------------------------------------- /applications/vertx/src/main/kotlin/io/github/shoaky/sourcedownloader/application/vertx/ApplicationConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.application.vertx 2 | 3 | import com.zaxxer.hikari.HikariConfig 4 | import io.vertx.core.http.HttpServerOptions 5 | 6 | data class ApplicationConfig( 7 | val server: HttpServerOptions = HttpServerOptions().also { 8 | it.port = 8080 9 | }, 10 | val sourceDownloader: SourceDownloaderConfig = SourceDownloaderConfig(), 11 | val datasource: HikariConfig = HikariConfig() 12 | ) { 13 | 14 | init { 15 | val path = sourceDownloader.dataLocation.toString().ifBlank { "." } 16 | datasource 17 | .also { 18 | it.driverClassName = "org.sqlite.JDBC" 19 | it.jdbcUrl = "jdbc:sqlite:$path/source-downloader.db" 20 | it.username = "sd" 21 | it.password = "sd" 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /applications/vertx/src/main/kotlin/io/github/shoaky/sourcedownloader/application/vertx/SourceDownloaderConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.application.vertx 2 | 3 | import java.nio.file.Path 4 | import kotlin.io.path.Path 5 | 6 | data class SourceDownloaderConfig( 7 | val dataLocation: Path = run { 8 | val path = System.getenv("SOURCE_DOWNLOADER_DATA_LOCATION") ?: "" 9 | Path(path) 10 | } 11 | ) -------------------------------------------------------------------------------- /applications/vertx/src/main/kotlin/io/github/shoaky/sourcedownloader/application/vertx/VertxWebhookAdapter.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.application.vertx 2 | 3 | import io.github.shoaky.sourcedownloader.component.trigger.WebhookTrigger 4 | import io.vertx.core.http.HttpMethod 5 | import io.vertx.ext.web.Router 6 | 7 | class VertxWebhookAdapter( 8 | private val router: Router 9 | ) : WebhookTrigger.Adapter { 10 | 11 | override fun registerEndpoint(path: String, method: String, handler: () -> Unit) { 12 | router.route(HttpMethod.valueOf(method), path) 13 | .blockingHandler(createRouteHandler { handler.invoke() }) 14 | } 15 | 16 | override fun unregisterEndpoint(path: String, method: String) { 17 | router.route(HttpMethod.valueOf(method), path) 18 | .remove() 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /applications/vertx/src/main/kotlin/io/github/shoaky/sourcedownloader/application/vertx/handlers/CoreApplicationHandlers.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.application.vertx.handlers 2 | 3 | import io.github.shoaky.sourcedownloader.CoreApplication 4 | import io.github.shoaky.sourcedownloader.application.vertx.createRouteHandler 5 | import io.vertx.core.Handler 6 | import io.vertx.ext.web.RoutingContext 7 | 8 | class CoreApplicationHandlers( 9 | private val coreApplication: CoreApplication 10 | ) { 11 | 12 | fun reload(): Handler { 13 | return createRouteHandler { 14 | coreApplication.reload() 15 | it.response().statusCode = 204 16 | it.response().end() 17 | } 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /applications/vertx/src/main/kotlin/io/github/shoaky/sourcedownloader/application/vertx/handlers/FailureDetailHandler.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.application.vertx.handlers 2 | 3 | import io.vertx.ext.web.RoutingContext 4 | 5 | interface FailureDetailHandler { 6 | 7 | fun handle(ctx: RoutingContext, failure: E): ProblemDetail 8 | 9 | } -------------------------------------------------------------------------------- /applications/vertx/src/main/kotlin/io/github/shoaky/sourcedownloader/application/vertx/handlers/TargetPathHandlers.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.application.vertx.handlers 2 | 3 | import io.github.shoaky.sourcedownloader.application.vertx.createRouteHandler 4 | import io.github.shoaky.sourcedownloader.core.ProcessingStorage 5 | import io.vertx.core.Handler 6 | import io.vertx.ext.web.RoutingContext 7 | 8 | class TargetPathHandlers( 9 | private val processingStorage: ProcessingStorage 10 | ) { 11 | 12 | fun deleteTargetPaths(): Handler { 13 | return createRouteHandler { ctx -> 14 | ctx.request().bodyHandler { buffer -> 15 | val paths = buffer.toJsonArray().map { it.toString() } 16 | processingStorage.deleteTargetPaths(paths, null) 17 | ctx.response().statusCode = 204 18 | ctx.response().end() 19 | } 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /common/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java") 3 | } 4 | 5 | group = "io.github.shoaky" 6 | 7 | dependencies { 8 | testImplementation(platform("org.junit:junit-bom:5.9.1")) 9 | testImplementation("org.junit.jupiter:junit-jupiter") 10 | } -------------------------------------------------------------------------------- /common/src/main/kotlin/io/github/shoaky/common/Generated.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.common 2 | 3 | @Retention(AnnotationRetention.RUNTIME) 4 | @Target( 5 | AnnotationTarget.CLASS, 6 | AnnotationTarget.FUNCTION, 7 | ) 8 | annotation class Generated() -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/component/ForceTrimmer.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.component.Trimmer 4 | 5 | object ForceTrimmer : Trimmer { 6 | 7 | override fun trim(value: String, expectLength: Int): String { 8 | return value.substring(0, expectLength) 9 | } 10 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/component/GeneralFileMover.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.FileContent 4 | import io.github.shoaky.sourcedownloader.sdk.SourceItem 5 | import io.github.shoaky.sourcedownloader.sdk.component.FileMover 6 | import kotlin.io.path.moveTo 7 | 8 | /** 9 | * 通过文件系统API移动文件 10 | */ 11 | object GeneralFileMover : FileMover { 12 | 13 | override fun move(sourceItem: SourceItem, file: FileContent): Boolean { 14 | file.fileDownloadPath.moveTo(file.targetPath()) 15 | return true 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/component/MappedFileTagger.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.SourceFile 4 | import io.github.shoaky.sourcedownloader.sdk.component.FileTagger 5 | import kotlin.io.path.name 6 | 7 | /** 8 | * 通过文件名(包括扩展名)映射来提供标签 9 | */ 10 | class MappedFileTagger( 11 | private val mapping: Map 12 | ) : FileTagger { 13 | 14 | override fun tag(sourceFile: SourceFile): String? { 15 | return mapping[sourceFile.path.name] 16 | } 17 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/component/RegexTrimmer.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.component.Trimmer 4 | 5 | class RegexTrimmer( 6 | val regex: Regex 7 | ) : Trimmer { 8 | 9 | override fun trim(value: String, expectLength: Int): String { 10 | return value.replace(regex, "") 11 | } 12 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/component/SimpleFileExistsDetector.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.ItemContent 4 | import io.github.shoaky.sourcedownloader.sdk.component.FileExistsDetector 5 | import io.github.shoaky.sourcedownloader.sdk.component.FileMover 6 | import java.nio.file.Path 7 | 8 | /** 9 | * 全部TargetPath存在时则认为Item存在 10 | */ 11 | object SimpleFileExistsDetector : FileExistsDetector { 12 | 13 | override fun exists(fileMover: FileMover, content: ItemContent): Map { 14 | val paths = content.fileContents.map { it.targetPath() } 15 | val exists = fileMover.exists(paths) 16 | return paths.zip(exists).associate { (path, exist) -> 17 | path to if (exist) path else null 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/component/downloader/NoneDownloader.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component.downloader 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.DownloadTask 4 | import io.github.shoaky.sourcedownloader.sdk.SourceFile 5 | import io.github.shoaky.sourcedownloader.sdk.SourceItem 6 | import io.github.shoaky.sourcedownloader.sdk.component.Downloader 7 | import java.nio.file.Path 8 | import kotlin.io.path.Path 9 | 10 | class NoneDownloader( 11 | val downloadPath: Path = Path("").toAbsolutePath() 12 | ) : Downloader { 13 | 14 | override fun submit(task: DownloadTask): Boolean { 15 | return true 16 | } 17 | 18 | override fun defaultDownloadPath(): Path { 19 | return downloadPath 20 | } 21 | 22 | override fun cancel(sourceItem: SourceItem, files: List) { 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/component/replacer/RegexVariableReplacer.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component.replacer 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.component.VariableReplacer 4 | 5 | class RegexVariableReplacer( 6 | private val regex: Regex, 7 | private val replacement: String, 8 | ) : VariableReplacer { 9 | 10 | override fun replace(key: String, value: String): String { 11 | return regex.replace(value, replacement) 12 | } 13 | 14 | override fun toString(): String { 15 | return this.regex.toString() + " -> " + this.replacement 16 | } 17 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/component/replacer/WindowsPathReplacer.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component.replacer 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.component.VariableReplacer 4 | 5 | object WindowsPathReplacer : VariableReplacer { 6 | 7 | private val charReplacements: Map = mapOf( 8 | '<' to '<', 9 | '>' to '>', 10 | ':' to ':', 11 | '"' to '"', 12 | '/' to '/', 13 | '\\' to '\', 14 | '|' to '|', 15 | '?' to '?', 16 | '*' to '*' 17 | ) 18 | 19 | override fun replace(key: String, value: String): String { 20 | val stringBuilder = StringBuilder() 21 | val charArray = value.toCharArray() 22 | for (char in charArray) { 23 | val c = charReplacements.getOrDefault(char, char) 24 | stringBuilder.append(c) 25 | } 26 | return stringBuilder.toString() 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/component/resolver/SystemFileResolver.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component.resolver 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.SourceFile 4 | import io.github.shoaky.sourcedownloader.sdk.SourceItem 5 | import io.github.shoaky.sourcedownloader.sdk.component.ItemFileResolver 6 | import kotlin.io.path.ExperimentalPathApi 7 | import kotlin.io.path.isDirectory 8 | import kotlin.io.path.toPath 9 | import kotlin.io.path.walk 10 | 11 | /** 12 | * SourceItem如果是文件夹,则解析文件夹下的所有文件 13 | * SourceItem本身就是文件,则解析自身为单个文件 14 | */ 15 | object SystemFileResolver : ItemFileResolver { 16 | 17 | override fun resolveFiles(sourceItem: SourceItem): List { 18 | val path = sourceItem.downloadUri.toPath() 19 | if (path.isDirectory()) { 20 | return path.walk().sorted().map { SourceFile(it) }.toList() 21 | } 22 | return listOf(SourceFile(path)) 23 | } 24 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/component/supplier/AlwaysReplaceSupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component.supplier 2 | 3 | import io.github.shoaky.sourcedownloader.component.AlwaysReplace 4 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 5 | import io.github.shoaky.sourcedownloader.sdk.Properties 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 7 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 8 | 9 | object AlwaysReplaceSupplier : ComponentSupplier { 10 | 11 | override fun apply(context: CoreContext, props: Properties): AlwaysReplace { 12 | return AlwaysReplace 13 | } 14 | 15 | override fun supportNoArgs(): Boolean = true 16 | 17 | override fun supplyTypes(): List { 18 | return listOf( 19 | ComponentType.fileReplacementDecider("always") 20 | ) 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/component/supplier/CronTriggerSupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component.supplier 2 | 3 | import io.github.shoaky.sourcedownloader.component.trigger.CronTrigger 4 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 5 | import io.github.shoaky.sourcedownloader.sdk.Properties 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 7 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 8 | 9 | object CronTriggerSupplier : ComponentSupplier { 10 | 11 | override fun apply(context: CoreContext, props: Properties): CronTrigger { 12 | return CronTrigger(props.get("expression")) 13 | } 14 | 15 | override fun supplyTypes(): List { 16 | return listOf( 17 | ComponentType.trigger("cron") 18 | ) 19 | } 20 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/component/supplier/DeleteEmptyDirectorySupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component.supplier 2 | 3 | import io.github.shoaky.sourcedownloader.component.DeleteEmptyDirectory 4 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 5 | import io.github.shoaky.sourcedownloader.sdk.Properties 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 7 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 8 | 9 | object DeleteEmptyDirectorySupplier : ComponentSupplier { 10 | 11 | override fun apply(context: CoreContext, props: Properties): DeleteEmptyDirectory { 12 | return DeleteEmptyDirectory 13 | } 14 | 15 | override fun supplyTypes(): List { 16 | return listOf( 17 | ComponentType.listener("delete-empty-directory") 18 | ) 19 | } 20 | 21 | override fun supportNoArgs(): Boolean = true 22 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/component/supplier/FixedSourceSupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component.supplier 2 | 3 | import io.github.shoaky.sourcedownloader.component.source.FixedSource 4 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 5 | import io.github.shoaky.sourcedownloader.sdk.Properties 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 7 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 8 | 9 | object FixedSourceSupplier : ComponentSupplier { 10 | 11 | override fun apply(context: CoreContext, props: Properties): FixedSource { 12 | return FixedSource(props.get("content"), props.getOrDefault("offset-mode", false)) 13 | } 14 | 15 | override fun supplyTypes(): List { 16 | return listOf( 17 | ComponentType.source("fixed"), 18 | ComponentType.fileResolver("fixed"), 19 | ) 20 | } 21 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/component/supplier/ForceTrimmerSupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component.supplier 2 | 3 | import io.github.shoaky.sourcedownloader.component.ForceTrimmer 4 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 5 | import io.github.shoaky.sourcedownloader.sdk.Properties 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 7 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 8 | 9 | object ForceTrimmerSupplier : ComponentSupplier { 10 | 11 | override fun apply(context: CoreContext, props: Properties): ForceTrimmer { 12 | return ForceTrimmer 13 | } 14 | 15 | override fun supplyTypes(): List { 16 | return listOf(ComponentType.trimmer("force")) 17 | } 18 | 19 | override fun supportNoArgs(): Boolean = true 20 | 21 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/component/supplier/GeneralFileMoverSupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component.supplier 2 | 3 | import io.github.shoaky.sourcedownloader.component.GeneralFileMover 4 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 5 | import io.github.shoaky.sourcedownloader.sdk.Properties 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 7 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 8 | 9 | object GeneralFileMoverSupplier : ComponentSupplier { 10 | 11 | override fun apply(context: CoreContext, props: Properties): GeneralFileMover { 12 | return GeneralFileMover 13 | } 14 | 15 | override fun supplyTypes(): List { 16 | return listOf( 17 | ComponentType.fileMover("general") 18 | ) 19 | } 20 | 21 | override fun supportNoArgs(): Boolean = true 22 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/component/supplier/HardlinkFileMoverSupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component.supplier 2 | 3 | import io.github.shoaky.sourcedownloader.component.HardlinkFileMover 4 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 5 | import io.github.shoaky.sourcedownloader.sdk.Properties 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 7 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 8 | 9 | object HardlinkFileMoverSupplier : ComponentSupplier { 10 | 11 | override fun apply(context: CoreContext, props: Properties): HardlinkFileMover { 12 | return HardlinkFileMover 13 | } 14 | 15 | override fun supplyTypes(): List { 16 | return listOf( 17 | ComponentType.fileMover("hardlink") 18 | ) 19 | } 20 | 21 | override fun supportNoArgs(): Boolean = true 22 | 23 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/component/supplier/HttpDownloaderSupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component.supplier 2 | 3 | import io.github.shoaky.sourcedownloader.component.downloader.HttpDownloader 4 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 5 | import io.github.shoaky.sourcedownloader.sdk.Properties 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 7 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 8 | import java.nio.file.Path 9 | 10 | object HttpDownloaderSupplier : ComponentSupplier { 11 | 12 | override fun apply(context: CoreContext, props: Properties): HttpDownloader { 13 | val path = props.get("download-path") 14 | return HttpDownloader(path) 15 | } 16 | 17 | override fun supplyTypes(): List { 18 | return listOf( 19 | ComponentType.downloader("http") 20 | ) 21 | } 22 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/component/supplier/ItemDirectoryExistsDetectorSupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component.supplier 2 | 3 | import io.github.shoaky.sourcedownloader.component.FileDirectoryExistsDetector 4 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 5 | import io.github.shoaky.sourcedownloader.sdk.Properties 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 7 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 8 | 9 | object ItemDirectoryExistsDetectorSupplier : ComponentSupplier { 10 | 11 | override fun apply(context: CoreContext, props: Properties): FileDirectoryExistsDetector { 12 | return FileDirectoryExistsDetector 13 | } 14 | 15 | override fun supplyTypes(): List { 16 | return listOf( 17 | ComponentType.itemExistsDetector("item-dir") 18 | ) 19 | } 20 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/component/supplier/MappedFileTaggerSupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component.supplier 2 | 3 | import io.github.shoaky.sourcedownloader.component.MappedFileTagger 4 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 5 | import io.github.shoaky.sourcedownloader.sdk.Properties 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 7 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 8 | 9 | object MappedFileTaggerSupplier : ComponentSupplier { 10 | 11 | override fun apply(context: CoreContext, props: Properties): MappedFileTagger { 12 | return MappedFileTagger(props.get("mapping")) 13 | } 14 | 15 | override fun supplyTypes(): List { 16 | return listOf( 17 | ComponentType.fileTagger("mapped") 18 | ) 19 | } 20 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/component/supplier/MockDownloaderSupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component.supplier 2 | 3 | import io.github.shoaky.sourcedownloader.component.downloader.MockDownloader 4 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 5 | import io.github.shoaky.sourcedownloader.sdk.Properties 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 7 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 8 | import java.nio.file.Path 9 | 10 | object MockDownloaderSupplier : ComponentSupplier { 11 | 12 | override fun apply(context: CoreContext, props: Properties): MockDownloader { 13 | val path = props.get("download-path") 14 | return MockDownloader(path) 15 | } 16 | 17 | override fun supplyTypes(): List { 18 | return listOf( 19 | ComponentType.downloader("mock") 20 | ) 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/component/supplier/NeverReplaceSupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component.supplier 2 | 3 | import io.github.shoaky.sourcedownloader.component.NeverReplace 4 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 5 | import io.github.shoaky.sourcedownloader.sdk.Properties 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 7 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 8 | 9 | object NeverReplaceSupplier : ComponentSupplier { 10 | 11 | override fun apply(context: CoreContext, props: Properties): NeverReplace { 12 | return NeverReplace 13 | } 14 | 15 | override fun supportNoArgs(): Boolean = true 16 | 17 | override fun supplyTypes(): List { 18 | return listOf( 19 | ComponentType.fileReplacementDecider("never") 20 | ) 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/component/supplier/RegexTrimmerSupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component.supplier 2 | 3 | import io.github.shoaky.sourcedownloader.component.RegexTrimmer 4 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 5 | import io.github.shoaky.sourcedownloader.sdk.Properties 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 7 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 8 | 9 | object RegexTrimmerSupplier : ComponentSupplier { 10 | 11 | override fun apply(context: CoreContext, props: Properties): RegexTrimmer { 12 | return RegexTrimmer(Regex(props.get("regex"))) 13 | } 14 | 15 | override fun supplyTypes(): List { 16 | return listOf( 17 | ComponentType.trimmer("regex") 18 | ) 19 | } 20 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/component/supplier/RegexVariableReplacerSupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component.supplier 2 | 3 | import io.github.shoaky.sourcedownloader.component.replacer.RegexVariableReplacer 4 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 5 | import io.github.shoaky.sourcedownloader.sdk.Properties 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 7 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 8 | 9 | object RegexVariableReplacerSupplier : ComponentSupplier { 10 | 11 | override fun apply(context: CoreContext, props: Properties): RegexVariableReplacer { 12 | return RegexVariableReplacer(props.get("regex"), props.get("replacement")) 13 | } 14 | 15 | override fun supplyTypes(): List { 16 | return listOf( 17 | ComponentType.variableReplacer("regex") 18 | ) 19 | } 20 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/component/supplier/SendHttpRequestSupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component.supplier 2 | 3 | import io.github.shoaky.sourcedownloader.component.SendHttpRequest 4 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 5 | import io.github.shoaky.sourcedownloader.sdk.Properties 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 7 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 8 | 9 | object SendHttpRequestSupplier : ComponentSupplier { 10 | 11 | override fun apply(context: CoreContext, props: Properties): SendHttpRequest { 12 | return SendHttpRequest(props.parse()) 13 | } 14 | 15 | override fun supplyTypes(): List { 16 | return listOf( 17 | ComponentType.listener("http") 18 | ) 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/component/supplier/TouchItemDirectorySupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component.supplier 2 | 3 | import io.github.shoaky.sourcedownloader.component.TouchItemDirectory 4 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 5 | import io.github.shoaky.sourcedownloader.sdk.Properties 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 7 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 8 | 9 | object TouchItemDirectorySupplier : ComponentSupplier { 10 | 11 | override fun apply(context: CoreContext, props: Properties): TouchItemDirectory { 12 | return TouchItemDirectory 13 | } 14 | 15 | override fun supplyTypes(): List { 16 | return listOf(ComponentType.listener("touch-item-directory")) 17 | } 18 | 19 | override fun supportNoArgs(): Boolean = true 20 | 21 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/component/supplier/UriSourceSupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component.supplier 2 | 3 | import io.github.shoaky.sourcedownloader.component.source.UriSource 4 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 5 | import io.github.shoaky.sourcedownloader.sdk.Properties 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 7 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 8 | import java.net.URI 9 | 10 | object UriSourceSupplier : ComponentSupplier { 11 | 12 | override fun apply(context: CoreContext, props: Properties): UriSource { 13 | val uri = props.get("uri") 14 | return UriSource(uri) 15 | } 16 | 17 | override fun supplyTypes(): List { 18 | return listOf( 19 | ComponentType.source("uri") 20 | ) 21 | } 22 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/component/supplier/UrlDownloaderSupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component.supplier 2 | 3 | import io.github.shoaky.sourcedownloader.component.downloader.UrlDownloader 4 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 5 | import io.github.shoaky.sourcedownloader.sdk.Properties 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 7 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 8 | import java.nio.file.Path 9 | 10 | object UrlDownloaderSupplier : ComponentSupplier { 11 | 12 | override fun apply(context: CoreContext, props: Properties): UrlDownloader { 13 | val path = props.get("download-path") 14 | return UrlDownloader(path) 15 | } 16 | 17 | override fun supplyTypes(): List { 18 | return listOf( 19 | ComponentType.downloader("url"), 20 | ) 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/component/supplier/UrlFileResolverSupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component.supplier 2 | 3 | import io.github.shoaky.sourcedownloader.component.resolver.UrlFileResolver 4 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 5 | import io.github.shoaky.sourcedownloader.sdk.Properties 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 7 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 8 | 9 | object UrlFileResolverSupplier : ComponentSupplier { 10 | 11 | override fun apply(context: CoreContext, props: Properties): UrlFileResolver { 12 | return UrlFileResolver 13 | } 14 | 15 | override fun supplyTypes(): List { 16 | return listOf(ComponentType.fileResolver("url")) 17 | } 18 | 19 | override fun supportNoArgs(): Boolean = true 20 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/component/supplier/WebhookTriggerSupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component.supplier 2 | 3 | import io.github.shoaky.sourcedownloader.component.trigger.WebhookTrigger 4 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 5 | import io.github.shoaky.sourcedownloader.sdk.Properties 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 7 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 8 | 9 | class WebhookTriggerSupplier( 10 | private val adapter: WebhookTrigger.Adapter 11 | ) : ComponentSupplier { 12 | 13 | override fun apply(context: CoreContext, props: Properties): WebhookTrigger { 14 | return WebhookTrigger(props.get("path"), props.getOrDefault("method", "GET"), adapter) 15 | } 16 | 17 | override fun supplyTypes(): List { 18 | return listOf(ComponentType.trigger("webhook")) 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/config/SourceDownloaderProperties.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.config 2 | 3 | import java.nio.file.Path 4 | 5 | data class SourceDownloaderProperties( 6 | val dataLocation: Path 7 | ) -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/AllDeclaredConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude 4 | import io.github.shoaky.sourcedownloader.core.component.ComponentConfig 5 | 6 | @JsonInclude(JsonInclude.Include.NON_DEFAULT) 7 | data class AllDeclaredConfig( 8 | val components: MutableMap> = mutableMapOf(), 9 | val processors: MutableList = mutableListOf(), 10 | val instances: MutableList = mutableListOf() 11 | ) -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/InstanceConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude 4 | 5 | @JsonInclude(JsonInclude.Include.NON_DEFAULT) 6 | data class InstanceConfig( 7 | val name: String, 8 | val props: Map 9 | ) -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/PersistentPointer.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core 2 | 3 | import com.fasterxml.jackson.annotation.JsonAnyGetter 4 | import com.fasterxml.jackson.annotation.JsonAnySetter 5 | import com.fasterxml.jackson.annotation.JsonUnwrapped 6 | import io.github.shoaky.sourcedownloader.sdk.util.Jackson 7 | 8 | data class PersistentPointer( 9 | @get:JsonUnwrapped 10 | @get:JsonAnyGetter 11 | val values: MutableMap = mutableMapOf() 12 | ) { 13 | 14 | @JsonAnySetter 15 | fun setValue(key: String, value: Any) { 16 | values[key] = value 17 | } 18 | 19 | override fun toString(): String { 20 | return Jackson.toJsonString(values) 21 | } 22 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/PluginLoader.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.plugin.Plugin 4 | 5 | interface PluginLoader { 6 | 7 | fun loadPlugins(classLoader: ClassLoader? = null): List 8 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/ProcessorConfigStorage.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core 2 | 3 | import io.github.shoaky.sourcedownloader.core.component.ComponentFailureType 4 | import io.github.shoaky.sourcedownloader.throwComponentException 5 | 6 | interface ProcessorConfigStorage { 7 | 8 | fun getAllProcessorConfig(): List 9 | 10 | fun getProcessorConfig(name: String): ProcessorConfig { 11 | return getAllProcessorConfig().firstOrNull { it.name == name } 12 | ?: throwComponentException("No processor config found for $name", ComponentFailureType.PROCESSOR_NOT_FOUND) 13 | } 14 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/VariableReplacerConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core 2 | 3 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize 4 | import io.github.shoaky.sourcedownloader.core.component.ComponentId 5 | 6 | @JsonDeserialize(using = VariableReplacerConfigDeserializer::class) 7 | data class VariableReplacerConfig( 8 | val id: ComponentId, 9 | val keys: Set? = null, 10 | ) -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/component/ComponentConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core.component 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude 4 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentTopType 5 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 6 | 7 | @JsonInclude(JsonInclude.Include.NON_DEFAULT) 8 | data class ComponentConfig( 9 | val name: String, 10 | val type: String, 11 | val props: Map = emptyMap(), 12 | ) { 13 | 14 | fun instanceName(topType: ComponentTopType): String { 15 | return ComponentType.of(topType, type).instanceName(name) 16 | } 17 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/component/ComponentFailureType.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core.component 2 | 3 | enum class ComponentFailureType( 4 | val type: String 5 | ) { 6 | 7 | SUPPLIER_NOT_FOUND("supplier_not_found"), 8 | TYPE_DUPLICATED("type_duplicated"), 9 | UNKNOWN_TYPE("unknown_type"), 10 | REQUIRED_PROP_NOT_FOUND("required_prop_not_found"), 11 | INVALID_PROP("props:invalid"), 12 | 13 | /** 14 | * The component is not compatible with the other components. 15 | */ 16 | INCOMPATIBILITY("incompatibility"), 17 | INSTANCE_NOT_FOUND("instance_not_found"), 18 | DEFINITION_NOT_FOUND("definition_not_found"), 19 | PROCESSOR_ALREADY_EXISTS("processor_already_exists"), 20 | PROCESSOR_NOT_FOUND("processor_not_found"), 21 | 22 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/component/ConfigOperator.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core.component 2 | 3 | import io.github.shoaky.sourcedownloader.core.ProcessorConfig 4 | import io.github.shoaky.sourcedownloader.core.ProcessorConfigStorage 5 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentTopType 6 | 7 | interface ConfigOperator : ProcessorConfigStorage, ComponentConfigStorage, InstanceConfigStorage { 8 | 9 | fun save(type: String, componentConfig: ComponentConfig) 10 | 11 | fun save(name: String, processorConfig: ProcessorConfig) 12 | 13 | fun deleteComponent(topType: ComponentTopType, type: String, name: String): Boolean 14 | 15 | fun deleteProcessor(name: String): Boolean 16 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/component/DefaultComponents.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core.component 2 | 3 | import com.fasterxml.jackson.module.kotlin.jacksonTypeRef 4 | import io.github.shoaky.sourcedownloader.sdk.util.Jackson 5 | import org.yaml.snakeyaml.Yaml 6 | 7 | class DefaultComponents : ComponentConfigStorage { 8 | 9 | private val componentConfig: Map> by lazy { 10 | val configStream = Thread.currentThread() 11 | .contextClassLoader 12 | .getResourceAsStream("default-component.yaml") 13 | val load = Yaml().load>(configStream) 14 | Jackson.convert(load, jacksonTypeRef()) 15 | } 16 | 17 | override fun getAllComponentConfig(): Map> { 18 | return componentConfig 19 | } 20 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/component/InstanceConfigStorage.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core.component 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.Properties 4 | 5 | interface InstanceConfigStorage { 6 | 7 | fun getInstanceProps(name: String): Properties 8 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/component/ListenerConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core.component 2 | 3 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize 4 | import io.github.shoaky.sourcedownloader.core.processor.ListenerMode 5 | 6 | @JsonDeserialize(using = ListenerConfigDeserializer::class) 7 | data class ListenerConfig( 8 | val id: ComponentId, 9 | val mode: ListenerMode = ListenerMode.EACH 10 | ) 11 | 12 | -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/component/NamedProcessListener.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core.component 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.component.ProcessListener 4 | 5 | class NamedProcessListener( 6 | val id: ComponentId, 7 | val component: ProcessListener 8 | ) : ProcessListener by component { 9 | 10 | override fun toString(): String { 11 | return id.toString() 12 | } 13 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/component/ObjectWrapper.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core.component 2 | 3 | interface ObjectWrapper { 4 | 5 | fun get(): T 6 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/component/ProcessorWrapper.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core.component 2 | 3 | import io.github.shoaky.sourcedownloader.core.processor.SourceProcessor 4 | 5 | data class ProcessorWrapper( 6 | val name: String, 7 | val processor: SourceProcessor 8 | ) : ObjectWrapper { 9 | 10 | override fun get(): SourceProcessor { 11 | return processor 12 | } 13 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/expression/CelCompiledExpression.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core.expression 2 | 3 | import org.projectnessie.cel.tools.Script 4 | 5 | class CelCompiledExpression( 6 | private val script: Script, 7 | private val resultType: Class, 8 | private val rawString: String, 9 | ) : CompiledExpression { 10 | 11 | var optional: Boolean = false 12 | 13 | override fun execute(variables: Map): T { 14 | return script.execute(resultType, variables) 15 | } 16 | 17 | override fun raw(): String { 18 | return rawString 19 | } 20 | 21 | override fun optional(): Boolean { 22 | return optional 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/expression/CompiledExpression.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core.expression 2 | 3 | import org.slf4j.LoggerFactory 4 | 5 | interface CompiledExpression { 6 | 7 | fun execute(variables: Map): T 8 | 9 | fun executeIgnoreError(variables: Map): T? { 10 | return runCatching { 11 | execute(variables) 12 | }.getOrElse { 13 | log.debug("Execute expression '${raw()}' failed:{}", it.message) 14 | null 15 | } 16 | } 17 | 18 | fun raw(): String 19 | 20 | fun optional(): Boolean = false 21 | 22 | companion object { 23 | 24 | private val log = LoggerFactory.getLogger(CompiledExpression::class.java) 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/expression/CompiledExpressionFactory.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core.expression 2 | 3 | interface CompiledExpressionFactory { 4 | 5 | fun create(raw: String, resultType: Class, def: Map): CompiledExpression 6 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/expression/ExpressionType.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core.expression 2 | 3 | enum class ExpressionType(val factory: CompiledExpressionFactory) { 4 | CEL(CelCompiledExpressionFactory), 5 | // SPEL(SpelCompiledExpressionFactory) 6 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/expression/VariableType.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core.expression 2 | 3 | enum class VariableType { 4 | STRING, 5 | BOOLEAN, 6 | 7 | /** 8 | * 没有范型描述默认String, Any 9 | */ 10 | MAP, 11 | 12 | /** 13 | * 没有范型描述默认Any 14 | */ 15 | ARRAY, 16 | DATE, 17 | INT, 18 | ANY 19 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/file/ReadonlyFileMover.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core.file 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.FileContent 4 | import io.github.shoaky.sourcedownloader.sdk.ItemContent 5 | import io.github.shoaky.sourcedownloader.sdk.SourceItem 6 | import io.github.shoaky.sourcedownloader.sdk.component.FileMover 7 | import java.nio.file.Path 8 | 9 | class ReadonlyFileMover(fileMover: FileMover) : FileMover by fileMover { 10 | 11 | override fun move(sourceItem: SourceItem, file: FileContent): Boolean { 12 | throw UnsupportedOperationException("ReadonlyFileMover") 13 | } 14 | 15 | override fun createDirectories(path: Path) { 16 | throw UnsupportedOperationException("ReadonlyFileMover") 17 | } 18 | 19 | override fun replace(itemContent: ItemContent): Boolean { 20 | throw UnsupportedOperationException("ReadonlyFileMover") 21 | } 22 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/file/RenameVariables.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core.file 2 | 3 | import com.jayway.jsonpath.DocumentContext 4 | import com.jayway.jsonpath.JsonPath 5 | 6 | data class RenameVariables( 7 | val variables: Map, 8 | val processedVariables: Map = emptyMap(), 9 | /** 10 | * 由VariableProvider提供的变量,已经包含在[variables]中 11 | */ 12 | val patternVariables: Map = emptyMap(), 13 | val trimVariables: Map = emptyMap(), 14 | ) { 15 | 16 | val allVariables: Map by lazy { 17 | variables + processedVariables + trimVariables 18 | } 19 | val document: DocumentContext by lazy { 20 | JsonPath.parse(variables + processedVariables) 21 | } 22 | 23 | companion object { 24 | 25 | val EMPTY = RenameVariables(emptyMap()) 26 | } 27 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/file/VariableErrorStrategy.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core.file 2 | 3 | enum class VariableErrorStrategy { 4 | ORIGINAL, 5 | PATTERN, 6 | STAY, 7 | TO_UNRESOLVED, 8 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/processor/DownloadStatus.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core.processor 2 | 3 | enum class DownloadStatus { 4 | FINISHED, 5 | NOT_FINISHED, 6 | NOT_FOUND; 7 | 8 | companion object { 9 | fun from(boolean: Boolean?): DownloadStatus { 10 | return when (boolean) { 11 | true -> FINISHED 12 | false -> NOT_FINISHED 13 | null -> NOT_FOUND 14 | } 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/processor/DryRunOptions.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core.processor 2 | 3 | data class DryRunOptions( 4 | val pointer: Map? = null, 5 | val filterProcessed: Boolean = true 6 | ) -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/processor/KeyFilterVariableReplacer.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core.processor 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.component.VariableReplacer 4 | 5 | class KeyFilterVariableReplacer( 6 | private val replacer: VariableReplacer, 7 | private val keys: Set?, 8 | ) : VariableReplacer { 9 | 10 | override fun replace(key: String, value: String): String { 11 | if (keys == null) { 12 | return replacer.replace(key, value) 13 | } 14 | if (keys.contains(key)) { 15 | return replacer.replace(key, value) 16 | } 17 | return value 18 | } 19 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/processor/ListenerMode.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core.processor 2 | 3 | enum class ListenerMode { 4 | EACH, 5 | BATCH 6 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/processor/ProcessStage.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core.processor 2 | 3 | class ProcessStage( 4 | private val stage: String, 5 | private val subject: Any? 6 | ) { 7 | 8 | override fun toString(): String { 9 | return "stage:'$stage', subject:$subject" 10 | } 11 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/processor/ProcessingTargetPath.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core.processor 2 | 3 | import java.nio.file.Path 4 | 5 | data class ProcessingTargetPath( 6 | val targetPath: Path, 7 | val processorName: String? = null, 8 | val itemHashing: String? = null, 9 | ) -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/processor/ProcessorManager.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core.processor 2 | 3 | import io.github.shoaky.sourcedownloader.core.ProcessorConfig 4 | import io.github.shoaky.sourcedownloader.core.component.ProcessorWrapper 5 | 6 | interface ProcessorManager { 7 | 8 | fun createProcessor(config: ProcessorConfig) 9 | 10 | fun getProcessor(name: String): ProcessorWrapper 11 | 12 | fun exists(name: String): Boolean { 13 | return getAllProcessorNames().contains(name) 14 | } 15 | 16 | fun getProcessors(): List 17 | 18 | fun destroyProcessor(processorName: String) 19 | 20 | fun getAllProcessorNames(): Set 21 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/processor/ProcessorSafeRunner.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core.processor 2 | 3 | import java.util.concurrent.atomic.AtomicBoolean 4 | 5 | class ProcessorSafeRunner( 6 | private val processor: SourceProcessor 7 | ) : Runnable { 8 | 9 | private val running = AtomicBoolean(false) 10 | override fun run() { 11 | val name = processor.name 12 | log.info("Processor:'$name' 触发获取源信息") 13 | if (running.compareAndSet(false, true).not()) { 14 | log.info("Processor:'$name' 上一次任务还未完成,跳过本次任务") 15 | return 16 | } 17 | try { 18 | processor.run() 19 | } catch (e: Exception) { 20 | log.error("Processor:'$name' 执行失败", e) 21 | } finally { 22 | running.set(false) 23 | } 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/processor/SourceItemPartition.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core.processor 2 | 3 | import io.github.shoaky.sourcedownloader.core.expression.CompiledExpression 4 | import io.github.shoaky.sourcedownloader.core.expression.variables 5 | import io.github.shoaky.sourcedownloader.sdk.SourceItem 6 | 7 | interface SourceItemPartition { 8 | 9 | fun match(item: SourceItem): Boolean 10 | } 11 | 12 | class ExpressionSourceItemPartition( 13 | private val expression: CompiledExpression 14 | ) : SourceItemPartition { 15 | 16 | override fun match(item: SourceItem): Boolean { 17 | return expression.execute(item.variables()) 18 | } 19 | } 20 | 21 | class TagSourceItemPartition( 22 | private val tags: Set 23 | ) : SourceItemPartition { 24 | 25 | override fun match(item: SourceItem): Boolean { 26 | return item.tags.containsAll(tags) 27 | } 28 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/core/processor/VariableConflictStrategy.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core.processor 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.component.VariableProvider 4 | 5 | enum class VariableConflictStrategy { 6 | /** 7 | * 任意一个,目前是按定义的顺序 8 | */ 9 | ANY, 10 | 11 | /** 12 | * 值相同的数量最多的 13 | */ 14 | VOTE, 15 | 16 | /** 17 | * 根据[VariableProvider.accuracy]排序,精确度越高越优先 18 | */ 19 | ACCURACY, 20 | 21 | /** 22 | * VOTE + ACCURACY 23 | */ 24 | SMART 25 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/repo/exposed/JsonColumnType.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.repo.exposed 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.util.Jackson 4 | import org.jetbrains.exposed.sql.Table 5 | import org.jetbrains.exposed.sql.json.json 6 | 7 | inline fun Table.json(name: String) = json(name, { Jackson.toJsonString(it) }, { Jackson.fromJson(it, T::class) }) 8 | -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/service/ComponentCreateBody.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.service 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentTopType 4 | 5 | data class ComponentCreateBody( 6 | val type: ComponentTopType, 7 | val typeName: String, 8 | val name: String, 9 | val props: Map, 10 | ) -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/service/ComponentInfo.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.service 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentTopType 4 | 5 | data class ComponentInfo( 6 | val type: ComponentTopType, 7 | val typeName: String, 8 | val name: String, 9 | val props: Map, 10 | val stateDetail: Any? = null, 11 | val primary: Boolean, 12 | val running: Boolean, 13 | val refs: Set?, 14 | val modifiable: Boolean = true, 15 | ) -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/service/EventItem.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.service 2 | 3 | data class EventItem( 4 | val id: String, 5 | val event: String, 6 | val data: Any, 7 | ) -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/service/NotFoundException.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.service 2 | 3 | class NotFoundException(override val message: String?) : RuntimeException() -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/service/PageRequest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.service 2 | 3 | data class PageRequest( 4 | var pageNumber: Int = 0, 5 | var pageSize: Int = 50 6 | ) -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/service/ProcessorInfo.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.service 2 | 3 | import io.github.shoaky.sourcedownloader.core.processor.ProcessorRuntime 4 | 5 | data class ProcessorInfo( 6 | val name: String, 7 | val enabled: Boolean, 8 | val category: String?, 9 | val tags: Set, 10 | val runtime: ProcessorRuntime.Snapshot?, 11 | ) -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/service/ProcessorState.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.service 2 | 3 | import io.github.shoaky.sourcedownloader.core.PersistentPointer 4 | import java.time.LocalDateTime 5 | 6 | /** 7 | * Processor状态 8 | */ 9 | data class ProcessorState( 10 | val sourceId:String, 11 | /** 12 | * Source当前处理的未知 13 | */ 14 | val pointer: PersistentPointer, 15 | /** 16 | * 最后一次活跃时间 17 | */ 18 | val lastActiveTime: LocalDateTime? = null, 19 | val retryTimes: Int = 0, 20 | ) -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/service/Scroll.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.service 2 | 3 | import io.github.shoaky.sourcedownloader.core.ProcessingContent 4 | 5 | data class Scroll( 6 | val contents: List, 7 | val nextMaxId: Long 8 | ) -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/service/UpdateProcessingContent.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.service 2 | 3 | import io.github.shoaky.sourcedownloader.core.ProcessingContent 4 | 5 | data class UpdateProcessingContent( 6 | val status: ProcessingContent.Status? = null, 7 | val renameTimes: Int? = null 8 | ) -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/util/Commons.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.util 2 | 3 | import java.nio.file.Path 4 | import java.nio.file.attribute.BasicFileAttributes 5 | import java.time.LocalDateTime 6 | import java.time.ZoneId 7 | import kotlin.io.path.notExists 8 | import kotlin.io.path.readAttributes 9 | 10 | fun Path.creationTime(): LocalDateTime? { 11 | if (this.notExists()) { 12 | return null 13 | } 14 | val attrs = this.readAttributes() 15 | val creationTime = attrs.creationTime() 16 | return LocalDateTime.ofInstant(creationTime.toInstant(), ZoneId.systemDefault()) 17 | } 18 | 19 | fun MutableMap>.addToCollection(key: K, value: T) { 20 | if (this.containsKey(key)) { 21 | this[key] = this.getValue(key) + value 22 | } else { 23 | this[key] = listOf(value) 24 | } 25 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/util/EnumValue.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.util 2 | 3 | import kotlin.reflect.KClass 4 | 5 | interface EnumValue { 6 | 7 | fun getValue(): T 8 | } 9 | 10 | fun KClass.fromValue(value: T): R where R : EnumValue, R : Enum { 11 | val enums = this.java.enumConstants 12 | for (enum in enums) { 13 | if (enum.getValue() == value) { 14 | return enum 15 | } 16 | } 17 | throw IllegalArgumentException("Unknown value $value for enum $this") 18 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/util/Events.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.util 2 | 3 | import com.google.common.eventbus.EventBus 4 | 5 | object Events { 6 | private val bus: EventBus = EventBus() 7 | 8 | fun register(any: Any) { 9 | bus.register(any) 10 | } 11 | 12 | fun post(event: Any) { 13 | bus.post(event) 14 | } 15 | 16 | fun unregister(any: Any) { 17 | bus.unregister(any) 18 | } 19 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/util/NoLock.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.util 2 | 3 | import java.util.concurrent.TimeUnit 4 | import java.util.concurrent.locks.Condition 5 | import java.util.concurrent.locks.Lock 6 | 7 | object NoLock : Lock { 8 | 9 | override fun lock() { 10 | } 11 | 12 | override fun lockInterruptibly() { 13 | } 14 | 15 | override fun tryLock(): Boolean { 16 | throw UnsupportedOperationException() 17 | } 18 | 19 | override fun tryLock(time: Long, unit: TimeUnit): Boolean { 20 | throw UnsupportedOperationException() 21 | } 22 | 23 | override fun unlock() { 24 | } 25 | 26 | override fun newCondition(): Condition { 27 | throw UnsupportedOperationException() 28 | } 29 | } 30 | 31 | fun Lock.lock(block: () -> Unit) { 32 | try { 33 | lock() 34 | block() 35 | } finally { 36 | unlock() 37 | } 38 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/io/github/shoaky/sourcedownloader/util/RestorableConfigOperator.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.util 2 | 3 | import io.github.shoaky.sourcedownloader.core.component.ConfigOperator 4 | import java.nio.file.Path 5 | import kotlin.io.path.readText 6 | import kotlin.io.path.writeText 7 | 8 | class RestorableConfigOperator( 9 | private val path: Path, 10 | private val configOperator: ConfigOperator 11 | ) : ConfigOperator by configOperator { 12 | 13 | private val cache = path.readText() 14 | 15 | fun restore() { 16 | path.writeText(cache) 17 | } 18 | } -------------------------------------------------------------------------------- /core/src/main/resources/META-INF/native-image/io/github/shoaky/source-downloader/core/native-image.properties: -------------------------------------------------------------------------------- 1 | Args=--features=io.github.shoaky.sourcedownloader.nativeimage.CoreFeature -------------------------------------------------------------------------------- /core/src/main/resources/db/migration/V2__update_index.sql: -------------------------------------------------------------------------------- 1 | DROP INDEX IF EXISTS uidx_processorname_sourceitemhashing; 2 | CREATE UNIQUE INDEX uidx_sourceitemhashing_processorname 3 | ON processing_record (source_item_hashing DESC, processor_name) -------------------------------------------------------------------------------- /core/src/main/resources/db/migration/V3__change_col_name.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE main.processing_record 2 | RENAME COLUMN source_content TO item_content; -------------------------------------------------------------------------------- /core/src/main/resources/db/migration/V4__add_processing_index.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX idx_processorname_status 2 | ON processing_record (processor_name, status) -------------------------------------------------------------------------------- /core/src/main/resources/db/migration/V5_0_1__add_index.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX idx_processorname_id 2 | ON processing_record (processor_name, id DESC); 3 | 4 | create index idx_createtime 5 | on processing_record (create_time desc); -------------------------------------------------------------------------------- /core/src/main/resources/db/migration/V5__change_col_name.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE processing_record 2 | RENAME COLUMN source_item_hashing TO item_hash; 3 | 4 | CREATE INDEX idx_processorname_createtime 5 | ON processing_record (processor_name, create_time); -------------------------------------------------------------------------------- /core/src/main/resources/default-component.yaml: -------------------------------------------------------------------------------- 1 | trigger: 2 | - name: 30min 3 | type: fixed 4 | props: 5 | interval: PT30M 6 | - name: 1hour 7 | type: fixed 8 | props: 9 | interval: PT1H 10 | - name: 2hour 11 | type: fixed 12 | props: 13 | interval: PT2H 14 | - name: 3hour 15 | type: fixed 16 | props: 17 | interval: PT3H 18 | - name: 6hour 19 | type: fixed 20 | props: 21 | interval: PT6H 22 | - name: 12hour 23 | type: fixed 24 | props: 25 | interval: PT12H 26 | - name: 1day 27 | type: fixed 28 | props: 29 | interval: P1D 30 | - name: webhook 31 | type: webhook 32 | props: 33 | path: default -------------------------------------------------------------------------------- /core/src/test/kotlin/io/github/shoaky/sourcedownloader/component/ForceTrimmerTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component 2 | 3 | import org.junit.jupiter.api.Test 4 | import kotlin.test.assertEquals 5 | 6 | class ForceTrimmerTest { 7 | 8 | @Test 9 | fun test() { 10 | assertEquals("te", ForceTrimmer.trim("test", 2)) 11 | } 12 | } -------------------------------------------------------------------------------- /core/src/test/kotlin/io/github/shoaky/sourcedownloader/component/provider/RegexVariableProviderTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component.provider 2 | 3 | import io.github.shoaky.sourcedownloader.sourceItem 4 | import org.junit.jupiter.api.Test 5 | import kotlin.test.assertEquals 6 | 7 | class RegexVariableProviderTest { 8 | 9 | @Test 10 | fun normal() { 11 | val provider = RegexVariableProvider( 12 | listOf(RegexVariable("date", Regex("\\d+年\\d+月号.*"))) 13 | ) 14 | 15 | val group = provider.itemVariables(sourceItem("dsadsa 2021年11月号 [IX]")) 16 | assertEquals("2021年11月号 [IX]", group.variables()["date"]) 17 | } 18 | } -------------------------------------------------------------------------------- /core/src/test/kotlin/io/github/shoaky/sourcedownloader/component/replacer/FullWidthReplacerTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.component.replacer 2 | 3 | import org.junit.jupiter.api.Test 4 | import kotlin.test.assertEquals 5 | 6 | class FullWidthReplacerTest { 7 | 8 | @Test 9 | fun test() { 10 | val str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 11 | val replaced = FullWidthReplacer.replace("", str) 12 | assertEquals("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", replaced) 13 | } 14 | } -------------------------------------------------------------------------------- /core/src/test/kotlin/io/github/shoaky/sourcedownloader/core/CorePathPatternTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core 2 | 3 | import io.github.shoaky.sourcedownloader.core.file.CorePathPattern 4 | import org.junit.jupiter.api.Test 5 | import kotlin.test.assertEquals 6 | 7 | class CorePathPatternTest { 8 | 9 | @Test 10 | fun depth() { 11 | val pathPattern = CorePathPattern("{name}/{title}") 12 | assertEquals(2, pathPattern.depth()) 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /core/src/test/kotlin/io/github/shoaky/sourcedownloader/core/RegexVariableReplacerSupplierTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core 2 | 3 | import io.github.shoaky.sourcedownloader.component.replacer.RegexVariableReplacer 4 | import org.junit.jupiter.api.Test 5 | import kotlin.test.assertEquals 6 | 7 | class RegexVariableReplacerSupplierTest { 8 | 9 | @Test 10 | fun test() { 11 | val replacer = RegexVariableReplacer("(?i)^BDRIP\$".toRegex(), "BD") 12 | val replace = replacer.replace("source", "BDrip") 13 | assertEquals("BD", replace) 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /core/src/test/kotlin/io/github/shoaky/sourcedownloader/core/WindowsPathReplacerTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core 2 | 3 | import io.github.shoaky.sourcedownloader.component.replacer.WindowsPathReplacer 4 | import org.junit.jupiter.api.Test 5 | import kotlin.test.assertEquals 6 | 7 | class WindowsPathReplacerTest { 8 | 9 | @Test 10 | fun test() { 11 | val replace = WindowsPathReplacer.replace("", "<>:\\/|?*1123adsad") 12 | assertEquals("<>:\/|?*1123adsad", replace) 13 | } 14 | } -------------------------------------------------------------------------------- /core/src/test/kotlin/io/github/shoaky/sourcedownloader/core/component/VariableReplacerConfigDeserializerTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core.component 2 | 3 | import com.fasterxml.jackson.dataformat.yaml.YAMLMapper 4 | import com.fasterxml.jackson.module.kotlin.jacksonTypeRef 5 | import io.github.shoaky.sourcedownloader.core.VariableReplacerConfig 6 | import org.junit.jupiter.api.Test 7 | import kotlin.test.assertEquals 8 | 9 | class VariableReplacerConfigDeserializerTest { 10 | 11 | @Test 12 | fun test() { 13 | val yamlMapper = YAMLMapper() 14 | val configs = yamlMapper.readValue( 15 | """ 16 | - "test" 17 | - "http:test" 18 | - id: "test1" 19 | keys: ["aaa", "bbb"] 20 | - id: "test2" 21 | """, jacksonTypeRef>() 22 | ) 23 | assertEquals(4, configs.size) 24 | } 25 | } -------------------------------------------------------------------------------- /core/src/test/kotlin/io/github/shoaky/sourcedownloader/core/expression/CelLibraryTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.core.expression 2 | 3 | import org.junit.jupiter.api.Test 4 | import kotlin.test.assertEquals 5 | 6 | class CelLibraryTest { 7 | 8 | @Test 9 | fun given_containsAny_when_listContainsAny_then_returnTrue() { 10 | val res = CelLibrary.containsAny( 11 | listOf("720p"), listOf("720P"), true 12 | ) 13 | assertEquals(true, res) 14 | } 15 | } -------------------------------------------------------------------------------- /core/src/test/kotlin/io/github/shoaky/sourcedownloader/integration/support/DelayItemDownloaderSupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.integration.support 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 4 | import io.github.shoaky.sourcedownloader.sdk.Properties 5 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 7 | 8 | object DelayItemDownloaderSupplier : ComponentSupplier { 9 | 10 | override fun apply(context: CoreContext, props: Properties): DelayItemDownloader { 11 | return DelayItemDownloader( 12 | props.get("download-path") 13 | ) 14 | } 15 | 16 | override fun supplyTypes(): List { 17 | return listOf( 18 | ComponentType.downloader("delay-item") 19 | ) 20 | } 21 | } -------------------------------------------------------------------------------- /core/src/test/kotlin/io/github/shoaky/sourcedownloader/integration/support/Item2ReplaceDeciderSupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.integration.support 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 4 | import io.github.shoaky.sourcedownloader.sdk.Properties 5 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 7 | 8 | object Item2ReplaceDeciderSupplier : ComponentSupplier { 9 | 10 | override fun apply(context: CoreContext, props: Properties): Item2ReplaceDecider { 11 | return Item2ReplaceDecider 12 | } 13 | 14 | override fun supplyTypes(): List { 15 | return listOf( 16 | ComponentType.fileReplacementDecider("idk-replace-decider") 17 | ) 18 | } 19 | 20 | override fun supportNoArgs(): Boolean { 21 | return true 22 | } 23 | } -------------------------------------------------------------------------------- /core/src/test/kotlin/io/github/shoaky/sourcedownloader/integration/support/SourceProcessorTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.integration.support 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.ItemContent 4 | import io.github.shoaky.sourcedownloader.sdk.SourceFile 5 | import io.github.shoaky.sourcedownloader.sdk.component.FileReplacementDecider 6 | import kotlin.io.path.nameWithoutExtension 7 | 8 | object Item2ReplaceDecider : FileReplacementDecider { 9 | 10 | override fun isReplace(current: ItemContent, before: ItemContent?, existingFile: SourceFile): Boolean { 11 | return current.fileContents.any { it.fileDownloadPath.nameWithoutExtension == "test-replace2" } 12 | } 13 | 14 | } 15 | 16 | -------------------------------------------------------------------------------- /core/src/test/kotlin/io/github/shoaky/sourcedownloader/integration/support/TestDirErrorDownloaderSupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.integration.support 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 4 | import io.github.shoaky.sourcedownloader.sdk.Properties 5 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 7 | 8 | object TestDirErrorDownloaderSupplier : ComponentSupplier { 9 | 10 | override fun apply(context: CoreContext, props: Properties): TestDirErrorDownloader { 11 | return TestDirErrorDownloader(props.get("download-path")) 12 | } 13 | 14 | override fun supplyTypes(): List { 15 | return listOf( 16 | ComponentType.downloader("test-dir-error") 17 | ) 18 | } 19 | } -------------------------------------------------------------------------------- /core/src/test/kotlin/io/github/shoaky/sourcedownloader/util/EnumValueTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.util 2 | 3 | import org.junit.jupiter.api.Test 4 | import kotlin.test.assertEquals 5 | 6 | class EnumValueTest { 7 | @Test 8 | fun test() { 9 | assertEquals(TestEnum.B, TestEnum::class.fromValue("1")) 10 | } 11 | } 12 | 13 | private enum class TestEnum : EnumValue { 14 | 15 | A, B, C 16 | 17 | ; 18 | 19 | override fun getValue(): String { 20 | return this.ordinal.toString() 21 | } 22 | } -------------------------------------------------------------------------------- /core/src/test/resources/config-test2.yaml: -------------------------------------------------------------------------------- 1 | instances: 2 | - name: client1 3 | props: 4 | appId: 1111 5 | components: 6 | source: 7 | - name: "test" 8 | type: "test" 9 | processors: 10 | - name: "test-normal-case" 11 | triggers: 12 | - "test" 13 | source: "test" 14 | item-file-resolver: "test" 15 | downloader: "test" 16 | variable-providers: 17 | - "test" 18 | file-mover: "test" 19 | save-path: "test-path" 20 | options: 21 | rename-task-interval: "PT1M40S" -------------------------------------------------------------------------------- /core/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker: -------------------------------------------------------------------------------- 1 | mock-maker-inline -------------------------------------------------------------------------------- /core/src/test/resources/script/test.ps1: -------------------------------------------------------------------------------- 1 | Write-Host ($args -join " ") -NoNewline -------------------------------------------------------------------------------- /core/src/test/resources/script/test.sh: -------------------------------------------------------------------------------- 1 | echo "$*\c" 2 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | #kapt.use.k2=true 2 | org.gradle.configuration-cache.parallel=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shoaky009/source-downloader/5235618f2c9fa8555901ecb4a33e32f1468b45be/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /plugin-maven-example/src/main/java/io/github/shoaky/sourcedownloader/maven/JavaPlugin.java: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.maven; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | import io.github.shoaky.sourcedownloader.sdk.Plugin; 5 | import io.github.shoaky.sourcedownloader.sdk.PluginContext; 6 | import io.github.shoaky.sourcedownloader.sdk.PluginDescription; 7 | 8 | public class JavaPlugin implements Plugin { 9 | @NotNull 10 | @Override 11 | public PluginDescription description() { 12 | return new PluginDescription("JavaPluginExample", "0.0.1"); 13 | } 14 | 15 | @Override 16 | public void destroy(@NotNull PluginContext pluginContext) { 17 | 18 | } 19 | 20 | @Override 21 | public void init(@NotNull PluginContext pluginContext) { 22 | 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /plugin-maven-example/src/main/kotlin/io/github/shoaky/sourcedownloader/maven/KotlinPlugin.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.maven 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.Plugin 4 | import io.github.shoaky.sourcedownloader.sdk.PluginContext 5 | import io.github.shoaky.sourcedownloader.sdk.PluginDescription 6 | 7 | class KotlinPlugin : Plugin { 8 | override fun init(pluginContext: PluginContext) { 9 | 10 | } 11 | 12 | override fun destroy(pluginContext: PluginContext) { 13 | 14 | } 15 | 16 | override fun description(): PluginDescription { 17 | return PluginDescription("KotlinPluginExample", "0.0.1") 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /plugin-maven-example/src/main/resources/META-INF/services/io.github.shoaky.sourcedownloader.sdk.Plugin: -------------------------------------------------------------------------------- 1 | io.github.shoaky.sourcedownloader.maven.KotlinPlugin -------------------------------------------------------------------------------- /plugins/build.gradle.kts: -------------------------------------------------------------------------------- 1 | tasks.jar { 2 | enabled = false 3 | } -------------------------------------------------------------------------------- /plugins/common-plugin/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `java-library` 3 | } 4 | 5 | dependencies { 6 | implementation(project(":sdk")) 7 | implementation("org.jetbrains.kotlin:kotlin-stdlib") 8 | testImplementation(libs.mockito.junit.jupiter) 9 | testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") 10 | testImplementation("org.instancio:instancio-junit:3.3.0") 11 | 12 | implementation(libs.rssreader) 13 | implementation(libs.anitomyJ) 14 | implementation(libs.bundles.bt) 15 | implementation(libs.tika.core) 16 | implementation(libs.tika.langdetect.optimaize) 17 | implementation(libs.jsoup) 18 | implementation(libs.fuzzywuzzy) 19 | implementation(libs.commons.text) 20 | implementation(libs.kotlin.retry) 21 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/common/CommonManualSource.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.common 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.component.ManualSource 4 | 5 | object CommonManualSource : ManualSource { 6 | 7 | 8 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/common/anime/BgmTvClientInstanceFactory.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.common.anime 2 | 3 | import io.github.shoaky.sourcedownloader.external.bangumi.BgmTvApiClient 4 | import io.github.shoaky.sourcedownloader.sdk.InstanceFactory 5 | import io.github.shoaky.sourcedownloader.sdk.Properties 6 | import java.net.URI 7 | 8 | object BgmTvClientInstanceFactory : InstanceFactory { 9 | 10 | override fun create(props: Properties): BgmTvApiClient { 11 | val endpoint = props.getOrDefault("endpoint", URI("https://api.bgm.tv/")) 12 | return props.getOrNull("token") 13 | ?.let { 14 | BgmTvApiClient(it, endpoint) 15 | } ?: BgmTvApiClient(endpoint = endpoint) 16 | } 17 | 18 | override fun type(): Class { 19 | return BgmTvApiClient::class.java 20 | } 21 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/common/anime/MikanSupportFactory.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.common.anime 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.InstanceFactory 4 | import io.github.shoaky.sourcedownloader.sdk.Properties 5 | 6 | object MikanSupportFactory : InstanceFactory { 7 | 8 | override fun create(props: Properties): MikanClient { 9 | return MikanClient( 10 | props.getOrNull("token") 11 | ) 12 | } 13 | 14 | override fun type(): Class { 15 | return MikanClient::class.java 16 | } 17 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/common/anime/Titles.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.common.anime 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.PatternVariables 4 | 5 | data class Titles( 6 | val title: String? = null, 7 | val romajiTitle: String? = null, 8 | ) : PatternVariables -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/common/anime/extractor/AllBracketTitleExtractor.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.common.anime.extractor 2 | 3 | object AllBracketTitleExtractor : Extractor { 4 | 5 | private val BRACKET_REGEX = Regex("\\[(.*?)]") 6 | 7 | override fun extract(raw: String): List? { 8 | val isNotAllBracket = raw.replace(BRACKET_REGEX, "").isNotBlank() 9 | if (isNotAllBracket) { 10 | return null 11 | } 12 | val titles = BRACKET_REGEX.findAll(raw).map { it.groupValues.last() }.toList() 13 | return titles 14 | } 15 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/common/anime/extractor/DefaultTitleExtractor.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.common.anime.extractor 2 | 3 | object DefaultTitleExtractor : Extractor { 4 | 5 | override fun extract(raw: String): List { 6 | return listOf(raw) 7 | } 8 | 9 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/common/anime/extractor/Extractor.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.common.anime.extractor 2 | 3 | interface Extractor { 4 | 5 | fun extract(raw: String): List? 6 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/common/bilibili/MediaItemPointer.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.common.bilibili 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.ItemPointer 4 | 5 | data class MediaItemPointer( 6 | val favoriteId: Long, 7 | val time: Long, 8 | val touchButton: Boolean = false 9 | ) : ItemPointer -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/common/fanbox/FanboxPointer.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.common.fanbox 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.ItemPointer 4 | import io.github.shoaky.sourcedownloader.sdk.SourcePointer 5 | 6 | /** 7 | * Key is creatorId 8 | */ 9 | data class FanboxPointer( 10 | val creatorPointers: MutableMap = mutableMapOf() 11 | ) : SourcePointer { 12 | 13 | override fun update(itemPointer: ItemPointer) { 14 | if (itemPointer is CreatorPointer) { 15 | creatorPointers[itemPointer.creatorId] = itemPointer 16 | } 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/common/getchu/GetchuDetailItem.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.common.getchu 2 | 3 | import com.fasterxml.jackson.databind.annotation.JsonSerialize 4 | import com.fasterxml.jackson.databind.ser.std.ToStringSerializer 5 | import io.github.shoaky.sourcedownloader.sdk.PatternVariables 6 | import java.time.LocalDate 7 | 8 | data class GetchuDetailItem( 9 | val getchuId: String? = null, 10 | val title: String? = null, 11 | val isbn: String? = null, 12 | val brand: String? = null, 13 | @JsonSerialize(using = ToStringSerializer::class) 14 | val releaseDate: LocalDate? = null 15 | ) : PatternVariables -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/common/getchu/GetchuSearchItem.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.common.getchu 2 | 3 | import org.jsoup.nodes.Element 4 | 5 | data class GetchuSearchItem( 6 | val element: Element, 7 | val title: String, 8 | val url: String 9 | ) -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/common/patreon/CampaignPointer.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.common.patreon 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.ItemPointer 4 | import java.time.YearMonth 5 | 6 | data class CampaignPointer( 7 | val campaignId: Long, 8 | /** 9 | * 已完成的yearMonth 10 | */ 11 | val lastYearMonth: YearMonth? = null, 12 | /** 13 | * 该年月下最后一个,该字段用于恢复之后是否使用下一个月来请求 14 | */ 15 | val lastOfMonth: Boolean = false, 16 | val lastPostId: Long = -1L, 17 | ) : ItemPointer -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/common/patreon/PatreonPointer.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.common.patreon 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.ItemPointer 4 | import io.github.shoaky.sourcedownloader.sdk.SourcePointer 5 | 6 | data class PatreonPointer( 7 | val campaignPointers: MutableMap = mutableMapOf() 8 | ) : SourcePointer { 9 | 10 | override fun update(itemPointer: ItemPointer) { 11 | if (itemPointer !is CampaignPointer) { 12 | return 13 | } 14 | campaignPointers[itemPointer.campaignId] = itemPointer 15 | } 16 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/common/pixiv/BookmarkPointer.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.common.pixiv 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.ItemPointer 4 | 5 | data class BookmarkPointer( 6 | val bookmarkId: String 7 | ) : ItemPointer -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/common/pixiv/IllustrationPointer.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.common.pixiv 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.ItemPointer 4 | 5 | data class IllustrationPointer( 6 | val illustrationId: Long, 7 | val userId: Long, 8 | ) : ItemPointer -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/common/rss/RssConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.common.rss 2 | 3 | import com.fasterxml.jackson.annotation.JsonAlias 4 | 5 | data class RssConfig( 6 | val url: String, 7 | val tags: List = emptyList(), 8 | val attributes: Map = emptyMap(), 9 | @JsonAlias("date-format") 10 | val dateFormat: String? = null 11 | ) -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/common/supplier/AnimeFileFilterSupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.common.supplier 2 | 3 | import io.github.shoaky.sourcedownloader.common.anime.AnimeFileFilter 4 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 5 | import io.github.shoaky.sourcedownloader.sdk.Properties 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 7 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 8 | 9 | object AnimeFileFilterSupplier : ComponentSupplier { 10 | 11 | override fun apply(context: CoreContext, props: Properties): AnimeFileFilter { 12 | return AnimeFileFilter 13 | } 14 | 15 | override fun supplyTypes(): List { 16 | return listOf( 17 | ComponentType.sourceFileFilter("anime") 18 | ) 19 | } 20 | 21 | override fun supportNoArgs(): Boolean = true 22 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/common/supplier/AnimeTaggerSupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.common.supplier 2 | 3 | import io.github.shoaky.sourcedownloader.common.anime.AnimeTagger 4 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 5 | import io.github.shoaky.sourcedownloader.sdk.Properties 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 7 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 8 | 9 | object AnimeTaggerSupplier : ComponentSupplier { 10 | 11 | override fun apply(context: CoreContext, props: Properties): AnimeTagger { 12 | return AnimeTagger 13 | } 14 | 15 | override fun supplyTypes(): List { 16 | return listOf( 17 | ComponentType.fileTagger("anime") 18 | ) 19 | } 20 | 21 | override fun supportNoArgs(): Boolean { 22 | return true 23 | } 24 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/common/supplier/ChiiVariableProviderSupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.common.supplier 2 | 3 | import io.github.shoaky.sourcedownloader.common.anime.ChiiVariableProvider 4 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 5 | import io.github.shoaky.sourcedownloader.sdk.Properties 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 7 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 8 | 9 | object ChiiVariableProviderSupplier : ComponentSupplier { 10 | 11 | override fun apply(context: CoreContext, props: Properties): ChiiVariableProvider { 12 | return ChiiVariableProvider() 13 | } 14 | 15 | override fun supplyTypes(): List { 16 | return listOf(ComponentType.variableProvider("chii")) 17 | } 18 | 19 | override fun supportNoArgs(): Boolean = true 20 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/common/supplier/CommonManualSourceSupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.common.supplier 2 | 3 | import io.github.shoaky.sourcedownloader.common.CommonManualSource 4 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 5 | import io.github.shoaky.sourcedownloader.sdk.Properties 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 7 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 8 | 9 | object CommonManualSourceSupplier : ComponentSupplier { 10 | 11 | override fun apply(context: CoreContext, props: Properties): CommonManualSource { 12 | return CommonManualSource 13 | } 14 | 15 | override fun supplyTypes(): List { 16 | return listOf( 17 | ComponentType.manualSource("common") 18 | ) 19 | } 20 | 21 | override fun supportNoArgs(): Boolean = true 22 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/common/supplier/DoujinTitleTrimmerSupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.common.supplier 2 | 3 | import io.github.shoaky.sourcedownloader.common.dlsite.DoujinTitleTrimmer 4 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 5 | import io.github.shoaky.sourcedownloader.sdk.Properties 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 7 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 8 | 9 | object DoujinTitleTrimmerSupplier : ComponentSupplier { 10 | 11 | override fun apply(context: CoreContext, props: Properties): DoujinTitleTrimmer { 12 | return DoujinTitleTrimmer 13 | } 14 | 15 | override fun supplyTypes(): List { 16 | return listOf( 17 | ComponentType.trimmer("doujin") 18 | ) 19 | } 20 | 21 | override fun supportNoArgs(): Boolean = true 22 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/common/supplier/EmbyImageTaggerSupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.common.supplier 2 | 3 | import io.github.shoaky.sourcedownloader.common.EmbyImageTagger 4 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 5 | import io.github.shoaky.sourcedownloader.sdk.Properties 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 7 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 8 | 9 | object EmbyImageTaggerSupplier : ComponentSupplier { 10 | 11 | override fun apply(context: CoreContext, props: Properties): EmbyImageTagger { 12 | return EmbyImageTagger 13 | } 14 | 15 | override fun supplyTypes(): List { 16 | return listOf( 17 | ComponentType.fileTagger("emby-image") 18 | ) 19 | } 20 | 21 | override fun supportNoArgs(): Boolean = true 22 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/common/supplier/JackettSourceSupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.common.supplier 2 | 3 | import io.github.shoaky.sourcedownloader.common.rss.JackettSource 4 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 5 | import io.github.shoaky.sourcedownloader.sdk.Properties 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 7 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 8 | 9 | object JackettSourceSupplier : ComponentSupplier { 10 | 11 | override fun apply(context: CoreContext, props: Properties): JackettSource { 12 | return JackettSource(props.get("url")) 13 | } 14 | 15 | override fun supplyTypes(): List { 16 | return listOf( 17 | ComponentType.source("jackett") 18 | ) 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/common/supplier/TorrentFileResolverSupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.common.supplier 2 | 3 | import io.github.shoaky.sourcedownloader.common.torrent.TorrentFileResolver 4 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 5 | import io.github.shoaky.sourcedownloader.sdk.Properties 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 7 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 8 | 9 | object TorrentFileResolverSupplier : ComponentSupplier { 10 | 11 | override fun apply(context: CoreContext, props: Properties): TorrentFileResolver { 12 | return TorrentFileResolver 13 | } 14 | 15 | override fun supplyTypes(): List { 16 | return listOf(ComponentType.fileResolver("torrent")) 17 | } 18 | 19 | override fun supportNoArgs(): Boolean = true 20 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/anilist/Media.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.anilist 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty 4 | 5 | data class Media( 6 | val id: Long, 7 | val title: Title, 8 | ) 9 | 10 | data class Title( 11 | val romaji: String? = null, 12 | val native: String? = null, 13 | ) 14 | 15 | data class Page( 16 | @JsonProperty("media") 17 | val medias: List = emptyList() 18 | ) 19 | 20 | data class PageResponse( 21 | @JsonProperty("Page") 22 | val page: Page, 23 | ) 24 | 25 | data class GraphQLResponse( 26 | val data: T, 27 | val errors: List> = emptyList(), 28 | ) -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/bangumi/BangumiRequest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.bangumi 2 | 3 | import com.google.common.net.HttpHeaders 4 | import com.google.common.net.MediaType 5 | import io.github.shoaky.sourcedownloader.sdk.http.BaseRequest 6 | 7 | abstract class BangumiRequest : BaseRequest() { 8 | 9 | override val mediaType: MediaType = MediaType.JSON_UTF_8 10 | 11 | init { 12 | httpHeaders[HttpHeaders.USER_AGENT] = "shoaky009/SourceDownloader/0.1 (Beta) (https://github.com/shoaky009/SourceDownloader)" 13 | httpHeaders[HttpHeaders.ACCEPT] = MediaType.JSON_UTF_8.toString() 14 | } 15 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/bangumi/GetSubjectRequest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.bangumi 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | import com.fasterxml.jackson.module.kotlin.jacksonTypeRef 5 | import io.github.shoaky.sourcedownloader.sdk.http.HttpMethod 6 | 7 | class GetSubjectRequest(subjectId: String) : BangumiRequest() { 8 | override val path: String = "/v0/subjects/$subjectId" 9 | override val responseBodyType: TypeReference = jacksonTypeRef() 10 | override val httpMethod: String = HttpMethod.GET.name 11 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/bangumi/SearchSubjectBody.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.bangumi 2 | 3 | data class SearchSubjectBody( 4 | val results: Int = 0, 5 | val list: List = emptyList() 6 | ) : Iterable { 7 | override fun iterator(): Iterator { 8 | return list.iterator() 9 | } 10 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/bangumi/SearchSubjectV0Body.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.bangumi 2 | 3 | data class SearchSubjectV0Body( 4 | val total: Int, 5 | val data: List 6 | ) : Iterable { 7 | 8 | override fun iterator(): Iterator { 9 | return data.iterator() 10 | } 11 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/bangumi/SearchSubjectV0Request.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.bangumi 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | import com.fasterxml.jackson.module.kotlin.jacksonTypeRef 5 | import io.github.shoaky.sourcedownloader.sdk.http.HttpMethod 6 | 7 | /** 8 | * [document link](https://bangumi.github.io/api/#/%E6%90%9C%E7%B4%A2/searchSubjectByKeywords) 9 | */ 10 | class SearchSubjectV0Request( 11 | val keyword: String, 12 | type: Int = 2, 13 | nsfw: Boolean = true, 14 | ) : BangumiRequest() { 15 | 16 | val filter = mapOf( 17 | "type" to listOf(type), 18 | "nsfw" to nsfw 19 | ) 20 | 21 | override val path: String = "/v0/search/subjects" 22 | override val responseBodyType: TypeReference = jacksonTypeRef() 23 | override val httpMethod: String = HttpMethod.POST.name 24 | 25 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/bangumi/Subject.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.bangumi 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty 4 | import java.time.LocalDate 5 | 6 | data class Subject( 7 | val id: Long, 8 | val name: String, 9 | @JsonProperty("name_cn") 10 | val nameCn: String, 11 | val date: LocalDate, 12 | @JsonProperty("total_episodes") 13 | val totalEpisodes: Int, 14 | val nsfw: Boolean = false 15 | ) 16 | -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/bangumi/SubjectItem.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.bangumi 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty 4 | 5 | data class SubjectItem( 6 | val id: Long, 7 | val name: String, 8 | @JsonProperty("name_cn") 9 | val nameCn: String, 10 | val url: String, 11 | ) -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/bangumi/SubjectV0Item.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.bangumi 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty 4 | 5 | data class SubjectV0Item( 6 | val id: Long, 7 | val name: String, 8 | @JsonProperty("name_cn") 9 | val nameCn: String, 10 | val image: String 11 | ) -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/bbdown/BbDownClient.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.bbdown 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.http.BaseRequest 4 | import io.github.shoaky.sourcedownloader.sdk.http.HookedApiClient 5 | import java.net.URI 6 | import java.net.http.HttpRequest 7 | import java.net.http.HttpResponse 8 | 9 | class BbDownClient( 10 | private val endpoint: URI, 11 | ) : HookedApiClient() { 12 | 13 | fun execute(request: BaseRequest): HttpResponse { 14 | return this.execute(endpoint, request) 15 | } 16 | 17 | override fun , T : Any> beforeRequest(requestBuilder: HttpRequest.Builder, request: R) { 18 | 19 | } 20 | 21 | override fun , T : Any> afterRequest(response: HttpResponse, request: R) { 22 | 23 | } 24 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/bbdown/DeleteTask.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.bbdown 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | import com.fasterxml.jackson.module.kotlin.jacksonTypeRef 5 | import com.google.common.net.MediaType 6 | import io.github.shoaky.sourcedownloader.sdk.http.BaseRequest 7 | 8 | class DeleteTask( 9 | aid: String, 10 | ) : BaseRequest() { 11 | 12 | override val path: String = "/remove-finished/$aid" 13 | override val responseBodyType: TypeReference = jacksonTypeRef() 14 | override val httpMethod: String = "GET" 15 | override val mediaType: MediaType? = null 16 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/bbdown/GetTask.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.bbdown 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | import com.fasterxml.jackson.module.kotlin.jacksonTypeRef 5 | import com.google.common.net.MediaType 6 | import io.github.shoaky.sourcedownloader.sdk.http.BaseRequest 7 | 8 | class GetTask( 9 | aid: String 10 | ) : BaseRequest() { 11 | 12 | override val path: String = "/get-tasks/$aid" 13 | override val responseBodyType: TypeReference = jacksonTypeRef() 14 | override val httpMethod: String = "GET" 15 | override val mediaType: MediaType? = null 16 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/bilibili/BilibiliResponse.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.bilibili 2 | 3 | class BilibiliResponse( 4 | val code: Int, 5 | val message: String? = null, 6 | val ttl: Int? = null, 7 | val data: T 8 | ) -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/bilibili/Media.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.bilibili 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty 4 | import java.net.URI 5 | 6 | data class Media( 7 | val id: Long, 8 | val type: Int, 9 | val title: String, 10 | val cover: URI, 11 | val page: Int, 12 | val duration: Long, 13 | val upper: Upper, 14 | val attr: Int, 15 | @JsonProperty("cnt_info") 16 | val cntInfo: CntInfo, 17 | val link: URI, 18 | val ctime: Long, 19 | val pubtime: Long, 20 | @JsonProperty("fav_time") 21 | val favTime: Long, 22 | @JsonProperty("bv_id") 23 | val bvId: String 24 | ) 25 | 26 | data class Upper( 27 | val minId: Long, 28 | val name: String, 29 | val face: URI, 30 | ) 31 | 32 | data class CntInfo( 33 | val collect: Long, 34 | val play: Long, 35 | val danmaku: Long, 36 | ) -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/chii/ChiiClient.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.chii 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.http.BaseRequest 4 | import io.github.shoaky.sourcedownloader.sdk.http.HookedApiClient 5 | import java.net.URI 6 | import java.net.http.HttpRequest 7 | import java.net.http.HttpResponse 8 | 9 | class ChiiClient( 10 | private val endpoint: URI = URI("https://chii.ai"), 11 | ) : HookedApiClient() { 12 | 13 | fun , T : Any> execute(request: R): HttpResponse { 14 | return super.execute(endpoint, request) 15 | } 16 | 17 | override fun , T : Any> beforeRequest(requestBuilder: HttpRequest.Builder, request: R) { 18 | } 19 | 20 | override fun , T : Any> afterRequest(response: HttpResponse, request: R) { 21 | } 22 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/dlsite/DlsiteWorkInfo.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.dlsite 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.PatternVariables 4 | 5 | data class DlsiteWorkInfo( 6 | val dlsiteId: String, 7 | val title: String? = null, 8 | val releaseDate: String? = null, 9 | val year: Int? = null, 10 | val month: Int? = null, 11 | val day: Int? = null, 12 | val seriesName: String? = null, 13 | val maker: String? = null, 14 | // val scenario: String? = null, 15 | // val illustration: String? = null, 16 | // val voiceActor: String? = null, 17 | val productFormat: String? = null, 18 | // manga 19 | val author: String? = null, 20 | 21 | // more.... 22 | ) : PatternVariables -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/dlsite/SearchWork.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.dlsite 2 | 3 | data class SearchWork( 4 | val dlsiteId: String, 5 | val title: String, 6 | ) -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/fanbox/CreatorPaginateRequest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.fanbox 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | import com.fasterxml.jackson.module.kotlin.jacksonTypeRef 5 | import java.net.URI 6 | 7 | data class CreatorPaginateRequest( 8 | val creatorId: String, 9 | ) : FanboxRequest>() { 10 | 11 | override val path: String = "/post.paginateCreator" 12 | override val responseBodyType: TypeReference>> = jacksonTypeRef() 13 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/fanbox/FanboxRequest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.fanbox 2 | 3 | import com.google.common.net.MediaType 4 | import io.github.shoaky.sourcedownloader.sdk.http.BaseRequest 5 | import io.github.shoaky.sourcedownloader.sdk.http.HttpMethod 6 | 7 | abstract class FanboxRequest : BaseRequest>() { 8 | 9 | override val httpMethod: String = HttpMethod.GET.name 10 | override val mediaType: MediaType? = null 11 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/fanbox/HomePostsRequest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.fanbox 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | import com.fasterxml.jackson.module.kotlin.jacksonTypeRef 5 | 6 | class HomePostsRequest( 7 | val limit: Int = 20, 8 | ) : FanboxRequest() { 9 | 10 | override val path: String = "/post.listHome" 11 | override val responseBodyType: TypeReference> = jacksonTypeRef() 12 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/fanbox/PostInfoRequest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.fanbox 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | import com.fasterxml.jackson.module.kotlin.jacksonTypeRef 5 | 6 | data class PostInfoRequest( 7 | val postId: String 8 | ) : FanboxRequest() { 9 | 10 | override val path: String = "/post.info" 11 | override val responseBodyType: TypeReference> = jacksonTypeRef() 12 | 13 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/fanbox/SupportingPostsRequest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.fanbox 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | import com.fasterxml.jackson.module.kotlin.jacksonTypeRef 5 | 6 | class SupportingPostsRequest( 7 | val limit: Int = 20, 8 | ) : FanboxRequest() { 9 | 10 | override val path: String = "/post.listSupporting" 11 | override val responseBodyType: TypeReference> = jacksonTypeRef() 12 | 13 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/fanbox/SupportingRequest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.fanbox 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | import com.fasterxml.jackson.module.kotlin.jacksonTypeRef 5 | 6 | class SupportingRequest : FanboxRequest>() { 7 | 8 | override val path: String = "/plan.listSupporting" 9 | override val responseBodyType: TypeReference>> = jacksonTypeRef() 10 | 11 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/openai/AiClient.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.openai 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.http.BaseRequest 4 | import io.github.shoaky.sourcedownloader.sdk.http.HookedApiClient 5 | import java.net.http.HttpRequest 6 | import java.net.http.HttpResponse 7 | import java.time.Duration 8 | 9 | class AiClient( 10 | private val apiKeys: List, 11 | private val timeout: Duration = Duration.ofSeconds(10L) 12 | ) : HookedApiClient() { 13 | override fun , T : Any> beforeRequest(requestBuilder: HttpRequest.Builder, request: R) { 14 | requestBuilder.header("Authorization", "Bearer ${apiKeys.random()}") 15 | requestBuilder.timeout(timeout) 16 | } 17 | 18 | override fun , T : Any> afterRequest(response: HttpResponse, request: R) { 19 | 20 | } 21 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/patreon/PatreonEntity.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.patreon 2 | 3 | data class PatreonEntity( 4 | val id: String, 5 | val type: String, 6 | val attributes: T, 7 | ) -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/patreon/PatreonResponse.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.patreon 2 | 3 | import com.fasterxml.jackson.databind.JsonNode 4 | 5 | data class PatreonResponse( 6 | val data: T, 7 | val included: List = emptyList(), 8 | ) -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/patreon/PostRequest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.patreon 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | import com.fasterxml.jackson.databind.annotation.JsonSerialize 5 | import com.fasterxml.jackson.module.kotlin.jacksonTypeRef 6 | import io.github.shoaky.sourcedownloader.sdk.util.JoinStringSerializer 7 | 8 | class PostRequest( 9 | postId: Long, 10 | @JsonSerialize(using = JoinStringSerializer::class) 11 | val include: List = mediaInclude, 12 | ) : PatreonRequest() { 13 | 14 | override val path: String = "/api/posts/$postId" 15 | override val responseBodyType: TypeReference = jacksonTypeRef() 16 | 17 | companion object { 18 | 19 | private val mediaInclude = listOf("media") 20 | } 21 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/patreon/PostTagRequest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.patreon 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty 4 | import com.fasterxml.jackson.core.type.TypeReference 5 | import com.fasterxml.jackson.module.kotlin.jacksonTypeRef 6 | 7 | class PostTagRequest( 8 | campaignId: Long, 9 | ) : PatreonRequest>>>() { 10 | 11 | override val path: String = "/api/campaigns/$campaignId/post-tags" 12 | override val responseBodyType: TypeReference>>> = jacksonTypeRef() 13 | 14 | } 15 | 16 | data class PostTag( 17 | @JsonProperty("tag_type") 18 | val tagType: String, 19 | val value: String 20 | ) 21 | 22 | -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/pixiv/GetFollowing.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.pixiv 2 | 3 | data class GetFollowing( 4 | val users: List = emptyList(), 5 | val total: Int 6 | ) 7 | -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/pixiv/GetIllustrationRequest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.pixiv 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | import com.fasterxml.jackson.module.kotlin.jacksonTypeRef 5 | import com.google.common.net.MediaType 6 | import io.github.shoaky.sourcedownloader.sdk.http.BaseRequest 7 | 8 | class GetIllustrationRequest( 9 | val id: Long 10 | ) : BaseRequest>() { 11 | 12 | override val path: String = "/ajax/illust/$id" 13 | override val responseBodyType: TypeReference> = jacksonTypeRef() 14 | override val httpMethod: String = "GET" 15 | override val mediaType: MediaType? = null 16 | 17 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/pixiv/GetIllustrationsRequest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.pixiv 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore 4 | import com.fasterxml.jackson.core.type.TypeReference 5 | import com.fasterxml.jackson.module.kotlin.jacksonTypeRef 6 | import com.google.common.net.MediaType 7 | import io.github.shoaky.sourcedownloader.sdk.http.BaseRequest 8 | 9 | class GetIllustrationsRequest( 10 | userId: Long, 11 | @JsonIgnore 12 | val ids: List, 13 | val lang: String = "zh" 14 | ) : BaseRequest>>() { 15 | 16 | override val path: String = "/ajax/user/$userId/illusts?ids[]=${ids.joinToString("&ids[]=")}" 17 | override val responseBodyType: TypeReference>> = jacksonTypeRef() 18 | override val httpMethod: String = "GET" 19 | override val mediaType: MediaType? = null 20 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/pixiv/GetUserAll.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.pixiv 2 | 3 | import com.fasterxml.jackson.databind.JsonNode 4 | import com.fasterxml.jackson.databind.node.NullNode 5 | 6 | data class GetUserAll( 7 | // key is id 8 | val illusts: Map = emptyMap(), 9 | // 有数据时是Object,没数据时会返回[] 10 | val manga: JsonNode = NullNode.instance, 11 | ) -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/pixiv/GetUserAllRequest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.pixiv 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | import com.fasterxml.jackson.module.kotlin.jacksonTypeRef 5 | import com.google.common.net.MediaType 6 | import io.github.shoaky.sourcedownloader.sdk.http.BaseRequest 7 | 8 | class GetUserAllRequest( 9 | userId: Long, 10 | val lang: String = "zh", 11 | ) : BaseRequest>() { 12 | 13 | override val path: String = "/ajax/user/$userId/profile/all" 14 | override val responseBodyType: TypeReference> = jacksonTypeRef() 15 | override val httpMethod: String = "GET" 16 | override val mediaType: MediaType? = null 17 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/pixiv/IllustrationPagesRequest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.pixiv 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | import com.fasterxml.jackson.module.kotlin.jacksonTypeRef 5 | import com.google.common.net.MediaType 6 | import io.github.shoaky.sourcedownloader.sdk.http.BaseRequest 7 | import java.net.URI 8 | 9 | class IllustrationPagesRequest( 10 | illustrationId: Long 11 | ) : BaseRequest>>() { 12 | 13 | override val path: String = "/ajax/illust/$illustrationId/pages" 14 | override val responseBodyType: TypeReference>> = jacksonTypeRef() 15 | override val httpMethod: String = "GET" 16 | override val mediaType: MediaType? = null 17 | } 18 | 19 | data class Image( 20 | val urls: Map, 21 | val width: Int, 22 | val height: Int 23 | ) -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/pixiv/PixivResponse.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.pixiv 2 | 3 | data class PixivResponse( 4 | val body: T, 5 | val error: Boolean = false, 6 | val message: String? = null, 7 | ) -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/qbittorrent/AppGetDefaultSavePathRequest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.qbittorrent 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | 5 | class AppGetDefaultSavePathRequest : QbittorrentRequest() { 6 | override val path: String = "/api/v2/app/defaultSavePath" 7 | 8 | override val responseBodyType: TypeReference = stringTypeReference 9 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/qbittorrent/LoginRequest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.qbittorrent 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | import com.google.common.net.MediaType 5 | import io.github.shoaky.sourcedownloader.sdk.http.HttpMethod 6 | 7 | class LoginRequest(val username: String?, val password: String?) : QbittorrentRequest() { 8 | override val path: String = "/api/v2/auth/login" 9 | override val responseBodyType: TypeReference = stringTypeReference 10 | override val httpMethod: String = HttpMethod.POST.name 11 | override val mediaType: MediaType = MediaType.FORM_DATA 12 | override val authenticationRequired: Boolean = false 13 | 14 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/qbittorrent/QbittorrentRequest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.qbittorrent 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore 4 | import com.google.common.net.MediaType 5 | import io.github.shoaky.sourcedownloader.sdk.http.BaseRequest 6 | import io.github.shoaky.sourcedownloader.sdk.http.HttpMethod 7 | 8 | abstract class QbittorrentRequest : BaseRequest() { 9 | override val mediaType: MediaType = MediaType.FORM_DATA 10 | override val httpMethod: String = HttpMethod.POST.name 11 | 12 | @JsonIgnore 13 | open val authenticationRequired: Boolean = true 14 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/qbittorrent/TorrentDeleteRequest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.qbittorrent 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | import com.google.common.net.MediaType 5 | import io.github.shoaky.sourcedownloader.sdk.http.HttpMethod 6 | 7 | class TorrentDeleteRequest( 8 | val hashes: List, 9 | val deleteFiles: Boolean = false 10 | ) : QbittorrentRequest() { 11 | 12 | override val path: String = "/api/v2/torrents/delete" 13 | override val responseBodyType: TypeReference = stringTypeReference 14 | override val httpMethod: String = HttpMethod.POST.name 15 | override val mediaType: MediaType = MediaType.FORM_DATA 16 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/qbittorrent/TorrentFile.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.qbittorrent 2 | 3 | import java.nio.file.Path 4 | 5 | data class TorrentFile( 6 | val index: Int, 7 | val name: Path, 8 | val progress: Float, 9 | val size: Long, 10 | /** 11 | * 0 Do not download 12 | * 1 Normal priority 13 | * 6 High priority 14 | * 7 Maximal priority 15 | */ 16 | val priority: Int 17 | ) -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/qbittorrent/TorrentFilePrioRequest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.qbittorrent 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | 5 | class TorrentFilePrioRequest( 6 | val hash: String, 7 | indexes: List, 8 | val priority: Int 9 | ) : QbittorrentRequest() { 10 | val id: String = indexes.joinToString("|") 11 | override val path: String = "/api/v2/torrents/filePrio" 12 | override val responseBodyType: TypeReference = stringTypeReference 13 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/qbittorrent/TorrentFilesRequest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.qbittorrent 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | import com.fasterxml.jackson.module.kotlin.jacksonTypeRef 5 | import io.github.shoaky.sourcedownloader.sdk.util.http.BodyWrapper 6 | 7 | class TorrentFilesRequest(val hash: String) : QbittorrentRequest() { 8 | 9 | override val path: String = "/api/v2/torrents/files" 10 | override val responseBodyType: TypeReference = jacksonTypeRef() 11 | 12 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/qbittorrent/TorrentInfo.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.qbittorrent 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty 4 | import java.nio.file.Path 5 | 6 | data class TorrentInfo( 7 | @JsonProperty("amount_left") 8 | val amountLeft: Long, 9 | val hash: String, 10 | val size: Long, 11 | val progress: Float, 12 | @JsonProperty("content_path") 13 | val contentPath: Path, 14 | ) -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/qbittorrent/TorrentProperties.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.qbittorrent 2 | 3 | data class TorrentProperties( 4 | val name: String, 5 | val size: Int, 6 | val progress: Float 7 | ) -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/qbittorrent/TorrentPropertiesRequest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.qbittorrent 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | import com.fasterxml.jackson.module.kotlin.jacksonTypeRef 5 | 6 | class TorrentPropertiesRequest(val hash: String) : QbittorrentRequest() { 7 | override val path: String = "/api/v2/torrents/properties" 8 | override val responseBodyType: TypeReference = jacksonTypeRef() 9 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/qbittorrent/TorrentsRenameFileRequest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.qbittorrent 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | 5 | class TorrentsRenameFileRequest( 6 | val hash: String, 7 | val oldPath: String, 8 | val newPath: String 9 | ) : QbittorrentRequest() { 10 | 11 | override val path: String = "/api/v2/torrents/renameFile" 12 | override val responseBodyType: TypeReference = stringTypeReference 13 | 14 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/qbittorrent/TorrentsSetLocationRequest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.qbittorrent 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | 5 | class TorrentsSetLocationRequest(hashList: List, val location: String) : QbittorrentRequest() { 6 | 7 | val hashes = hashList.joinToString("|") 8 | 9 | override val path: String = "/api/v2/torrents/setLocation" 10 | override val responseBodyType: TypeReference = stringTypeReference 11 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/rss/ItemExt.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.rss 2 | 3 | import com.apptasticsoftware.rssreader.DateTimeParser 4 | import com.apptasticsoftware.rssreader.Item 5 | 6 | class ItemExt(dateTimeParser: DateTimeParser) : Item(dateTimeParser) { 7 | 8 | val tags: MutableSet = mutableSetOf() 9 | val attrs: MutableMap = mutableMapOf() 10 | 11 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/rss/RssExtReader.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.rss 2 | 3 | import com.apptasticsoftware.rssreader.AbstractRssReader 4 | import com.apptasticsoftware.rssreader.Channel 5 | import com.apptasticsoftware.rssreader.DateTimeParser 6 | import io.github.shoaky.sourcedownloader.sdk.util.http.httpClient 7 | 8 | class RssExtReader : AbstractRssReader(httpClient) { 9 | 10 | override fun createChannel(dateTimeParser: DateTimeParser): Channel { 11 | return Channel(dateTimeParser) 12 | } 13 | 14 | override fun createItem(dateTimeParser: DateTimeParser): ItemExt { 15 | return ItemExt(dateTimeParser) 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/season/ContainsSeasonKeyword.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.season 2 | 3 | object ContainsSeasonKeyword : SeasonParser { 4 | 5 | /** 6 | * 只需要被空格包围的 7 | */ 8 | private val keywords = 9 | listOf(" II ", " III ", " IV ", " V ", " VI ", " VII ", " VIII ", " IX ", " X ", " Ⅲ ", " Ⅱ ", " Ⅳ ") 10 | 11 | override fun input(subject: String): SeasonResult? { 12 | for (keyword in keywords) { 13 | if (subject.contains(keyword).not()) { 14 | continue 15 | } 16 | 17 | GeneralSeasonParser.seasonNumberMapping[keyword.trim()]?.let { 18 | return SeasonResult(it, SeasonResult.Accuracy.ACCURATE) 19 | } 20 | } 21 | return null 22 | } 23 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/season/ExtractTitleSeasonParser.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.season 2 | 3 | import com.dgtlrepublic.anitomyj.AnitomyJ 4 | import com.dgtlrepublic.anitomyj.Element 5 | 6 | object ExtractTitleSeasonParser : SeasonParser { 7 | 8 | override fun input(subject: String): SeasonResult? { 9 | return AnitomyJ.parse(subject) 10 | .filter { it.category == Element.ElementCategory.kElementAnimeTitle } 11 | .map { 12 | LastStringSeasonParser.input(it.value) 13 | }.firstOrNull() 14 | } 15 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/season/SeasonParser.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.season 2 | 3 | interface SeasonParser { 4 | 5 | fun input(subject: String): SeasonResult? 6 | 7 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/season/SeasonResult.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.season 2 | 3 | data class SeasonResult( 4 | val value: Int? = null, 5 | val accuracy: Accuracy = Accuracy.MED 6 | ) : Comparable { 7 | 8 | override fun compareTo(other: SeasonResult): Int { 9 | return accuracy.compareTo(other.accuracy) 10 | } 11 | 12 | enum class Accuracy { 13 | LOW, 14 | MED, 15 | ACCURATE 16 | } 17 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/season/SpSeasonParser.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.season 2 | 3 | object SpSeasonParser : SeasonParser { 4 | 5 | private val spRegexes = listOf( 6 | "OVA|OAD|SPs|S00|SP(\\d+)|SP (\\d+)".toRegex(), 7 | "Special|extra(\\d+)".toRegex(RegexOption.IGNORE_CASE), 8 | "特别篇|特別篇|\\[SP]|映像特典".toRegex(), 9 | ) 10 | 11 | override fun input(subject: String): SeasonResult? { 12 | for (spRegex in spRegexes) { 13 | val matchResult = spRegex.find(subject) 14 | if (matchResult != null) { 15 | return SeasonResult(0, SeasonResult.Accuracy.ACCURATE) 16 | } 17 | } 18 | return null 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/tmdb/GetTvShow.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.tmdb 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore 4 | import com.fasterxml.jackson.core.type.TypeReference 5 | import com.fasterxml.jackson.module.kotlin.jacksonTypeRef 6 | import com.google.common.net.MediaType 7 | import io.github.shoaky.sourcedownloader.sdk.http.BaseRequest 8 | import io.github.shoaky.sourcedownloader.sdk.http.HttpMethod 9 | 10 | data class GetTvShow( 11 | @JsonIgnore 12 | val id: Long, 13 | val language: String = "ja-jp", 14 | ) : BaseRequest() { 15 | 16 | override val path: String = "/3/tv/$id" 17 | override val responseBodyType: TypeReference = jacksonTypeRef() 18 | override val httpMethod: String = HttpMethod.GET.name 19 | override val mediaType: MediaType = MediaType.JSON_UTF_8 20 | 21 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/tmdb/PageResult.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.tmdb 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty 4 | 5 | 6 | data class PageResult( 7 | val page: Int, 8 | val results: List, 9 | @JsonProperty("total_pages") 10 | val totalPages: Int, 11 | @JsonProperty("total_results") 12 | val totalResults: Int, 13 | ) 14 | -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/tmdb/SearchResult.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.tmdb 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty 4 | 5 | data class SearchResult( 6 | val id: Long, 7 | @JsonProperty("original_name") 8 | val originalName: String, 9 | val name: String 10 | ) -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/tmdb/TmdbSeason.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.tmdb 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty 4 | 5 | data class TmdbSeason( 6 | val name: String, 7 | @JsonProperty("season_number") 8 | val seasonNumber: Int, 9 | ) 10 | -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/tmdb/TvShow.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.tmdb 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty 4 | 5 | data class TvShow( 6 | val id: Long, 7 | @JsonProperty("number_of_seasons") 8 | val numberOfSeasons: Int? = null, 9 | val seasons: List 10 | ) 11 | -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/transmission/Seassion.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.transmission 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty 4 | import java.nio.file.Path 5 | 6 | data class Session( 7 | @JsonProperty("download-dir") 8 | val downloadPath: Path, 9 | val version: String 10 | ) 11 | -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/transmission/SessionGet.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.transmission 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | import com.fasterxml.jackson.module.kotlin.jacksonTypeRef 5 | 6 | class SessionGet : TransmissionRequest() { 7 | override val method: String = "session-get" 8 | override val arguments: Map = mapOf() 9 | override val responseBodyType: TypeReference> = jacksonTypeRef() 10 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/transmission/TestCsrfRequest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.transmission 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | import com.fasterxml.jackson.module.kotlin.jacksonTypeRef 5 | 6 | internal class TestCsrfRequest : TransmissionRequest() { 7 | override val method: String = "test" 8 | override val arguments: Map = emptyMap() 9 | override val responseBodyType: TypeReference> = jacksonTypeRef() 10 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/transmission/Torrent.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.transmission 2 | 3 | import java.nio.file.Path 4 | 5 | data class Torrent( 6 | val id: Long, 7 | val name: String, 8 | val hashString: String, 9 | val isFinished: Boolean, 10 | val status: Int, 11 | val files: List, 12 | val fileStats: List, 13 | val percentComplete: Double, 14 | ) 15 | 16 | data class TorrentFile( 17 | val name: Path, 18 | val length: Long 19 | ) 20 | 21 | data class FileStats( 22 | val priority: Int, 23 | val wanted: Boolean 24 | ) -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/transmission/TorrentDelete.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.transmission 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | import com.fasterxml.jackson.module.kotlin.jacksonTypeRef 5 | 6 | class TorrentDelete( 7 | private val ids: List, 8 | private val deleteLocalData: Boolean = false 9 | ) : TransmissionRequest() { 10 | 11 | override val method: String = "torrent-remove" 12 | override val responseBodyType: TypeReference> = jacksonTypeRef() 13 | override val arguments: Map = buildMap { 14 | ids.takeIf { it.isNotEmpty() }?.let { put("ids", it) } 15 | put("delete-local-data", deleteLocalData) 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/transmission/TorrentGet.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.transmission 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | import com.fasterxml.jackson.module.kotlin.jacksonTypeRef 5 | import kotlin.reflect.full.declaredMemberProperties 6 | 7 | class TorrentGet( 8 | private val ids: List = emptyList() 9 | ) : TransmissionRequest() { 10 | 11 | override val method: String = "torrent-get" 12 | override val responseBodyType: TypeReference> = jacksonTypeRef() 13 | override val arguments: Map = buildMap { 14 | ids.takeIf { it.isNotEmpty() }?.let { put("ids", it) } 15 | put("fields", fields) 16 | } 17 | 18 | companion object { 19 | private val fields = Torrent::class.declaredMemberProperties.map { it.name } 20 | } 21 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/transmission/TorrentSet.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.transmission 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | import com.fasterxml.jackson.module.kotlin.jacksonTypeRef 5 | 6 | data class TorrentSet( 7 | private val ids: List = listOf(), 8 | private val filesUnwanted: List = listOf(), 9 | ) : TransmissionRequest() { 10 | override val method: String = "torrent-set" 11 | override val responseBodyType: TypeReference> = jacksonTypeRef() 12 | override val arguments: Map = buildMap { 13 | if (ids.isEmpty()) { 14 | throw IllegalArgumentException("ids must not be empty") 15 | } 16 | put("ids", ids) 17 | filesUnwanted.takeIf { it.isNotEmpty() }?.let { put("files-unwanted", it) } 18 | } 19 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/transmission/TorrentSetLocation.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.transmission 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | import com.fasterxml.jackson.module.kotlin.jacksonTypeRef 5 | import java.nio.file.Path 6 | 7 | class TorrentSetLocation( 8 | private val ids: List = listOf(), 9 | private val location: Path, 10 | private val move: Boolean 11 | ) : TransmissionRequest() { 12 | override val method: String = "torrent-set-location" 13 | override val arguments: Map = buildMap { 14 | if (ids.isEmpty()) { 15 | throw IllegalArgumentException("ids must not be empty") 16 | } 17 | put("ids", ids) 18 | put("location", location.toString()) 19 | put("move", move) 20 | } 21 | override val responseBodyType: TypeReference> = jacksonTypeRef() 22 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/transmission/TransmissionResponse.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.transmission 2 | 3 | data class TransmissionResponse( 4 | val arguments: R, 5 | val result: String = "failed", 6 | val tag: Long 7 | ) { 8 | fun isSuccess(): Boolean { 9 | return result == SUCCESS 10 | } 11 | 12 | companion object { 13 | private const val SUCCESS = "success" 14 | } 15 | } 16 | 17 | data class TorrentGetResponse( 18 | val torrents: List 19 | ) -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/ydl/CancelDownload.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.ydl 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty 4 | import com.fasterxml.jackson.core.type.TypeReference 5 | import com.google.common.net.MediaType 6 | import io.github.shoaky.sourcedownloader.sdk.http.BaseRequest 7 | 8 | class CancelDownload( 9 | @JsonProperty("download_uid") 10 | val downloadUid: String, 11 | ) : BaseRequest() { 12 | 13 | override val path: String = "/api/cancelDownload" 14 | override val responseBodyType: TypeReference = 15 | object : TypeReference() {} 16 | override val httpMethod: String = "POST" 17 | override val mediaType: MediaType = MediaType.JSON_UTF_8 18 | 19 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/ydl/DownloadInfo.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.ydl 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty 4 | 5 | data class DownloadInfo( 6 | val uid: String, 7 | val url: String, 8 | val title: String, 9 | val type: String, 10 | @JsonProperty("percent_complete") 11 | val percentComplete: Double 12 | ) -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/ydl/DownloadsResponse.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.ydl 2 | 3 | data class DownloadsResponse( 4 | val downloads: List 5 | ) -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/ydl/FileFormate.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.ydl 2 | 3 | data class FileFormate( 4 | val id: String, 5 | val title: String, 6 | val filename: String 7 | ) -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/ydl/GetDownloads.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.ydl 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | import com.fasterxml.jackson.module.kotlin.jacksonTypeRef 5 | import com.google.common.net.MediaType 6 | import io.github.shoaky.sourcedownloader.sdk.http.BaseRequest 7 | 8 | class GetDownloads : BaseRequest() { 9 | 10 | override val path: String = "/api/downloads" 11 | override val responseBodyType: TypeReference = jacksonTypeRef() 12 | override val httpMethod: String = "POST" 13 | override val mediaType: MediaType = MediaType.JSON_UTF_8 14 | 15 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/ydl/GetFileFormats.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.ydl 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | import com.google.common.net.MediaType 5 | import io.github.shoaky.sourcedownloader.sdk.http.BaseRequest 6 | import java.net.URL 7 | 8 | class GetFileFormats( 9 | val url: URL, 10 | ) : BaseRequest>() { 11 | 12 | override val path: String = "/api/getFileFormats" 13 | override val responseBodyType: TypeReference> = 14 | object : TypeReference>() {} 15 | override val httpMethod: String = "POST" 16 | override val mediaType: MediaType = MediaType.JSON_UTF_8 17 | 18 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/ydl/SubmitDownload.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.ydl 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | import com.google.common.net.MediaType 5 | import io.github.shoaky.sourcedownloader.sdk.http.BaseRequest 6 | import java.net.URL 7 | 8 | class SubmitDownload( 9 | val url: URL, 10 | val type: String = "video", 11 | ) : BaseRequest() { 12 | 13 | override val path: String = "/api/downloadFile" 14 | override val responseBodyType: TypeReference = 15 | object : TypeReference() {} 16 | override val httpMethod: String = "POST" 17 | override val mediaType: MediaType = MediaType.JSON_UTF_8 18 | 19 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/external/ydl/YoutubeDLResponse.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.ydl 2 | 3 | data class YoutubeDLResponse( 4 | val result: T, 5 | ) -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/util/UnescapeHtmlDeserializer.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.util 2 | 3 | import com.fasterxml.jackson.core.JsonParser 4 | import com.fasterxml.jackson.databind.DeserializationContext 5 | import com.fasterxml.jackson.databind.deser.std.StringDeserializer 6 | import org.apache.commons.text.StringEscapeUtils 7 | 8 | class UnescapeHtmlDeserializer : StringDeserializer() { 9 | 10 | override fun deserialize(p: JsonParser, ctxt: DeserializationContext): String? { 11 | val deserialize = super.deserialize(p, ctxt) 12 | return StringEscapeUtils.unescapeHtml4(deserialize) 13 | } 14 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/resources/META-INF/native-image/io.github.shoaky/source-downloader/native-image.properties: -------------------------------------------------------------------------------- 1 | Args=--features=io.github.shoaky.sourcedownloader.nativeimage.CommonPluginFeature -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/resources/META-INF/native-image/io.github.shoaky/source-downloader/reflect-config.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "bt.bencoding.serializers.BEMapDecoder", 4 | "methods": [ 5 | { 6 | "name": "", 7 | "parameterTypes": [] 8 | } 9 | ] 10 | }, 11 | { 12 | "name": "bt.bencoding.serializers.BEListDecoder", 13 | "methods": [ 14 | { 15 | "name": "", 16 | "parameterTypes": [] 17 | } 18 | ] 19 | }, 20 | { 21 | "name": "bt.bencoding.serializers.BEIntegerDecoder", 22 | "methods": [ 23 | { 24 | "name": "", 25 | "parameterTypes": [] 26 | } 27 | ] 28 | }, 29 | { 30 | "name": "bt.bencoding.serializers.BEStringDecoder", 31 | "methods": [ 32 | { 33 | "name": "", 34 | "parameterTypes": [] 35 | } 36 | ] 37 | } 38 | ] -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/resources/META-INF/native-image/io.github.shoaky/source-downloader/resource-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "resources": { 3 | "includes": [ 4 | { 5 | "pattern": "\\Qorg/apache/tika/mime/tika-mimetypes.xml\\E" 6 | }, 7 | { 8 | "pattern": "\\Qlanguages/zh-CN\\E" 9 | }, 10 | { 11 | "pattern": "\\Qlanguages/zh-TW\\E" 12 | }, 13 | { 14 | "pattern": "\\Qlanguages/ja\\E" 15 | } 16 | ], 17 | "excludes": [] 18 | }, 19 | "bundles": [] 20 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/resources/META-INF/services/io.github.shoaky.sourcedownloader.sdk.plugin.Plugin: -------------------------------------------------------------------------------- 1 | io.github.shoaky.sourcedownloader.common.CommonPlugin -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/resources/custom-mimetypes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <_comment>Subtitle File 5 | 6 | 7 | 8 | 9 | 10 | 11 | <_comment>Nfo File 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /plugins/common-plugin/src/main/resources/sd-description.yaml: -------------------------------------------------------------------------------- 1 | component: 2 | - id: io.github.shoaky.sourcedownloader.common.supplier.MikanVariableProviderSupplier 3 | description: | 4 | 根据蜜柑RSS的link,爬取番剧对应页面获取到对应的bgm.tv的信息 5 | 季度则会从bgm.tv的中标题,文件名,原标题+TMDB解析,如果都没有符合规则的则默认01 6 | 因此该变量提供是非常准确和稳定的,只有season存在不准确的情况 7 | properties: 8 | - name: token 9 | description: 蜜柑的token,有些番剧需要登陆才能到番剧详情页面推荐填写 10 | required: false 11 | variables: 12 | - name: name 13 | description: bgm.tv 番剧原名 14 | - name: nameCn 15 | description: bgm.tv 番剧中文名 16 | - name: mikanTitle 17 | description: 蜜柑番剧名 18 | - name: date 19 | description: bgm.tv 番剧放送日期 20 | example: 2022-01-01 21 | - name: year 22 | description: 年份 23 | - name: month 24 | description: 月份 25 | - name: season 26 | description: 动画季度 27 | example: 01 -------------------------------------------------------------------------------- /plugins/common-plugin/src/test/kotlin/io/github/shoaky/sourcedownloader/Support.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.SourceItem 4 | import java.net.URI 5 | import java.time.LocalDateTime 6 | 7 | 8 | fun sourceItem(title: String = "test", contentType: String = "", 9 | link: String = "http://localhost", downloadUrl: String = "http://localhost" 10 | ): SourceItem { 11 | return SourceItem( 12 | title, 13 | URI(link), 14 | LocalDateTime.now(), 15 | contentType, 16 | URI(downloadUrl) 17 | ) 18 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/test/kotlin/io/github/shoaky/sourcedownloader/common/anime/AnimeTaggerTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.common.anime 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.SourceFile 4 | import org.junit.jupiter.api.Test 5 | import java.nio.file.Files 6 | import kotlin.io.path.Path 7 | import kotlin.test.assertEquals 8 | 9 | class AnimeTaggerTest { 10 | 11 | @Test 12 | fun test() { 13 | Files.readAllLines(Path("src", "test", "resources", "anime-tagger-data.csv")) 14 | .forEach { line -> 15 | val split = line.split(",") 16 | val expected = split[0].takeIf { it.isNotEmpty() } 17 | val path = split[1] 18 | assertEquals(expected, AnimeTagger.tag(SourceFile(Path(path)))) 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/test/kotlin/io/github/shoaky/sourcedownloader/common/dlsite/DoujinTitleTrimmerTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.common.dlsite 2 | 3 | import org.junit.jupiter.api.Test 4 | 5 | class DoujinTitleTrimmerTest { 6 | 7 | @Test 8 | fun test() { 9 | val testData = mapOf( 10 | "【1dsam】8888【222】" to "8888【222】", 11 | "【xa,ds.a】999999999【222】" to "999999999", 12 | "(1)8888【222】" to "(1)8888", 13 | "1234567890。1113" to "1234567890" 14 | ) 15 | for ((value, expect) in testData.entries) { 16 | val result = DoujinTitleTrimmer.trim(value, 10) 17 | assert(result == expect) { 18 | "Expected: $expect, but got: $result" 19 | } 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/test/kotlin/io/github/shoaky/sourcedownloader/common/mikan/MikanPointerTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.common.mikan 2 | 3 | import io.github.shoaky.sourcedownloader.common.anime.MikanPointer 4 | import org.junit.jupiter.api.Test 5 | import java.time.OffsetDateTime 6 | 7 | class MikanPointerTest { 8 | 9 | @Test 10 | fun clean() { 11 | val pointer = MikanPointer( 12 | shows = mutableMapOf( 13 | "NotCleaning" to OffsetDateTime.now(), 14 | "Clean" to OffsetDateTime.now().minusMonths(2L) 15 | ) 16 | ) 17 | pointer.cleanMonthly() 18 | assert(pointer.shows.containsKey("NotCleaning")) 19 | assert(pointer.shows.containsKey("Clean").not()) 20 | } 21 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/test/kotlin/io/github/shoaky/sourcedownloader/common/rss/RssSourceTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.common.rss 2 | 3 | import io.github.shoaky.sourcedownloader.common.supplier.RssSourceSupplier 4 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 5 | import io.github.shoaky.sourcedownloader.sdk.Properties 6 | import org.junit.jupiter.api.Assertions.assertEquals 7 | import org.junit.jupiter.api.Disabled 8 | import org.junit.jupiter.api.Test 9 | 10 | @Disabled 11 | class RssSourceTest { 12 | 13 | private val rssSource = RssSourceSupplier.apply( 14 | CoreContext.empty, 15 | Properties.fromMap( 16 | mapOf("url" to "https://mikanani.me/RSS/Bangumi?bangumiId=2852&subgroupid=583") 17 | ) 18 | ) 19 | 20 | @Test 21 | fun normal() { 22 | val items = rssSource.fetch() 23 | assertEquals(12, items.toList().size) 24 | } 25 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/test/kotlin/io/github/shoaky/sourcedownloader/external/anilist/AnilistClientTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.anilist 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.util.Jackson 4 | import org.junit.jupiter.api.Disabled 5 | import org.junit.jupiter.api.Test 6 | 7 | @Disabled 8 | class AnilistClientTest { 9 | 10 | @Test 11 | fun test() { 12 | val anilistClient = AnilistClient() 13 | val execute = anilistClient.execute(Search("Tate no Yuusha no Nariagari Season 02")) 14 | println(Jackson.toJsonString(execute.body().data.page.medias)) 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/test/kotlin/io/github/shoaky/sourcedownloader/external/tmdb/TmdbClientTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.external.tmdb 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.util.Jackson 4 | import org.junit.jupiter.api.Disabled 5 | import org.junit.jupiter.api.Test 6 | 7 | @Disabled 8 | class TmdbClientTest { 9 | 10 | private val client = TmdbClient.default 11 | 12 | @Test 13 | fun search_tv_show() { 14 | val execute = client.execute(SearchTvShow("けものフレンズ")) 15 | execute.body().results.forEach { 16 | println(Jackson.toJsonString(it)) 17 | } 18 | } 19 | 20 | @Test 21 | fun get_tv_show() { 22 | val execute = client.execute(GetTvShow(69288)) 23 | println(Jackson.toJsonString(execute.body())) 24 | } 25 | } -------------------------------------------------------------------------------- /plugins/common-plugin/src/test/resources/anime-tagger-data.csv: -------------------------------------------------------------------------------- 1 | special,/mnt/temp-media/anime-temp/[VCB-Studio] Ayakashi Triangle [Ma10p_1080p]/SPs/[VCB-Studio] Ayakashi Triangle [EP01-06 Summary][Ma10p_1080p][x265_flac].mkv 2 | ,[ANi] Dark Gathering - 黑暗集会 - 01 [1080P][Baha][WEB-DL][AAC AVC][CHT][MP4].mp4 3 | movie,[爱恋字幕社&漫猫字幕社][剧场版关于我转生变成史莱姆这档事 红莲之绊篇][Tensei shitara Slime Datta Ken Movie Guren no Kizuna-hen][1080p][MP4][简中] 4 | ova,LoveLive! 虹咲学园学园偶像同好会 Next Sky OVA GB_CN HEVC10_opus 1080p [430.13 MB] 5 | oad,LoveLive! 虹咲学园学园偶像同好会 Next Sky OAD GB_CN HEVC10_opus 1080p [430.13 MB] -------------------------------------------------------------------------------- /plugins/common-plugin/src/test/resources/language/language-simple1.srt: -------------------------------------------------------------------------------- 1 | 1 2 | 00:00:01,000 --> 00:00:03,000 3 | 我作为医生只能这么说了 4 | 5 | 2 6 | 00:00:01,000 --> 00:00:03,000 7 | 我作为医生只能这么说了 8 | 9 | 3 10 | 00:00:01,000 --> 00:00:03,000 11 | 我作为医生只能这么说了 -------------------------------------------------------------------------------- /plugins/common-plugin/src/test/resources/language/language-simple2.srt: -------------------------------------------------------------------------------- 1 | 1 2 | 00:00:01,000 --> 00:00:03,000 3 | 這是我作為母親唯一的心願了 4 | 5 | 2 6 | 00:00:01,000 --> 00:00:03,000 7 | 這是我作為母親唯一的心願了 8 | 9 | 3 10 | 00:00:01,000 --> 00:00:03,000 11 | 這是我作為母親唯一的心願了 -------------------------------------------------------------------------------- /plugins/common-plugin/src/test/resources/raw-anime-title-data.csv: -------------------------------------------------------------------------------- 1 | [Moozzi2] Hyouge Mono - TV + Tokuen BD,Hyouge Mono 2 | [ANK-Raws] ささみさん@がんばらない (BDrip 1920x1080 x264 FLAC Hi10P),ささみさん@がんばらない 3 | [Moozzi2] Yuyushiki - TV + SP,Yuyushiki 4 | [UCCUSS] Mangirl! まんがーる!,Mangirl! まんがーる! 5 | [UCCUSS] Mangirl! まんがーる!全13话,Mangirl! まんがーる! 6 | [7³ACG x 北宇治字幕组] 虹四格/虹咲四格/にじよんあにめーしょん/Nijiyon Animation | 01-15 [简繁字幕] BDrip 1080p x265 FLAC,にじよんあにめーしょん 7 | [7³ACG x 亿次研同好会(Billion Meta Lab)] 明日方舟:黎明前奏/Arknights Prelude to Dawn/アークナイツ | 01-08 [中配/日配+简繁日英字幕] BDrip 1080p x265 FLAC,アークナイツ 8 | [7³ACG] NEW GAME! S01/ニューゲーム | 01-12+OVA [简繁字幕] BDrip 1080p x265 FLAC 2.0,ニューゲーム -------------------------------------------------------------------------------- /plugins/foreign-plugin/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `java-library` 3 | } 4 | 5 | dependencies { 6 | implementation(project(":sdk")) 7 | implementation(project(":common")) 8 | implementation("org.jetbrains.kotlin:kotlin-stdlib") 9 | implementation("org.jetbrains.kotlin:kotlin-reflect") 10 | testImplementation(libs.mockito.junit.jupiter) 11 | testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") 12 | 13 | implementation("io.grpc:grpc-core:1.60.1") 14 | } -------------------------------------------------------------------------------- /plugins/foreign-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/foreign/ForeignPatternVariables.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.foreign 2 | 3 | import io.github.shoaky.sourcedownloader.foreign.methods.VariableProviderMethods 4 | import io.github.shoaky.sourcedownloader.sdk.PatternVariables 5 | 6 | class ForeignPatternVariables : PatternVariables { 7 | 8 | lateinit var client: ForeignStateClient 9 | lateinit var paths: VariableProviderMethods 10 | 11 | override fun variables(): Map { 12 | TODO() 13 | } 14 | } -------------------------------------------------------------------------------- /plugins/foreign-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/foreign/ForeignPlugin.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.foreign 2 | 3 | import io.github.shoaky.sourcedownloader.foreign.supplier.ForeignSourceSupplier 4 | import io.github.shoaky.sourcedownloader.sdk.plugin.Plugin 5 | import io.github.shoaky.sourcedownloader.sdk.plugin.PluginContext 6 | import io.github.shoaky.sourcedownloader.sdk.plugin.PluginDescription 7 | 8 | class ForeignPlugin : Plugin { 9 | 10 | override fun init(pluginContext: PluginContext) { 11 | pluginContext.registerSupplier( 12 | ForeignSourceSupplier 13 | ) 14 | } 15 | 16 | override fun destroy(pluginContext: PluginContext) { 17 | } 18 | 19 | override fun description(): PluginDescription { 20 | return PluginDescription( 21 | "foreign-plugin", 22 | "0.0.1" 23 | ) 24 | } 25 | } -------------------------------------------------------------------------------- /plugins/foreign-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/foreign/ForeignPointer.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.foreign 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore 4 | import io.github.shoaky.sourcedownloader.sdk.ItemPointer 5 | import io.github.shoaky.sourcedownloader.sdk.SourcePointer 6 | 7 | class ForeignPointer( 8 | val data: MutableMap = mutableMapOf() 9 | ) : SourcePointer { 10 | 11 | @JsonIgnore 12 | lateinit var foreignStateClient: ForeignStateClient 13 | 14 | @JsonIgnore 15 | lateinit var pointerUpdatePath: String 16 | 17 | override fun update(itemPointer: ItemPointer) { 18 | TODO() 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /plugins/foreign-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/foreign/ForeignStateClient.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.foreign 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | 5 | interface ForeignStateClient { 6 | 7 | fun postState( 8 | path: String, 9 | state: Any, 10 | typeReference: TypeReference 11 | ): T 12 | 13 | fun getState( 14 | path: String, 15 | typeReference: TypeReference 16 | ): T 17 | 18 | } -------------------------------------------------------------------------------- /plugins/foreign-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/foreign/component/ForeignFileContentFilter.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.foreign.component 2 | 3 | import io.github.shoaky.sourcedownloader.foreign.ForeignStateClient 4 | import io.github.shoaky.sourcedownloader.sdk.FileContent 5 | import io.github.shoaky.sourcedownloader.sdk.component.FileContentFilter 6 | 7 | class ForeignFileContentFilter( 8 | private val client: ForeignStateClient, 9 | ) : FileContentFilter { 10 | 11 | override fun test(t: FileContent): Boolean { 12 | TODO("Not yet implemented") 13 | } 14 | } -------------------------------------------------------------------------------- /plugins/foreign-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/foreign/component/ForeignFileExistsDetector.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.foreign.component 2 | 3 | import io.github.shoaky.sourcedownloader.foreign.ForeignStateClient 4 | import io.github.shoaky.sourcedownloader.sdk.ItemContent 5 | import io.github.shoaky.sourcedownloader.sdk.component.FileExistsDetector 6 | import io.github.shoaky.sourcedownloader.sdk.component.FileMover 7 | import java.nio.file.Path 8 | 9 | class ForeignFileExistsDetector( 10 | private val client: ForeignStateClient, 11 | ) : FileExistsDetector { 12 | 13 | override fun exists(fileMover: FileMover, content: ItemContent): Map { 14 | TODO("Not yet implemented") 15 | } 16 | } -------------------------------------------------------------------------------- /plugins/foreign-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/foreign/component/ForeignFileReplacementDecider.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.foreign.component 2 | 3 | import io.github.shoaky.sourcedownloader.foreign.ForeignStateClient 4 | import io.github.shoaky.sourcedownloader.sdk.ItemContent 5 | import io.github.shoaky.sourcedownloader.sdk.SourceFile 6 | import io.github.shoaky.sourcedownloader.sdk.component.FileReplacementDecider 7 | 8 | class ForeignFileReplacementDecider( 9 | private val client: ForeignStateClient, 10 | ) : FileReplacementDecider { 11 | 12 | override fun isReplace(current: ItemContent, before: ItemContent?, existingFile: SourceFile): Boolean { 13 | TODO("Not yet implemented") 14 | } 15 | } -------------------------------------------------------------------------------- /plugins/foreign-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/foreign/component/ForeignFileTagger.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.foreign.component 2 | 3 | import io.github.shoaky.sourcedownloader.foreign.ForeignStateClient 4 | import io.github.shoaky.sourcedownloader.sdk.SourceFile 5 | import io.github.shoaky.sourcedownloader.sdk.component.FileTagger 6 | 7 | class ForeignFileTagger( 8 | private val client: ForeignStateClient, 9 | ) : FileTagger { 10 | 11 | override fun tag(sourceFile: SourceFile): String? { 12 | TODO("Not yet implemented") 13 | } 14 | } -------------------------------------------------------------------------------- /plugins/foreign-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/foreign/component/ForeignItemContentFilter.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.foreign.component 2 | 3 | import io.github.shoaky.sourcedownloader.foreign.ForeignStateClient 4 | import io.github.shoaky.sourcedownloader.sdk.ItemContent 5 | import io.github.shoaky.sourcedownloader.sdk.component.ItemContentFilter 6 | 7 | class ForeignItemContentFilter( 8 | private val client: ForeignStateClient, 9 | ) : ItemContentFilter { 10 | 11 | override fun test(t: ItemContent): Boolean { 12 | TODO("Not yet implemented") 13 | } 14 | } -------------------------------------------------------------------------------- /plugins/foreign-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/foreign/component/ForeignItemFileResolver.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.foreign.component 2 | 3 | import com.fasterxml.jackson.module.kotlin.jacksonTypeRef 4 | import io.github.shoaky.sourcedownloader.foreign.ForeignStateClient 5 | import io.github.shoaky.sourcedownloader.sdk.SourceFile 6 | import io.github.shoaky.sourcedownloader.sdk.SourceItem 7 | import io.github.shoaky.sourcedownloader.sdk.component.ItemFileResolver 8 | 9 | class ForeignItemFileResolver( 10 | private val client: ForeignStateClient, 11 | private val path: String 12 | ) : ItemFileResolver { 13 | 14 | override fun resolveFiles(sourceItem: SourceItem): List { 15 | return client.postState( 16 | path, 17 | mapOf("sourceItem" to sourceItem), 18 | jacksonTypeRef() 19 | ) 20 | } 21 | } -------------------------------------------------------------------------------- /plugins/foreign-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/foreign/component/ForeignSourceItemFilter.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.foreign.component 2 | 3 | import io.github.shoaky.sourcedownloader.foreign.ForeignStateClient 4 | import io.github.shoaky.sourcedownloader.sdk.SourceItem 5 | import io.github.shoaky.sourcedownloader.sdk.component.SourceItemFilter 6 | 7 | class ForeignSourceItemFilter( 8 | private val client: ForeignStateClient, 9 | ) : SourceItemFilter { 10 | 11 | override fun test(t: SourceItem): Boolean { 12 | TODO("Not yet implemented") 13 | } 14 | } -------------------------------------------------------------------------------- /plugins/foreign-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/foreign/methods/DownloaderForeignMethods.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.foreign.methods 2 | 3 | data class DownloaderForeignMethods( 4 | val submit: String = "/downloader/submit", 5 | val cancel: String = "/downloader/cancel", 6 | val defaultDownloadPath: String = "/downloader/default_download_path", 7 | ) -------------------------------------------------------------------------------- /plugins/foreign-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/foreign/methods/SourceForeignMethods.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.foreign.methods 2 | 3 | data class SourceForeignMethods( 4 | val fetch: String = "/source/fetch", 5 | val next: String = "/source/next", 6 | val hasNext: String = "/source/has_next", 7 | val pointerUpdate: String = "/source/pointer_update", 8 | ) -------------------------------------------------------------------------------- /plugins/foreign-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/foreign/methods/VariableProviderMethods.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.foreign.methods 2 | 3 | data class VariableProviderMethods( 4 | val createItemGroup: String = "/variable_provider/create_item_group", 5 | val support: String = "/variable_provider/support", 6 | val accuracy: String = "/variable_provider/accuracy", 7 | val filePatternVariables: String = "/variable_provider/file_pattern_variables", 8 | val sharedPatternVariables: String = "/variable_provider/shared_pattern_variables", 9 | ) 10 | -------------------------------------------------------------------------------- /plugins/foreign-plugin/src/main/resources/META-INF/services/io.github.shoaky.sourcedownloader.sdk.plugin.Plugin: -------------------------------------------------------------------------------- 1 | io.github.shoaky.sourcedownloader.foreign.ForeignPlugin -------------------------------------------------------------------------------- /plugins/telegram4j-plugin/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | `java-library` 3 | } 4 | 5 | // repositories { 6 | // rootProject.repositories.add( 7 | // maven { 8 | // url = uri("https://mvn.mchv.eu/repository/mchv/") 9 | // } 10 | // ) 11 | // } 12 | 13 | dependencies { 14 | implementation(project(":sdk")) 15 | implementation(project(":common")) 16 | implementation("org.jetbrains.kotlin:kotlin-stdlib") 17 | implementation("org.jetbrains.kotlin:kotlin-reflect") 18 | testImplementation(libs.mockito.junit.jupiter) 19 | testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") 20 | 21 | implementation(libs.zxing.core) 22 | implementation(libs.telegram4j) 23 | // implementation(platform("it.tdlight:tdlight-java-bom:3.4.0+td.1.8.26")) 24 | // implementation("it.tdlight:tdlight-java") 25 | // implementation(group = "it.tdlight", name = "tdlight-natives", classifier = "macos_arm64") 26 | } -------------------------------------------------------------------------------- /plugins/telegram4j-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/telegram/ChatConfig.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.telegram 2 | 3 | import com.fasterxml.jackson.annotation.JsonAlias 4 | import java.time.LocalDate 5 | 6 | data class ChatConfig( 7 | @JsonAlias("chat-id") 8 | val chatId: Long, 9 | @JsonAlias("begin-date") 10 | val beginDate: LocalDate? = null 11 | ) -------------------------------------------------------------------------------- /plugins/telegram4j-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/telegram/TelegramMediaTagger.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.telegram 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.SourceFile 4 | import io.github.shoaky.sourcedownloader.sdk.component.FileTagger 5 | 6 | /** 7 | * Telegram文件标签器,用于标记Telegram文件的类型 8 | */ 9 | object TelegramMediaTagger : FileTagger { 10 | 11 | override fun tag(sourceFile: SourceFile): String? { 12 | return sourceFile.attrs[TelegramSource.MEDIA_TYPE_ATTR]?.toString() 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /plugins/telegram4j-plugin/src/main/kotlin/io/github/shoaky/sourcedownloader/telegram/TelegramMediaTaggerSupplier.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.telegram 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.CoreContext 4 | import io.github.shoaky.sourcedownloader.sdk.Properties 5 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentSupplier 6 | import io.github.shoaky.sourcedownloader.sdk.component.ComponentType 7 | 8 | object TelegramMediaTaggerSupplier : ComponentSupplier { 9 | 10 | override fun apply(context: CoreContext, props: Properties): TelegramMediaTagger { 11 | return TelegramMediaTagger 12 | } 13 | 14 | override fun supplyTypes(): List { 15 | return listOf( 16 | ComponentType.fileTagger("telegram") 17 | ) 18 | } 19 | 20 | override fun supportNoArgs(): Boolean = true 21 | } -------------------------------------------------------------------------------- /plugins/telegram4j-plugin/src/main/resources/META-INF/services/io.github.shoaky.sourcedownloader.sdk.plugin.Plugin: -------------------------------------------------------------------------------- 1 | io.github.shoaky.sourcedownloader.telegram.Telegram4jPlugin -------------------------------------------------------------------------------- /plugins/telegram4j-plugin/src/test/kotlin/io/github/shoaky/sourcedownloader/telegram/ChatPointerTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.telegram 2 | 3 | import org.junit.jupiter.api.Assertions.assertEquals 4 | import org.junit.jupiter.api.Test 5 | import telegram4j.core.util.Id 6 | 7 | class ChatPointerTest { 8 | @Test 9 | fun test() { 10 | val chatPointer = ChatPointer(1, -100) 11 | assertEquals(1, chatPointer.nextMessageId()) 12 | assertEquals(101, chatPointer.copy(fromMessageId = 100).nextMessageId()) 13 | 14 | assertEquals(11, ChatPointer(-11).parseChatId()) 15 | 16 | assertEquals(Id.Type.CHAT, ChatPointer(11).createId().type) 17 | assertEquals(Id.Type.CHANNEL, ChatPointer(-11).createId().type) 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/Cache.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk 2 | 3 | interface Cache { 4 | 5 | fun get(key: K): V 6 | } -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/CacheLoader.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk 2 | 3 | interface CacheLoader { 4 | 5 | fun load(key: K): V 6 | } -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/DownloadOptions.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk 2 | 3 | data class DownloadOptions( 4 | val category: String? = null, 5 | val tags: List = emptyList(), 6 | val headers: Map = emptyMap() 7 | ) -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/DownloadTask.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk 2 | 3 | import java.net.URI 4 | import java.nio.file.Path 5 | 6 | data class DownloadTask( 7 | val sourceItem: SourceItem, 8 | /** 9 | * The absolute path of the file to download. 10 | */ 11 | val downloadFiles: List, 12 | /** 13 | * The absolute path of the directory. 14 | */ 15 | val downloadPath: Path, 16 | val options: DownloadOptions = DownloadOptions(), 17 | ) { 18 | 19 | fun downloadUri(): URI { 20 | return sourceItem.downloadUri 21 | } 22 | 23 | fun relativePaths(): List { 24 | return downloadFiles.map { it.path }.map { 25 | if (it.isAbsolute) { 26 | downloadPath.relativize(it) 27 | } else { 28 | it 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/FileStatus.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk 2 | 3 | interface FileStatus { 4 | 5 | fun status(): String 6 | 7 | fun isSuccessful(): Boolean 8 | } -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/FixedItemContent.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk 2 | 3 | data class FixedItemContent( 4 | override val sourceItem: SourceItem, 5 | override val fileContents: List, 6 | override val itemVariables: PatternVariables = PatternVariables.EMPTY 7 | ) : ItemContent { 8 | 9 | override fun summaryContent(): String = "" 10 | } -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/InstanceFactory.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk 2 | 3 | interface InstanceFactory { 4 | fun create(props: Properties): T 5 | 6 | fun type(): Class 7 | 8 | } -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/InstanceManager.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk 2 | 3 | interface InstanceManager { 4 | 5 | fun loadInstance(name: String, klass: Class, props: Properties? = null): T 6 | 7 | fun getInstance(klass: Class): List 8 | 9 | fun registerInstanceFactory(vararg factories: InstanceFactory<*>) 10 | 11 | fun destroyInstance(name: String) 12 | } -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/ItemPointer.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk 2 | 3 | /** 4 | * 用于标识[Source]中的一个Item 5 | */ 6 | interface ItemPointer { 7 | 8 | companion object { 9 | 10 | fun of(id: String, value: Any): ItemPointer { 11 | return SimpleItemPointer(id, value) 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/NullPointer.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk 2 | 3 | object NullPointer : SourcePointer, ItemPointer { 4 | 5 | override fun toString(): String { 6 | return "null" 7 | } 8 | 9 | override fun update(itemPointer: ItemPointer) { 10 | // Do nothing 11 | } 12 | 13 | override fun equals(other: Any?): Boolean { 14 | return other is NullPointer 15 | } 16 | } -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/PatternVariables.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore 4 | import com.fasterxml.jackson.annotation.JsonInclude 5 | import com.fasterxml.jackson.module.kotlin.jacksonTypeRef 6 | import io.github.shoaky.sourcedownloader.sdk.util.Jackson 7 | 8 | @JsonInclude(JsonInclude.Include.NON_NULL) 9 | interface PatternVariables { 10 | 11 | @JsonIgnore 12 | fun variables(): Map { 13 | return Jackson.convert(this, jacksonTypeRef()) 14 | } 15 | 16 | companion object { 17 | 18 | val EMPTY = object : PatternVariables {} 19 | } 20 | } -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/PointedItem.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk 2 | 3 | data class PointedItem( 4 | val sourceItem: SourceItem, 5 | val pointer: IP 6 | ) -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/ProcessingException.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk 2 | 3 | import java.io.IOException 4 | 5 | /** 6 | * @param skip if true processor will commit source state, witch means source will not be processed again, 7 | * use it when the [SourceItem] can not be processed any more, e.g. resource is 404 8 | */ 9 | class ProcessingException( 10 | message: String, 11 | val skip: Boolean = false 12 | ) : RuntimeException(message) { 13 | 14 | companion object { 15 | 16 | fun skip(message: String): Throwable { 17 | return ProcessingException(message, true) 18 | } 19 | 20 | fun retryable(message: String, throwable: Throwable): Throwable { 21 | return IOException(message, throwable) 22 | } 23 | 24 | fun retryable(message: String): Throwable { 25 | return IOException(message) 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/ProcessorInfo.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk 2 | 3 | import com.fasterxml.jackson.databind.annotation.JsonSerialize 4 | import com.fasterxml.jackson.databind.ser.std.ToStringSerializer 5 | import java.nio.file.Path 6 | 7 | data class ProcessorInfo( 8 | val name: String, 9 | @JsonSerialize(using = ToStringSerializer::class) 10 | val downloadPath: Path, 11 | @JsonSerialize(using = ToStringSerializer::class) 12 | val sourceSavePath: Path, 13 | val tags: List = emptyList(), 14 | val category: String? = null, 15 | // val components: Map 16 | ) -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/ProcessorTask.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk 2 | 3 | class ProcessorTask( 4 | val processName: String, 5 | val runnable: Runnable, 6 | /** 7 | * For grouping tasks 8 | */ 9 | val group: String? = null 10 | ) : Runnable by runnable { 11 | 12 | override fun equals(other: Any?): Boolean { 13 | if (this === other) return true 14 | if (other !is ProcessorTask) return false 15 | 16 | if (processName != other.processName) return false 17 | 18 | return true 19 | } 20 | 21 | override fun hashCode(): Int { 22 | return processName.hashCode() 23 | } 24 | } -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/SimpleItemPointer.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk 2 | 3 | data class SimpleItemPointer( 4 | val id: String, 5 | val value: T 6 | ) : ItemPointer -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/SourceItemConvertScript.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk 2 | 3 | /** 4 | * e.g. 5 | * titleExtract = windows.document.querySelector("title").innerText 6 | * dateExtract = windows.document.querySelector("date").innerText 7 | * contentTypeExtract = windows.document.querySelector("content-type").innerText 8 | * downloadUriExtract = document.querySelector("download-uri']").href 9 | * linkExtract = window.location.href 10 | */ 11 | data class SourceItemConvertScript( 12 | /** 13 | * Item element selector add click event 14 | */ 15 | val itemBox: String, 16 | val titleExtract: String, 17 | val linkExtract: String, 18 | val dateExtract: String, 19 | val contentTypeExtract: String, 20 | val downloadUriExtract: String? = null, 21 | val attributesExtract: String = "{}", 22 | val tagsExtract: String = "[]", 23 | ) -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/SourcePointer.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.component.Source 4 | 5 | /** 6 | * 提供给[Source]数据读取到什么位置了 7 | * 该数据会被持久化,用于下一次传递给[Source]时,从上次读取的位置开始读取 8 | * 9 | */ 10 | interface SourcePointer { 11 | 12 | /** 13 | * 当Process每处理完一个Item都会调用一次该方法,用于更新[SourcePointer]的状态 14 | */ 15 | fun update(itemPointer: ItemPointer) 16 | } -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/component/AsyncDownloader.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk.component 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.SourceItem 4 | 5 | /** 6 | * Async downloader, submit method will not wait for the download to complete before returning 7 | */ 8 | interface AsyncDownloader : Downloader { 9 | 10 | /** 11 | * @param sourceItem the item to check if finished 12 | * @return null if the task not found, otherwise return true if the task is finished 13 | */ 14 | fun isFinished(sourceItem: SourceItem): Boolean? 15 | 16 | } 17 | -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/component/BatchFileMover.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk.component 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.ItemContent 4 | 5 | interface BatchFileMover : FileMover { 6 | 7 | fun batchMove(itemContent: ItemContent): BatchMoveResult 8 | 9 | } -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/component/BatchMoveResult.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk.component 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.FileContent 4 | 5 | data class BatchMoveResult( 6 | val success: Boolean, 7 | val failed: List 8 | ) -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/component/ComponentException.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk.component 2 | 3 | @Suppress("UNUSED") 4 | class ComponentException( 5 | message: String, 6 | val type: String, 7 | cause: Throwable? = null 8 | ) : RuntimeException(message, cause) { 9 | 10 | companion object { 11 | 12 | @JvmStatic 13 | fun props(message: String) = ComponentException(message, "props:invalid") 14 | 15 | @JvmStatic 16 | fun props(message: String, cause: Throwable) = ComponentException(message, "props:invalid", cause) 17 | 18 | @JvmStatic 19 | fun compatibility(message: String) = ComponentException(message, "compatibility") 20 | 21 | @JvmStatic 22 | fun other(message: String) = ComponentException(message, "other") 23 | 24 | @JvmStatic 25 | fun processing(message: String) = ComponentException(message, "processing") 26 | } 27 | } -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/component/ComponentMetadata.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk.component 2 | 3 | data class ComponentMetadata( 4 | val description: String? = null, 5 | val propertySchema: JsonSchema, 6 | val uiSchema: Map = emptyMap() 7 | ) -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/component/ComponentStateful.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk.component 2 | 3 | 4 | interface ComponentStateful { 5 | 6 | fun stateDetail(): Any 7 | 8 | } 9 | -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/component/FetchContext.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk.component 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.SourcePointer 4 | 5 | data class FetchContext( 6 | val pointer: SP, 7 | val limit: Int, 8 | val attrs: MutableMap = mutableMapOf(), 9 | ) { 10 | 11 | fun loadAttr(key: String, loader: () -> T): T { 12 | @Suppress("UNCHECKED_CAST") 13 | return attrs.getOrPut(key) { loader.invoke() } as T 14 | } 15 | } -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/http/StatusCodes.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk.http 2 | 3 | object StatusCodes { 4 | 5 | const val OK = 200 6 | const val CREATED = 201 7 | const val NOT_FOUND = 404 8 | const val BAD_REQUEST = 400 9 | const val UNAUTHORIZED = 401 10 | const val FORBIDDEN = 403 11 | const val TOO_MANY_REQUESTS = 429 12 | const val INTERNAL_SERVER_ERROR = 500 13 | const val SERVICE_UNAVAILABLE = 503 14 | const val GATEWAY_TIMEOUT = 504 15 | 16 | } -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/plugin/Plugin.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk.plugin 2 | 3 | interface Plugin { 4 | 5 | fun init(pluginContext: PluginContext) 6 | 7 | fun destroy(pluginContext: PluginContext) 8 | 9 | fun description(): PluginDescription 10 | } -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/plugin/PluginDescription.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk.plugin 2 | 3 | data class PluginDescription( 4 | val name: String, 5 | val version: String 6 | ) { 7 | fun fullName(): String = "$name:$version" 8 | } -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/util/FlattenIterator.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk.util 2 | 3 | private class FlattenIterator( 4 | private val iterator: Iterator>, 5 | ) : Iterator { 6 | 7 | private var items = iterator.next().iterator() 8 | 9 | override fun hasNext(): Boolean { 10 | if (items.hasNext()) { 11 | return true 12 | } 13 | if (iterator.hasNext()) { 14 | items = iterator.next().iterator() 15 | return hasNext() 16 | } 17 | return false 18 | } 19 | 20 | override fun next(): T { 21 | return items.next() 22 | } 23 | } 24 | 25 | fun Iterator>.flatten(): Iterator { 26 | return FlattenIterator(this) 27 | } -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/util/TextClear.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk.util 2 | 3 | class TextClear( 4 | private val replaces: Map, 5 | ) { 6 | 7 | fun input(text: String): String { 8 | var res = text 9 | replaces.forEach { 10 | res = res.replace(it.key, it.value) 11 | } 12 | return res 13 | } 14 | } -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/util/http/BodyMapper.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk.util.http 2 | 3 | import org.slf4j.LoggerFactory 4 | import java.net.CookieManager 5 | import java.net.URI 6 | import java.net.http.HttpClient 7 | import java.net.http.HttpRequest 8 | import java.time.Duration 9 | import java.util.concurrent.Executors 10 | 11 | val defaultCookieManager = CookieManager() 12 | val httpClient: HttpClient = HttpClient.newBuilder() 13 | .executor(Executors.newVirtualThreadPerTaskExecutor()) 14 | .cookieHandler(defaultCookieManager) 15 | .connectTimeout(Duration.ofSeconds(10)) 16 | .build() 17 | 18 | internal val log = LoggerFactory.getLogger("HTTP") 19 | 20 | fun httpGetRequest( 21 | uri: URI, 22 | headers: Map = emptyMap() 23 | ): HttpRequest { 24 | val builder = HttpRequest.newBuilder(uri).GET() 25 | headers.forEach(builder::header) 26 | return builder.build() 27 | } -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/util/http/BodyMappingException.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk.util.http 2 | 3 | import java.net.http.HttpHeaders 4 | 5 | class BodyMappingException( 6 | val body: String, 7 | val statusCode: Int, 8 | val headers: HttpHeaders, 9 | message: String, 10 | ) : RuntimeException(message) -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/util/http/BodyWrapper.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk.util.http 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference 4 | import io.github.shoaky.sourcedownloader.sdk.util.Jackson 5 | 6 | class BodyWrapper( 7 | private val raw: ByteArray 8 | ) { 9 | 10 | fun parseJson(typeRef: TypeReference): T { 11 | return Jackson.fromJson(raw, typeRef) 12 | } 13 | 14 | fun stringify(): String { 15 | return String(raw, Charsets.UTF_8) 16 | } 17 | } -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/util/http/CommonBodyMapper.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk.util.http 2 | 3 | class CommonBodyMapper( 4 | private val bodyMapper: BodyMapper 5 | ) : BodyMapper { 6 | 7 | override fun mapping(info: MappingInfo): T { 8 | return bodyMapper.mapping(info) 9 | } 10 | } -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/util/masking/Masking.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk.util.masking 2 | 3 | import com.fasterxml.jackson.annotation.JacksonAnnotationsInside 4 | import com.fasterxml.jackson.databind.annotation.JsonSerialize 5 | import io.github.shoaky.sourcedownloader.sdk.util.masking.MaskingMiddle 6 | import io.github.shoaky.sourcedownloader.sdk.util.masking.StringMaskingSerializer 7 | import kotlin.reflect.KClass 8 | 9 | /** 10 | * jackson字符串脱敏 具体逻辑由[StringMasking]的实现决定 11 | */ 12 | @JacksonAnnotationsInside 13 | @Target(AnnotationTarget.FIELD) 14 | @JsonSerialize(using = StringMaskingSerializer::class) 15 | @Retention(AnnotationRetention.RUNTIME) 16 | annotation class Masking( 17 | val value: KClass = MaskingMiddle::class 18 | ) 19 | -------------------------------------------------------------------------------- /sdk/src/main/kotlin/io/github/shoaky/sourcedownloader/sdk/util/masking/StringMasking.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk.util.masking 2 | 3 | interface StringMasking { 4 | 5 | fun mask(data: String): String 6 | } 7 | -------------------------------------------------------------------------------- /sdk/src/test/kotlin/io/github/shoaky/sourcedownloader/sdk/PatternVariablesTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk 2 | 3 | import org.junit.jupiter.api.Test 4 | import kotlin.test.assertEquals 5 | 6 | class PatternVariablesTest { 7 | 8 | @Test 9 | fun given_null_value_should_empty() { 10 | val variables = TestData().variables() 11 | assertEquals(true, variables.isEmpty()) 12 | } 13 | 14 | } 15 | 16 | private data class TestData( 17 | val name: String? = null, 18 | ) : PatternVariables -------------------------------------------------------------------------------- /sdk/src/test/kotlin/io/github/shoaky/sourcedownloader/sdk/component/ComponentTypeTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk.component 2 | 3 | import org.junit.jupiter.api.Assertions.assertEquals 4 | import org.junit.jupiter.api.Test 5 | 6 | class ComponentTypeTest { 7 | @Test 8 | fun given_mover_type_should_expected() { 9 | val type = ComponentType.fileMover("mikan") 10 | val fullName = type.fullName() 11 | assertEquals("file-mover:mikan", fullName) 12 | val instanceName = type.instanceName("mine") 13 | assertEquals("$fullName:mine", instanceName) 14 | } 15 | } -------------------------------------------------------------------------------- /sdk/src/test/kotlin/io/github/shoaky/sourcedownloader/sdk/util/FlattenIteratorTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk.util 2 | 3 | import org.junit.jupiter.api.Test 4 | import kotlin.test.assertContentEquals 5 | 6 | class FlattenIteratorTest { 7 | 8 | @Test 9 | fun test() { 10 | val data = (0..20).toList() 11 | val flatten = data.chunked(2).flatten() 12 | assertContentEquals(data, flatten.toList()) 13 | } 14 | 15 | @Test 16 | fun given_empty_list() { 17 | val data = listOf() 18 | val flatten = data.chunked(2).flatten() 19 | assertContentEquals(data, flatten.toList()) 20 | } 21 | } -------------------------------------------------------------------------------- /sdk/src/test/kotlin/io/github/shoaky/sourcedownloader/sdk/util/masking/MaskingTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.shoaky.sourcedownloader.sdk.util.masking 2 | 3 | import io.github.shoaky.sourcedownloader.sdk.util.Jackson 4 | import org.junit.jupiter.api.Test 5 | 6 | class MaskingTest { 7 | 8 | @Test 9 | fun masking() { 10 | val json = Jackson.toJsonString(Data("1111111", "22222")) 11 | assert(json.contains("11***11")) 12 | assert(json.contains("22222")) 13 | } 14 | 15 | private data class Data( 16 | @Masking 17 | val name: String, 18 | val mobile: String, 19 | ) 20 | } -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "source-downloader" 2 | 3 | include(":sdk") 4 | include(":core") 5 | include(":plugins:common-plugin") 6 | include(":plugins:telegram4j-plugin") 7 | include(":plugins:foreign-plugin") 8 | include(":common") 9 | include(":applications:spring") 10 | include(":applications:minimize") 11 | include(":applications:vertx") 12 | 13 | pluginManagement { 14 | repositories { 15 | mavenCentral() 16 | gradlePluginPortal() 17 | maven { url = uri("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap") } 18 | } 19 | } 20 | plugins { 21 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" 22 | } 23 | --------------------------------------------------------------------------------