Files
epgphp/public.php
mxdabc 57dae083a6 init: Upload Code From Github
要提交的变更:
	修改:     README.md
	新文件:   README_zh-CN.md
	新文件:   assets/CHANGELOG.md
	新文件:   assets/Parsedown.php
	新文件:   assets/css/login.css
	新文件:   assets/css/manage.css
	新文件:   assets/defaultConfig.json
	新文件:   assets/defaultIconList.json
	新文件:   assets/html/favicon.ico
	新文件:   assets/html/login.html
	新文件:   assets/html/manage.html
	新文件:   assets/js/console.js
	新文件:   assets/js/manage.js
	新文件:   assets/opencc/composer.json
	新文件:   assets/opencc/composer.lock
	新文件:   assets/opencc/vendor/autoload.php
	新文件:   assets/opencc/vendor/bin/opencc
	新文件:   assets/opencc/vendor/composer/ClassLoader.php
	新文件:   assets/opencc/vendor/composer/InstalledVersions.php
	新文件:   assets/opencc/vendor/composer/LICENSE
	新文件:   assets/opencc/vendor/composer/autoload_classmap.php
	新文件:   assets/opencc/vendor/composer/autoload_files.php
	新文件:   assets/opencc/vendor/composer/autoload_namespaces.php
	新文件:   assets/opencc/vendor/composer/autoload_psr4.php
	新文件:   assets/opencc/vendor/composer/autoload_real.php
	新文件:   assets/opencc/vendor/composer/autoload_static.php
	新文件:   assets/opencc/vendor/composer/installed.json
	新文件:   assets/opencc/vendor/composer/installed.php
	新文件:   assets/opencc/vendor/composer/platform_check.php
	新文件:   assets/opencc/vendor/overtrue/php-opencc/.editorconfig
	新文件:   assets/opencc/vendor/overtrue/php-opencc/.github/FUNDING.yml
	新文件:   assets/opencc/vendor/overtrue/php-opencc/.github/workflows/test.yml
	新文件:   assets/opencc/vendor/overtrue/php-opencc/LICENSE
	新文件:   assets/opencc/vendor/overtrue/php-opencc/README.md
	新文件:   assets/opencc/vendor/overtrue/php-opencc/bin/build
	新文件:   assets/opencc/vendor/overtrue/php-opencc/bin/opencc
	新文件:   assets/opencc/vendor/overtrue/php-opencc/composer.json
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/dictionary/HKVariants.txt
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/dictionary/HKVariantsRevPhrases.txt
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/dictionary/JPShinjitaiCharacters.txt
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/dictionary/JPShinjitaiPhrases.txt
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/dictionary/JPVariants.txt
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/dictionary/README.md
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/dictionary/STCharacters.txt
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/dictionary/STPhrases.txt
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/dictionary/TSCharacters.txt
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/dictionary/TSPhrases.txt
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/dictionary/TWPhrasesIT.txt
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/dictionary/TWPhrasesName.txt
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/dictionary/TWPhrasesOther.txt
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/dictionary/TWVariants.txt
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/dictionary/TWVariantsRevPhrases.txt
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/parsed/HKVariants.php
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/parsed/HKVariantsRev.php
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/parsed/HKVariantsRevPhrases.php
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/parsed/JPShinjitaiCharacters.php
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/parsed/JPShinjitaiPhrases.php
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/parsed/JPVariants.php
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/parsed/JPVariantsRev.php
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/parsed/STCharacters.php
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/parsed/STPhrases.php
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/parsed/TSCharacters.php
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/parsed/TSPhrases.php
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/parsed/TWPhrases.php
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/parsed/TWPhrasesIT.php
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/parsed/TWPhrasesName.php
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/parsed/TWPhrasesOther.php
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/parsed/TWPhrasesRev.php
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/parsed/TWVariants.php
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/parsed/TWVariantsRev.php
	新文件:   assets/opencc/vendor/overtrue/php-opencc/data/parsed/TWVariantsRevPhrases.php
	新文件:   assets/opencc/vendor/overtrue/php-opencc/src/Console/BuildCommand.php
	新文件:   assets/opencc/vendor/overtrue/php-opencc/src/Console/ConvertCommand.php
	新文件:   assets/opencc/vendor/overtrue/php-opencc/src/Contracts/ConverterInterface.php
	新文件:   assets/opencc/vendor/overtrue/php-opencc/src/Converter.php
	新文件:   assets/opencc/vendor/overtrue/php-opencc/src/Dictionary.php
	新文件:   assets/opencc/vendor/overtrue/php-opencc/src/OpenCC.php
	新文件:   assets/opencc/vendor/overtrue/php-opencc/src/Strategy.php
	新文件:   assets/opencc/vendor/psr/container/.gitignore
	新文件:   assets/opencc/vendor/psr/container/LICENSE
	新文件:   assets/opencc/vendor/psr/container/README.md
	新文件:   assets/opencc/vendor/psr/container/composer.json
	新文件:   assets/opencc/vendor/psr/container/src/ContainerExceptionInterface.php
	新文件:   assets/opencc/vendor/psr/container/src/ContainerInterface.php
	新文件:   assets/opencc/vendor/psr/container/src/NotFoundExceptionInterface.php
	新文件:   assets/opencc/vendor/symfony/console/Application.php
	新文件:   assets/opencc/vendor/symfony/console/Attribute/AsCommand.php
	新文件:   assets/opencc/vendor/symfony/console/CHANGELOG.md
	新文件:   assets/opencc/vendor/symfony/console/CI/GithubActionReporter.php
	新文件:   assets/opencc/vendor/symfony/console/Color.php
	新文件:   assets/opencc/vendor/symfony/console/Command/Command.php
	新文件:   assets/opencc/vendor/symfony/console/Command/CompleteCommand.php
	新文件:   assets/opencc/vendor/symfony/console/Command/DumpCompletionCommand.php
	新文件:   assets/opencc/vendor/symfony/console/Command/HelpCommand.php
	新文件:   assets/opencc/vendor/symfony/console/Command/LazyCommand.php
	新文件:   assets/opencc/vendor/symfony/console/Command/ListCommand.php
	新文件:   assets/opencc/vendor/symfony/console/Command/LockableTrait.php
	新文件:   assets/opencc/vendor/symfony/console/Command/SignalableCommandInterface.php
	新文件:   assets/opencc/vendor/symfony/console/Command/TraceableCommand.php
	新文件:   assets/opencc/vendor/symfony/console/CommandLoader/CommandLoaderInterface.php
	新文件:   assets/opencc/vendor/symfony/console/CommandLoader/ContainerCommandLoader.php
	新文件:   assets/opencc/vendor/symfony/console/CommandLoader/FactoryCommandLoader.php
	新文件:   assets/opencc/vendor/symfony/console/Completion/CompletionInput.php
	新文件:   assets/opencc/vendor/symfony/console/Completion/CompletionSuggestions.php
	新文件:   assets/opencc/vendor/symfony/console/Completion/Output/BashCompletionOutput.php
	新文件:   assets/opencc/vendor/symfony/console/Completion/Output/CompletionOutputInterface.php
	新文件:   assets/opencc/vendor/symfony/console/Completion/Output/FishCompletionOutput.php
	新文件:   assets/opencc/vendor/symfony/console/Completion/Output/ZshCompletionOutput.php
	新文件:   assets/opencc/vendor/symfony/console/Completion/Suggestion.php
	新文件:   assets/opencc/vendor/symfony/console/ConsoleEvents.php
	新文件:   assets/opencc/vendor/symfony/console/Cursor.php
	新文件:   assets/opencc/vendor/symfony/console/DataCollector/CommandDataCollector.php
	新文件:   assets/opencc/vendor/symfony/console/Debug/CliRequest.php
	新文件:   assets/opencc/vendor/symfony/console/DependencyInjection/AddConsoleCommandPass.php
	新文件:   assets/opencc/vendor/symfony/console/Descriptor/ApplicationDescription.php
	新文件:   assets/opencc/vendor/symfony/console/Descriptor/Descriptor.php
	新文件:   assets/opencc/vendor/symfony/console/Descriptor/DescriptorInterface.php
	新文件:   assets/opencc/vendor/symfony/console/Descriptor/JsonDescriptor.php
	新文件:   assets/opencc/vendor/symfony/console/Descriptor/MarkdownDescriptor.php
	新文件:   assets/opencc/vendor/symfony/console/Descriptor/ReStructuredTextDescriptor.php
	新文件:   assets/opencc/vendor/symfony/console/Descriptor/TextDescriptor.php
	新文件:   assets/opencc/vendor/symfony/console/Descriptor/XmlDescriptor.php
	新文件:   assets/opencc/vendor/symfony/console/Event/ConsoleCommandEvent.php
	新文件:   assets/opencc/vendor/symfony/console/Event/ConsoleErrorEvent.php
	新文件:   assets/opencc/vendor/symfony/console/Event/ConsoleEvent.php
	新文件:   assets/opencc/vendor/symfony/console/Event/ConsoleSignalEvent.php
	新文件:   assets/opencc/vendor/symfony/console/Event/ConsoleTerminateEvent.php
	新文件:   assets/opencc/vendor/symfony/console/EventListener/ErrorListener.php
	新文件:   assets/opencc/vendor/symfony/console/Exception/CommandNotFoundException.php
	新文件:   assets/opencc/vendor/symfony/console/Exception/ExceptionInterface.php
	新文件:   assets/opencc/vendor/symfony/console/Exception/InvalidArgumentException.php
	新文件:   assets/opencc/vendor/symfony/console/Exception/InvalidOptionException.php
	新文件:   assets/opencc/vendor/symfony/console/Exception/LogicException.php
	新文件:   assets/opencc/vendor/symfony/console/Exception/MissingInputException.php
	新文件:   assets/opencc/vendor/symfony/console/Exception/NamespaceNotFoundException.php
	新文件:   assets/opencc/vendor/symfony/console/Exception/RunCommandFailedException.php
	新文件:   assets/opencc/vendor/symfony/console/Exception/RuntimeException.php
	新文件:   assets/opencc/vendor/symfony/console/Formatter/NullOutputFormatter.php
	新文件:   assets/opencc/vendor/symfony/console/Formatter/NullOutputFormatterStyle.php
	新文件:   assets/opencc/vendor/symfony/console/Formatter/OutputFormatter.php
	新文件:   assets/opencc/vendor/symfony/console/Formatter/OutputFormatterInterface.php
	新文件:   assets/opencc/vendor/symfony/console/Formatter/OutputFormatterStyle.php
	新文件:   assets/opencc/vendor/symfony/console/Formatter/OutputFormatterStyleInterface.php
	新文件:   assets/opencc/vendor/symfony/console/Formatter/OutputFormatterStyleStack.php
	新文件:   assets/opencc/vendor/symfony/console/Formatter/WrappableOutputFormatterInterface.php
	新文件:   assets/opencc/vendor/symfony/console/Helper/DebugFormatterHelper.php
	新文件:   assets/opencc/vendor/symfony/console/Helper/DescriptorHelper.php
	新文件:   assets/opencc/vendor/symfony/console/Helper/Dumper.php
	新文件:   assets/opencc/vendor/symfony/console/Helper/FormatterHelper.php
	新文件:   assets/opencc/vendor/symfony/console/Helper/Helper.php
	新文件:   assets/opencc/vendor/symfony/console/Helper/HelperInterface.php
	新文件:   assets/opencc/vendor/symfony/console/Helper/HelperSet.php
	新文件:   assets/opencc/vendor/symfony/console/Helper/InputAwareHelper.php
	新文件:   assets/opencc/vendor/symfony/console/Helper/OutputWrapper.php
	新文件:   assets/opencc/vendor/symfony/console/Helper/ProcessHelper.php
	新文件:   assets/opencc/vendor/symfony/console/Helper/ProgressBar.php
	新文件:   assets/opencc/vendor/symfony/console/Helper/ProgressIndicator.php
	新文件:   assets/opencc/vendor/symfony/console/Helper/QuestionHelper.php
	新文件:   assets/opencc/vendor/symfony/console/Helper/SymfonyQuestionHelper.php
	新文件:   assets/opencc/vendor/symfony/console/Helper/Table.php
	新文件:   assets/opencc/vendor/symfony/console/Helper/TableCell.php
	新文件:   assets/opencc/vendor/symfony/console/Helper/TableCellStyle.php
	新文件:   assets/opencc/vendor/symfony/console/Helper/TableRows.php
	新文件:   assets/opencc/vendor/symfony/console/Helper/TableSeparator.php
	新文件:   assets/opencc/vendor/symfony/console/Helper/TableStyle.php
	新文件:   assets/opencc/vendor/symfony/console/Input/ArgvInput.php
	新文件:   assets/opencc/vendor/symfony/console/Input/ArrayInput.php
	新文件:   assets/opencc/vendor/symfony/console/Input/Input.php
	新文件:   assets/opencc/vendor/symfony/console/Input/InputArgument.php
	新文件:   assets/opencc/vendor/symfony/console/Input/InputAwareInterface.php
	新文件:   assets/opencc/vendor/symfony/console/Input/InputDefinition.php
	新文件:   assets/opencc/vendor/symfony/console/Input/InputInterface.php
	新文件:   assets/opencc/vendor/symfony/console/Input/InputOption.php
	新文件:   assets/opencc/vendor/symfony/console/Input/StreamableInputInterface.php
	新文件:   assets/opencc/vendor/symfony/console/Input/StringInput.php
	新文件:   assets/opencc/vendor/symfony/console/LICENSE
	新文件:   assets/opencc/vendor/symfony/console/Logger/ConsoleLogger.php
	新文件:   assets/opencc/vendor/symfony/console/Messenger/RunCommandContext.php
	新文件:   assets/opencc/vendor/symfony/console/Messenger/RunCommandMessage.php
	新文件:   assets/opencc/vendor/symfony/console/Messenger/RunCommandMessageHandler.php
	新文件:   assets/opencc/vendor/symfony/console/Output/AnsiColorMode.php
	新文件:   assets/opencc/vendor/symfony/console/Output/BufferedOutput.php
	新文件:   assets/opencc/vendor/symfony/console/Output/ConsoleOutput.php
	新文件:   assets/opencc/vendor/symfony/console/Output/ConsoleOutputInterface.php
	新文件:   assets/opencc/vendor/symfony/console/Output/ConsoleSectionOutput.php
	新文件:   assets/opencc/vendor/symfony/console/Output/NullOutput.php
	新文件:   assets/opencc/vendor/symfony/console/Output/Output.php
	新文件:   assets/opencc/vendor/symfony/console/Output/OutputInterface.php
	新文件:   assets/opencc/vendor/symfony/console/Output/StreamOutput.php
	新文件:   assets/opencc/vendor/symfony/console/Output/TrimmedBufferOutput.php
	新文件:   assets/opencc/vendor/symfony/console/Question/ChoiceQuestion.php
	新文件:   assets/opencc/vendor/symfony/console/Question/ConfirmationQuestion.php
	新文件:   assets/opencc/vendor/symfony/console/Question/Question.php
	新文件:   assets/opencc/vendor/symfony/console/README.md
	新文件:   assets/opencc/vendor/symfony/console/Resources/bin/hiddeninput.exe
	新文件:   assets/opencc/vendor/symfony/console/Resources/completion.bash
	新文件:   assets/opencc/vendor/symfony/console/Resources/completion.fish
	新文件:   assets/opencc/vendor/symfony/console/Resources/completion.zsh
	新文件:   assets/opencc/vendor/symfony/console/SignalRegistry/SignalMap.php
	新文件:   assets/opencc/vendor/symfony/console/SignalRegistry/SignalRegistry.php
	新文件:   assets/opencc/vendor/symfony/console/SingleCommandApplication.php
	新文件:   assets/opencc/vendor/symfony/console/Style/OutputStyle.php
	新文件:   assets/opencc/vendor/symfony/console/Style/StyleInterface.php
	新文件:   assets/opencc/vendor/symfony/console/Style/SymfonyStyle.php
	新文件:   assets/opencc/vendor/symfony/console/Terminal.php
	新文件:   assets/opencc/vendor/symfony/console/Tester/ApplicationTester.php
	新文件:   assets/opencc/vendor/symfony/console/Tester/CommandCompletionTester.php
	新文件:   assets/opencc/vendor/symfony/console/Tester/CommandTester.php
	新文件:   assets/opencc/vendor/symfony/console/Tester/Constraint/CommandIsSuccessful.php
	新文件:   assets/opencc/vendor/symfony/console/Tester/TesterTrait.php
	新文件:   assets/opencc/vendor/symfony/console/composer.json
	新文件:   assets/opencc/vendor/symfony/deprecation-contracts/CHANGELOG.md
	新文件:   assets/opencc/vendor/symfony/deprecation-contracts/LICENSE
	新文件:   assets/opencc/vendor/symfony/deprecation-contracts/README.md
	新文件:   assets/opencc/vendor/symfony/deprecation-contracts/composer.json
	新文件:   assets/opencc/vendor/symfony/deprecation-contracts/function.php
	新文件:   assets/opencc/vendor/symfony/polyfill-ctype/Ctype.php
	新文件:   assets/opencc/vendor/symfony/polyfill-ctype/LICENSE
	新文件:   assets/opencc/vendor/symfony/polyfill-ctype/README.md
	新文件:   assets/opencc/vendor/symfony/polyfill-ctype/bootstrap.php
	新文件:   assets/opencc/vendor/symfony/polyfill-ctype/bootstrap80.php
	新文件:   assets/opencc/vendor/symfony/polyfill-ctype/composer.json
	新文件:   assets/opencc/vendor/symfony/polyfill-intl-grapheme/Grapheme.php
	新文件:   assets/opencc/vendor/symfony/polyfill-intl-grapheme/LICENSE
	新文件:   assets/opencc/vendor/symfony/polyfill-intl-grapheme/README.md
	新文件:   assets/opencc/vendor/symfony/polyfill-intl-grapheme/bootstrap.php
	新文件:   assets/opencc/vendor/symfony/polyfill-intl-grapheme/bootstrap80.php
	新文件:   assets/opencc/vendor/symfony/polyfill-intl-grapheme/composer.json
	新文件:   assets/opencc/vendor/symfony/polyfill-intl-normalizer/LICENSE
	新文件:   assets/opencc/vendor/symfony/polyfill-intl-normalizer/Normalizer.php
	新文件:   assets/opencc/vendor/symfony/polyfill-intl-normalizer/README.md
	新文件:   assets/opencc/vendor/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php
	新文件:   assets/opencc/vendor/symfony/polyfill-intl-normalizer/Resources/unidata/canonicalComposition.php
	新文件:   assets/opencc/vendor/symfony/polyfill-intl-normalizer/Resources/unidata/canonicalDecomposition.php
	新文件:   assets/opencc/vendor/symfony/polyfill-intl-normalizer/Resources/unidata/combiningClass.php
	新文件:   assets/opencc/vendor/symfony/polyfill-intl-normalizer/Resources/unidata/compatibilityDecomposition.php
	新文件:   assets/opencc/vendor/symfony/polyfill-intl-normalizer/bootstrap.php
	新文件:   assets/opencc/vendor/symfony/polyfill-intl-normalizer/bootstrap80.php
	新文件:   assets/opencc/vendor/symfony/polyfill-intl-normalizer/composer.json
	新文件:   assets/opencc/vendor/symfony/polyfill-mbstring/LICENSE
	新文件:   assets/opencc/vendor/symfony/polyfill-mbstring/Mbstring.php
	新文件:   assets/opencc/vendor/symfony/polyfill-mbstring/README.md
	新文件:   assets/opencc/vendor/symfony/polyfill-mbstring/Resources/unidata/caseFolding.php
	新文件:   assets/opencc/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php
	新文件:   assets/opencc/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php
	新文件:   assets/opencc/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php
	新文件:   assets/opencc/vendor/symfony/polyfill-mbstring/bootstrap.php
	新文件:   assets/opencc/vendor/symfony/polyfill-mbstring/bootstrap80.php
	新文件:   assets/opencc/vendor/symfony/polyfill-mbstring/composer.json
	新文件:   assets/opencc/vendor/symfony/process/CHANGELOG.md
	新文件:   assets/opencc/vendor/symfony/process/Exception/ExceptionInterface.php
	新文件:   assets/opencc/vendor/symfony/process/Exception/InvalidArgumentException.php
	新文件:   assets/opencc/vendor/symfony/process/Exception/LogicException.php
	新文件:   assets/opencc/vendor/symfony/process/Exception/ProcessFailedException.php
	新文件:   assets/opencc/vendor/symfony/process/Exception/ProcessSignaledException.php
	新文件:   assets/opencc/vendor/symfony/process/Exception/ProcessTimedOutException.php
	新文件:   assets/opencc/vendor/symfony/process/Exception/RunProcessFailedException.php
	新文件:   assets/opencc/vendor/symfony/process/Exception/RuntimeException.php
	新文件:   assets/opencc/vendor/symfony/process/ExecutableFinder.php
	新文件:   assets/opencc/vendor/symfony/process/InputStream.php
	新文件:   assets/opencc/vendor/symfony/process/LICENSE
	新文件:   assets/opencc/vendor/symfony/process/Messenger/RunProcessContext.php
	新文件:   assets/opencc/vendor/symfony/process/Messenger/RunProcessMessage.php
	新文件:   assets/opencc/vendor/symfony/process/Messenger/RunProcessMessageHandler.php
	新文件:   assets/opencc/vendor/symfony/process/PhpExecutableFinder.php
	新文件:   assets/opencc/vendor/symfony/process/PhpProcess.php
	新文件:   assets/opencc/vendor/symfony/process/PhpSubprocess.php
	新文件:   assets/opencc/vendor/symfony/process/Pipes/AbstractPipes.php
	新文件:   assets/opencc/vendor/symfony/process/Pipes/PipesInterface.php
	新文件:   assets/opencc/vendor/symfony/process/Pipes/UnixPipes.php
	新文件:   assets/opencc/vendor/symfony/process/Pipes/WindowsPipes.php
	新文件:   assets/opencc/vendor/symfony/process/Process.php
	新文件:   assets/opencc/vendor/symfony/process/ProcessUtils.php
	新文件:   assets/opencc/vendor/symfony/process/README.md
	新文件:   assets/opencc/vendor/symfony/process/composer.json
	新文件:   assets/opencc/vendor/symfony/service-contracts/Attribute/Required.php
	新文件:   assets/opencc/vendor/symfony/service-contracts/Attribute/SubscribedService.php
	新文件:   assets/opencc/vendor/symfony/service-contracts/CHANGELOG.md
	新文件:   assets/opencc/vendor/symfony/service-contracts/LICENSE
	新文件:   assets/opencc/vendor/symfony/service-contracts/README.md
	新文件:   assets/opencc/vendor/symfony/service-contracts/ResetInterface.php
	新文件:   assets/opencc/vendor/symfony/service-contracts/ServiceCollectionInterface.php
	新文件:   assets/opencc/vendor/symfony/service-contracts/ServiceLocatorTrait.php
	新文件:   assets/opencc/vendor/symfony/service-contracts/ServiceMethodsSubscriberTrait.php
	新文件:   assets/opencc/vendor/symfony/service-contracts/ServiceProviderInterface.php
	新文件:   assets/opencc/vendor/symfony/service-contracts/ServiceSubscriberInterface.php
	新文件:   assets/opencc/vendor/symfony/service-contracts/ServiceSubscriberTrait.php
	新文件:   assets/opencc/vendor/symfony/service-contracts/Test/ServiceLocatorTest.php
	新文件:   assets/opencc/vendor/symfony/service-contracts/Test/ServiceLocatorTestCase.php
	新文件:   assets/opencc/vendor/symfony/service-contracts/composer.json
	新文件:   assets/opencc/vendor/symfony/string/AbstractString.php
	新文件:   assets/opencc/vendor/symfony/string/AbstractUnicodeString.php
	新文件:   assets/opencc/vendor/symfony/string/ByteString.php
	新文件:   assets/opencc/vendor/symfony/string/CHANGELOG.md
	新文件:   assets/opencc/vendor/symfony/string/CodePointString.php
	新文件:   assets/opencc/vendor/symfony/string/Exception/ExceptionInterface.php
	新文件:   assets/opencc/vendor/symfony/string/Exception/InvalidArgumentException.php
	新文件:   assets/opencc/vendor/symfony/string/Exception/RuntimeException.php
	新文件:   assets/opencc/vendor/symfony/string/Inflector/EnglishInflector.php
	新文件:   assets/opencc/vendor/symfony/string/Inflector/FrenchInflector.php
	新文件:   assets/opencc/vendor/symfony/string/Inflector/InflectorInterface.php
	新文件:   assets/opencc/vendor/symfony/string/LICENSE
	新文件:   assets/opencc/vendor/symfony/string/LazyString.php
	新文件:   assets/opencc/vendor/symfony/string/README.md
	新文件:   assets/opencc/vendor/symfony/string/Resources/data/wcswidth_table_wide.php
	新文件:   assets/opencc/vendor/symfony/string/Resources/data/wcswidth_table_zero.php
	新文件:   assets/opencc/vendor/symfony/string/Resources/functions.php
	新文件:   assets/opencc/vendor/symfony/string/Slugger/AsciiSlugger.php
	新文件:   assets/opencc/vendor/symfony/string/Slugger/SluggerInterface.php
	新文件:   assets/opencc/vendor/symfony/string/UnicodeString.php
	新文件:   assets/opencc/vendor/symfony/string/composer.json
	新文件:   assets/phpliteadmin.php
	新文件:   config/Caddyfile
	新文件:   config/nginx.conf
	新文件:   cron.php
	新文件:   cron/requirements.txt
	新文件:   cron/update.py
	新文件:   index.php
	新文件:   manage.php
	新文件:   public.php
	新文件:   update.php
2025-08-02 19:59:41 +08:00

779 lines
35 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* @file public.php
* @brief 公共脚本
*
* 该脚本包含公共设置、公共函数。
*
* 作者: Tak
* GitHub: https://github.com/taksssss/EPG-Server
* 二次开发: mxdabc
* Github: https://github.com/mxdabc/epgphp
*/
require 'assets/opencc/vendor/autoload.php'; // 引入 Composer 自动加载器
use Overtrue\PHPOpenCC\OpenCC; // 使用 OpenCC 库
// 检查并解析配置文件和图标列表文件
@mkdir(__DIR__ . '/data', 0755, true);
$iconDir = __DIR__ . '/data/icon/'; @mkdir($iconDir, 0755, true);
$liveDir = __DIR__ . '/data/live/'; @mkdir($liveDir, 0755, true);
$liveFileDir = __DIR__ . '/data/live/file/'; @mkdir($liveFileDir, 0755, true);
file_exists($configPath = __DIR__ . '/data/config.json') || copy(__DIR__ . '/assets/defaultConfig.json', $configPath);
file_exists($iconListPath = __DIR__ . '/data/iconList.json') || file_put_contents($iconListPath, json_encode(new stdClass(), JSON_PRETTY_PRINT));
($iconList = json_decode(file_get_contents($iconListPath), true)) !== null || die("图标列表文件解析失败: " . json_last_error_msg());
$iconListDefault = json_decode(file_get_contents(__DIR__ . '/assets/defaultIconList.json'), true) or die("默认图标列表文件解析失败: " . json_last_error_msg());
$iconListMerged = array_merge($iconListDefault, $iconList); // 同一个键,以 iconList 的为准
$Config = json_decode(file_get_contents($configPath), true) or die("配置文件解析失败: " . json_last_error_msg());
// 获取 serverUrl
$protocol = ($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? (($_SERVER['HTTPS'] ?? '') === 'on' ? 'https' : 'http'));
$host = $_SERVER['HTTP_X_FORWARDED_HOST'] ?? $_SERVER['HTTP_HOST'] ?? '';
$uri = rtrim(strtok(dirname($_SERVER['HTTP_X_ORIGINAL_URI'] ?? @$_SERVER['REQUEST_URI']) ?? '', '?'), '/');
$serverUrl = $protocol . '://' . $host . $uri;
// 设置时区为亚洲/上海
date_default_timezone_set("Asia/Shanghai");
// 创建或打开数据库
try {
// 检测数据库类型
$is_sqlite = $Config['db_type'] === 'sqlite';
$dsn = $is_sqlite ? 'sqlite:' . __DIR__ . '/data/data.db'
: "mysql:host={$Config['mysql']['host']};dbname={$Config['mysql']['dbname']};charset=utf8mb4";
$db = new PDO($dsn, $Config['mysql']['username'] ?? null, $Config['mysql']['password'] ?? null);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
echo '数据库连接失败: ' . $e->getMessage();
exit();
}
/*
Redis 缓存密码,默认不启用,除非需要设置密码。
$Config = [
'redis_password' => 'your_redis_password', // 替换为你的 Redis 密码
];
*/
// 初始化数据库表
function initialDB() {
global $db;
global $is_sqlite;
$tables = [
"CREATE TABLE IF NOT EXISTS epg_data (
date " . ($is_sqlite ? 'TEXT' : 'VARCHAR(255)') . " NOT NULL,
channel " . ($is_sqlite ? 'TEXT' : 'VARCHAR(255)') . " NOT NULL,
epg_diyp TEXT,
PRIMARY KEY (date, channel)
)",
"CREATE TABLE IF NOT EXISTS gen_list (
id " . ($is_sqlite ? 'INTEGER PRIMARY KEY AUTOINCREMENT' : 'INT PRIMARY KEY AUTO_INCREMENT') . ",
channel " . ($is_sqlite ? 'TEXT' : 'VARCHAR(255)') . " NOT NULL
)",
"CREATE TABLE IF NOT EXISTS update_log (
id " . ($is_sqlite ? 'INTEGER PRIMARY KEY AUTOINCREMENT' : 'INT PRIMARY KEY AUTO_INCREMENT') . ",
timestamp " . ($is_sqlite ? 'DATETIME DEFAULT CURRENT_TIMESTAMP' : 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP') . ",
log_message TEXT NOT NULL
)",
"CREATE TABLE IF NOT EXISTS cron_log (
id " . ($is_sqlite ? 'INTEGER PRIMARY KEY AUTOINCREMENT' : 'INT PRIMARY KEY AUTO_INCREMENT') . ",
timestamp " . ($is_sqlite ? 'DATETIME DEFAULT CURRENT_TIMESTAMP' : 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP') . ",
log_message TEXT NOT NULL
)"
];
foreach ($tables as $table) {
$db->exec($table);
}
}
// 获取处理后的频道名:$t2s参数表示繁简转换默认false
function cleanChannelName($channel, $t2s = false) {
global $Config;
$channel_ori = $channel;
// 默认忽略 - 跟 空格
$channel_replacements = ['-', ' '];
$channel = str_replace($channel_replacements, '', $channel);
// 频道映射,优先级最高,支持正则表达式和多对一映射
foreach ($Config['channel_mappings'] as $replace => $search) {
if (strpos($search, 'regex:') === 0) {
$pattern = substr($search, 6);
if (preg_match($pattern, $channel_ori)) {
return strtoupper(preg_replace($pattern, $replace, $channel_ori));
}
} else {
// 普通映射,可能为多对一
$channels = array_map('trim', explode(',', $search));
foreach ($channels as $singleChannel) {
if (strcasecmp($channel, str_replace($channel_replacements, '', $singleChannel)) === 0) {
return strtoupper($replace);
}
}
}
}
// 默认不进行繁简转换
if ($t2s) {
$channel = t2s($channel);
}
return strtoupper($channel);
}
// 繁体转简体
function t2s($channel) {
return OpenCC::convert($channel, 'TRADITIONAL_TO_SIMPLIFIED');
}
// 台标模糊匹配
function iconUrlMatch($originalChannel, $getDefault = true) {
global $Config, $iconListMerged;
// 精确匹配
if (isset($iconListMerged[$originalChannel])) {
return $iconListMerged[$originalChannel];
}
$bestMatch = null;
$iconUrl = null;
// 正向模糊匹配(原始频道名包含在列表中的频道名中)
foreach ($iconListMerged as $channelName => $icon) {
if (stripos($channelName, $originalChannel) !== false) {
if ($bestMatch === null || strlen($channelName) < strlen($bestMatch)) {
$bestMatch = $channelName;
$iconUrl = $icon;
}
}
}
// 反向模糊匹配(列表中的频道名包含在原始频道名中)
if (!$iconUrl) {
foreach ($iconListMerged as $channelName => $icon) {
if (stripos($originalChannel, $channelName) !== false) {
if ($bestMatch === null || strlen($channelName) > strlen($bestMatch)) {
$bestMatch = $channelName;
$iconUrl = $icon;
}
}
}
}
// 如果没有找到匹配的图标,使用默认图标(如果配置中存在)
return $iconUrl ?: ($getDefault && !empty($Config['default_icon']) ? $Config['default_icon'] : null);
}
// 下载文件
function downloadData($url, $userAgent = '', $timeout = 30, $connectTimeout = 10, $retry = 3) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_SSL_VERIFYPEER => 0,
CURLOPT_SSL_VERIFYHOST => 0,
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_FOLLOWLOCATION => 1,
CURLOPT_TIMEOUT => $timeout,
CURLOPT_CONNECTTIMEOUT => $connectTimeout,
CURLOPT_HTTPHEADER => [
'User-Agent: ' . $userAgent ?:
'CrestekkPf/1.2.0 (compatible; EPGCrawl/1.4.0; EPGVer/4.0; +https://www.mxdyeah.top/pages/spider/)',
'Accept: */*',
'Connection: keep-alive'
]
]);
while ($retry--) {
$data = curl_exec($ch);
if (!curl_errno($ch)) break;
}
curl_close($ch);
return $data ?: false;
}
// 日志记录函数
function logMessage(&$log_messages, $message) {
$log_messages[] = date("[y-m-d H:i:s]") . " " . $message;
echo date("[y-m-d H:i:s]") . " " . $message . "<br>";
}
// 下载 JSON 数据并存入数据库
function downloadJSONData($data_source, $data_str, $db, &$log_messages, $replaceFlag = true) {
$db->beginTransaction();
try {
processJsonData($data_source, $data_str, $db, $log_messages, $replaceFlag);
$db->commit();
} catch (Exception $e) {
$db->rollBack();
logMessage($log_messages, "{$data_source}" . $e->getMessage());
}
echo "<br>";
}
// 处理 JSON 数据并存入数据库
function processJsonData($data_source, $data_str, $db, &$log_messages, $replaceFlag) {
$processFunction = ($data_source === 'tvmao') ? 'processTvmaoJsonData' :
($data_source === 'cntv' ? 'processCntvJsonData' : null);
if ($processFunction) {
$allChannelProgrammes = $processFunction($data_str);
foreach ($allChannelProgrammes as $channelId => $channelProgrammes) {
$processCount = $channelProgrammes['process_count'];
if ($processCount) {
insertDataToDatabase([$channelId => $channelProgrammes], $db, $data_source, $replaceFlag);
}
logMessage($log_messages, "{$data_source}{$channelProgrammes['channel_name']} " .
($processCount ? "更新成功,共 {$processCount}" : "下载失败!!!"));
}
}
}
// 处理 tvmao 数据
function processTvmaoJsonData($data_str) {
$tvmaostr = str_ireplace('tvmao,', '', $data_str);
$channelProgrammes = [];
foreach (explode(',', $tvmaostr) as $tvmao_info) {
list($channelName, $channelId) = array_map('trim', explode(':', trim($tvmao_info)) + [null, $tvmao_info]);
$channelProgrammes[$channelId]['channel_name'] = cleanChannelName($channelName);
$json_url = "https://sp0.baidu.com/8aQDcjqpAAV3otqbppnN2DJv/api.php?query={$channelId}&resource_id=12520&format=json"; //? 最好不要用
$json_data = downloadData($json_url);
$json_data = mb_convert_encoding($json_data, 'UTF-8', 'GBK');
$data = json_decode($json_data, true);
if (empty($data['data'])) {
$channelProgrammes[$channelId]['process_count'] = 0;
continue;
}
$data = $data['data'][0]['data'];
$skipTime = null;
foreach ($data as $epg) {
if ($time_str = $epg['times'] ?? '') {
$starttime = DateTime::createFromFormat('Y/m/d H:i', $time_str);
$date = $starttime->format('Y-m-d');
// 如果第一条数据早于今天 02:00则认为今天的数据是齐全的
if (is_null($skipTime)) {
$skipTime = $starttime < new DateTime("today 02:00") ?
new DateTime("today 00:00") : new DateTime("tomorrow 00:00");
}
if ($starttime < $skipTime) continue;
$channelProgrammes[$channelId]['diyp_data'][$date][] = [
'start' => $starttime->format('H:i'),
'end' => '', // 初始为空
'title' => trim($epg['title']),
'desc' => ''
];
}
}
// 填充 'end' 字段
foreach ($channelProgrammes[$channelId]['diyp_data'] as $date => &$programmes) {
foreach ($programmes as $i => &$programme) {
$nextStart = $programmes[$i + 1]['start'] ?? '00:00'; // 下一个节目开始时间或 00:00
$programme['end'] = $nextStart; // 填充下一个节目的 'start'
if ($nextStart === '00:00') {
// 尝试获取第二天数据并补充
$nextDate = (new DateTime($date))->modify('+1 day')->format('Y-m-d');
$nextDayProgrammes = $channelProgrammes[$channelId]['diyp_data'][$nextDate] ?? [];
if (!empty($nextDayProgrammes) && $nextDayProgrammes[0]['start'] !== '00:00') {
array_unshift($channelProgrammes[$channelId]['diyp_data'][$nextDate], [
'start' => '00:00',
'end' => '',
'title' => $programme['title'],
'desc' => ''
]);
}
}
}
}
$channelProgrammes[$channelId]['process_count'] = count($data);
}
return $channelProgrammes;
}
// 处理 cntv 数据
function processCntvJsonData($data_str) {
$date_range = 1;
if (preg_match('/^cntv:(\d+),\s*(.*)$/i', $data_str, $matches)) {
$date_range = $matches[1]; // 提取日期范围
$cntvstr = $matches[2]; // 提取频道字符串
} else {
$cntvstr = str_ireplace('cntv,', '', $data_str); // 没有日期范围时去除 'cntv,'
}
$need_dates = array_map(function($i) { return (new DateTime())->modify("+$i day")->format('Ymd'); }, range(0, $date_range - 1));
$channelProgrammes = [];
foreach (explode(',', $cntvstr) as $cntv_info) {
list($channelName, $channelId) = array_map('trim', explode(':', trim($cntv_info)) + [null, $cntv_info]);
$channelId = strtolower($channelId);
$channelProgrammes[$channelId]['channel_name'] = cleanChannelName($channelName);
$processCount = 0;
foreach ($need_dates as $need_date) {
$json_url = "https://api.cntv.cn/epg/getEpgInfoByChannelNew?c={$channelId}&serviceId=tvcctv&d={$need_date}";
$json_data = downloadData($json_url);
$data = json_decode($json_data, true);
if (!isset($data['data'][$channelId]['list'])) {
continue;
}
$data = $data['data'][$channelId]['list'];
foreach ($data as $epg) {
$starttime = (new DateTime())->setTimestamp($epg['startTime']);
$endtime = (new DateTime())->setTimestamp($epg['endTime']);
$date = $starttime->format('Y-m-d');
$channelProgrammes[$channelId]['diyp_data'][$date][] = [
'start' => $starttime->format('H:i'),
'end' => $endtime->format('H:i'),
'title' => trim($epg['title']),
'desc' => ''
];
}
$processCount += count($data);
}
$channelProgrammes[$channelId]['process_count'] = $processCount;
}
return $channelProgrammes;
}
// 插入数据到数据库
function insertDataToDatabase($channelsData, $db, $sourceUrl, $replaceFlag = true) {
global $processedRecords;
global $Config;
foreach ($channelsData as $channelId => $channelData) {
$channelName = $channelData['channel_name'];
foreach ($channelData['diyp_data'] as $date => $diypProgrammes) {
// 检查是否全天只有一个节目
if (count($title = array_unique(array_column($diypProgrammes, 'title'))) === 1
&& preg_match('/节目|節目/u', $title[0])) {
continue; // 跳过后续处理
}
// 生成 epg_diyp 数据内容
$diypContent = json_encode([
'channel_name' => $channelName,
'date' => $date,
'url' => 'https://github.com/mxdabc/epgphp',
'source' => $sourceUrl,
'epg_data' => $diypProgrammes
], JSON_UNESCAPED_UNICODE);
// 当天及未来数据覆盖,其他日期数据忽略
$action = $date >= date('Y-m-d') && $replaceFlag ? 'REPLACE' : 'IGNORE';
// 根据数据库类型选择 SQL 语句
if ($Config['db_type'] === 'sqlite') {
$sql = "INSERT OR $action INTO epg_data (date, channel, epg_diyp) VALUES (:date, :channel, :epg_diyp)";
} else {
$sql = ($action === 'REPLACE')
? "REPLACE INTO epg_data (date, channel, epg_diyp) VALUES (:date, :channel, :epg_diyp)"
: "INSERT IGNORE INTO epg_data (date, channel, epg_diyp) VALUES (:date, :channel, :epg_diyp)";
}
// 准备并执行 SQL 语句
$stmt = $db->prepare($sql);
$stmt->bindValue(':date', $date, PDO::PARAM_STR);
$stmt->bindValue(':channel', $channelName, PDO::PARAM_STR);
$stmt->bindValue(':epg_diyp', $diypContent, PDO::PARAM_STR);
$stmt->execute();
if ($stmt->rowCount() > 0) {
$recordKey = $channelName . '-' . $date;
$processedRecords[$recordKey] = true;
}
}
}
}
// 读取 modifications.csv 文件,获取已存在的数据
function getExistingData() {
global $liveDir;
$existingData = [];
$modificationsFilePath = $liveDir . 'modifications.csv';
if (file_exists($modificationsFilePath)) {
$modificationsFile = fopen($modificationsFilePath, 'r');
$header = fgetcsv($modificationsFile); // 读取表头
while ($row = fgetcsv($modificationsFile)) {
if (empty(array_filter($row))) continue; // 跳过空行
$rowData = array_combine($header, $row);
$existingData[$rowData['tag']] = $rowData; // 使用 tag 作为映射的键
}
fclose($modificationsFile);
}
return $existingData;
}
// 解析 txt、m3u 直播源,并生成直播列表(包含分组、地址等信息)
function doParseSourceInfo($urlLine = null) {
// 获取当前的最大执行时间,临时设置超时时间为 20 分钟
$original_time_limit = ini_get('max_execution_time');
set_time_limit(20*60);
global $liveDir, $liveFileDir, $Config;
$liveChannelNameProcess = $Config['live_channel_name_process'] ?? false; // 标记是否处理频道名
// 频道数据模糊匹配函数
function dbChannelNameMatch($channelName) {
global $db;
$concat = $db->getAttribute(PDO::ATTR_DRIVER_NAME) === 'mysql' ? "CONCAT('%', channel, '%')" : "'%' || channel || '%'";
$stmt = $db->prepare("
SELECT channel FROM epg_data WHERE (channel = :channel OR channel LIKE :like_channel OR :channel LIKE $concat)
ORDER BY CASE WHEN channel = :channel THEN 1 WHEN channel LIKE :like_channel THEN 2 ELSE 3 END, LENGTH(channel) DESC
LIMIT 1
");
$stmt->execute([':channel' => $channelName, ':like_channel' => $channelName . '%']);
return $stmt->fetchColumn();
}
// 获取 modifications.csv 数据
$existingData = getExistingData();
// 读取 source.txt 内容,处理每行 URL
$errorLog = '';
$sourceContent = file_get_contents($liveDir . 'source.txt');
$lines = $urlLine ? [$urlLine] : array_filter(array_map('ltrim', explode("\n", $sourceContent)));
$allChannelData = [];
foreach ($lines as $line) {
if (empty($line) || $line[0] === '#') continue;
// 解析 URL 和分组前缀
list($url, $groupPrefix, $userAgent) = explode('#', $line) + [1 => '', 2 => ''];
$url = trim($url);
$groupPrefix = ltrim($groupPrefix);
$userAgent = trim($userAgent);
// 获取 URL 内容
$urlContent = (stripos($url, '/data/live/file/') === 0)
? @file_get_contents(__DIR__ . $url)
: downloadData($url, $userAgent, 5);
$fileName = md5(urlencode($url)); // 用 MD5 对 URL 进行命名
$localFilePath = $liveFileDir . '/' . $fileName . '.m3u';
if (!$urlContent || stripos($urlContent, 'not found') !== false) {
$urlContent = file_exists($localFilePath) ? file_get_contents($localFilePath) : '';
if (!$urlContent) { $errorLog .= "$url 解析失败<br>"; continue; }
else { $errorLog .= "$url 使用本地缓存<br>"; }
}
$encoding = mb_detect_encoding($urlContent, ['UTF-8', 'GBK', 'CP936'], true);
if ($encoding === 'GBK' || $encoding === 'CP936') {
$urlContent = mb_convert_encoding($urlContent, 'UTF-8', 'GBK');
}
$urlContentLines = explode("\n", $urlContent);
$urlChannelData = [];
// 处理 M3U 格式的直播源
if (strpos($urlContent, '#EXTM3U') !== false) {
foreach ($urlContentLines as $i => $urlContentLine) {
$urlContentLine = trim($urlContentLine);
// 跳过空行和 M3U 头部
if (empty($urlContentLine) || strpos($urlContentLine, '#EXTM3U') === 0) continue;
if (strpos($urlContentLine, '#EXTINF') === 0 && isset($urlContentLines[$i + 1]) &&
strpos($urlContentLines[$i + 1], '#EXTINF') !== 0) {
// 处理 #EXTINF 行,提取频道信息
if (preg_match('/#EXTINF:-?\d+(.*),(.+)/', $urlContentLine, $matches)) {
$channelInfo = $matches[1];
$groupTitle = preg_match('/group-title="([^"]+)"/', $channelInfo, $match) ? trim($match[1]) : '';
$originalChannelName = trim($matches[2]);
$streamUrl = '';
$j = $i + 1;
while (!empty($urlContentLines[$j]) && $urlContentLines[$j][0] === '#') {
$streamUrl .= trim($urlContentLines[$j++]) . '<br>';
}
$streamUrl .= strtok(trim($urlContentLines[$j] ?? ''), '\\');
$tag = md5($url . $groupTitle . $originalChannelName . $streamUrl);
$rowData = [
'groupTitle' => $groupPrefix . $groupTitle,
'channelName' => $originalChannelName,
'chsChannelName' => '',
'streamUrl' => $streamUrl,
'iconUrl' => preg_match('/tvg-logo="([^"]+)"/', $channelInfo, $match) ? $match[1] : '',
'tvgId' => preg_match('/tvg-id="([^"]+)"/', $channelInfo, $match) ? $match[1] : '',
'tvgName' => preg_match('/tvg-name="([^"]+)"/', $channelInfo, $match) ? $match[1] : '',
'disable' => 0,
'modified' => 0,
'source' => $url,
'tag' => $tag,
];
$urlChannelData[] = $rowData;
}
}
}
} else {
// 处理 TXT 格式的直播源
$groupTitle = '';
foreach ($urlContentLines as $urlContentLine) {
$urlContentLine = trim($urlContentLine);
$parts = explode(',', $urlContentLine);
if (count($parts) >= 2) {
if ($parts[1] === '#genre#') {
$groupTitle = trim($parts[0]); // 更新 group-title
continue;
}
$originalChannelName = trim($parts[0]);
$streamUrl = trim($parts[1]);
$tag = md5($url . $groupTitle . $originalChannelName . $streamUrl);
$rowData = [
'groupTitle' => $groupPrefix . $groupTitle,
'channelName' => $originalChannelName,
'chsChannelName' => '',
'streamUrl' => $streamUrl,
'iconUrl' => '',
'tvgId' => '',
'tvgName' => '',
'disable' => 0,
'modified' => 0,
'source' => $url,
'tag' => $tag,
];
$urlChannelData[] = $rowData;
}
}
}
// 将所有 channelName 整合到一起,统一调用 t2s 进行繁简转换
$channelNames = array_column($urlChannelData, 'channelName'); // 提取所有 channelName
$chsChannelNames = explode("\n", t2s(implode("\n", $channelNames))); // 繁简转换
// 将转换后的信息写回 urlChannelData
foreach ($urlChannelData as $index => &$row) {
// 检查该行是否已经修改
if (isset($existingData[$row['tag']])) {
$row = $existingData[$row['tag']];
continue;
}
// 更新部分信息
$chsChannelName = $chsChannelNames[$index];
$cleanChannelName = cleanChannelName($chsChannelName);
$dbChannelName = dbChannelNameMatch($cleanChannelName);
$finalChannelName = $dbChannelName ?: $cleanChannelName;
$row['channelName'] = $liveChannelNameProcess ? $finalChannelName : $row['channelName'];
$row['chsChannelName'] = $chsChannelName;
$row['iconUrl'] = iconUrlMatch($finalChannelName) ?? $row['iconUrl'];
$row['tvgName'] = $dbChannelName ?? $row['tvgName'];
}
generateLiveFiles($urlChannelData, "file/{$fileName}"); // 单独直播源文件
$allChannelData = array_merge($allChannelData, $urlChannelData); // 写入 allChannelData
}
if (!$urlLine) {
generateLiveFiles($allChannelData, 'tv'); // 总直播源文件
}
// 恢复原始超时时间
set_time_limit($original_time_limit);
return $errorLog ?: true;
}
// 生成 M3U 和 TXT 文件
function generateLiveFiles($channelData, $fileName, $saveOnly = false) {
global $Config, $liveDir;
// 获取配置
$fuzzyMatchingEnable = $Config['live_fuzzy_match'] ?? 1;
$commentEnabled = $Config['live_url_comment'] ?? 0;
$txtCommentEnabled = $Config['live_url_comment'] === 1 || $Config['live_url_comment'] === 3 ?? 0;
$m3uCommentEnabled = $Config['live_url_comment'] === 2 || $Config['live_url_comment'] === 3 ?? 0;
// 读取 template.txt 文件内容
$templateFilePath = $liveDir . 'template.txt';
$templateExist = file_exists($templateFilePath) && !empty($templateContent = file_get_contents($templateFilePath));
$m3uContent = "#EXTM3U x-tvg-url=\"\"\n";
$groups = [];
$liveTvgIdEnable = $Config['live_tvg_id_enable'] ?? 1;
$liveTvgNameEnable = $Config['live_tvg_name_enable'] ?? 1;
$liveTvgLogoEnable = $Config['live_tvg_logo_enable'] ?? 1;
if ($fileName === 'tv' && ($Config['live_template_enable'] ?? 1) && $templateExist && !$saveOnly) {
// 处理有模板且开启的情况
$templateGroups = [];
// 解析 template.txt 内容
$currentGroup = '未分组';
foreach (explode("\n", $templateContent) as $line) {
$line = trim($line, " ,");
if (empty($line)) continue;
if (strpos($line, '#') === 0) {
$groupParts = array_map('trim', explode(',', substr($line, 1)));
$currentGroup = $groupParts[0]; // 提取分组名
$currentGroupSources = array_slice($groupParts, 1); // 提取分组源(多个值)
$templateGroups[$currentGroup]['source'] = $currentGroupSources; // 存储为数组
} else {
$channels = array_map('trim', explode(',', $line));
foreach ($channels as $channel) {
$templateGroups[$currentGroup]['channels'][] = $channel;
}
}
}
// 处理每个分组
$newChannelData = [];
foreach ($templateGroups as $templateGroupTitle => $groupInfo) {
// 如果没有指定频道,直接检查来源、分组标题是否匹配
if (empty($groupInfo['channels'])) {
foreach ($channelData as $row) {
list($groupTitle, $channelName, , $streamUrl, $iconUrl, $tvgId, $tvgName, $disable, , $source) = array_values($row);
if ((!empty($groupInfo['source']) && !in_array($source, $groupInfo['source'])) || ($templateGroupTitle !== 'default' &&
(empty($groupTitle) || stripos($groupTitle, $templateGroupTitle) === false && stripos($templateGroupTitle, $groupTitle) === false))) {
continue;
}
// 更新信息
$streamParts = explode("<br>", $streamUrl);
$streamUrl = array_pop($streamParts);
$extraInfo = $streamParts ? implode("\n", $streamParts) . "\n" : '';
$txtStreamUrl = $streamUrl . (($txtCommentEnabled && strpos($streamUrl, '$') === false) ? "\${$groupTitle}" : "");
$m3uStreamUrl = $streamUrl . (($m3uCommentEnabled && strpos($streamUrl, '$') === false) ? "\${$groupTitle}" : "");
$rowGroupTitle = $templateGroupTitle === 'default' ? $groupTitle : $templateGroupTitle;
$row['groupTitle'] = $rowGroupTitle;
$row['streamUrl'] = $streamUrl . (($commentEnabled && strpos($streamUrl, '$') === false) ? "\${$groupTitle}" : "");
$newChannelData[] = $row;
if ($disable) continue;
// 生成 M3U 内容
$extInfLine = "#EXTINF:-1" .
($tvgId && $liveTvgIdEnable ? " tvg-id=\"$tvgId\"" : "") .
($tvgName && $liveTvgNameEnable ? " tvg-name=\"$tvgName\"" : "") .
($iconUrl && $liveTvgLogoEnable ? " tvg-logo=\"$iconUrl\"" : "") .
" group-title=\"$rowGroupTitle\"," .
"$channelName";
$m3uContent .= $extInfLine . "\n" . $extraInfo . $m3uStreamUrl . "\n";
$groups[$rowGroupTitle][] = "$channelName,$txtStreamUrl";
}
} else {
// 获取繁简转换后的模板频道名称
$groupChannels = $groupInfo['channels'];
$cleanChsGroupChannelNames = explode("\n", t2s(implode("\n", array_map('cleanChannelName', $groupChannels))));
// 如果指定了频道,先遍历 $groupChannels保证顺序不变
foreach ($groupChannels as $index => $groupChannelName) {
$cleanChsGroupChannelName = $cleanChsGroupChannelNames[$index];
foreach ($channelData as $row) {
list($groupTitle, $channelName, $chsChannelName, $streamUrl, $iconUrl, $tvgId, $tvgName, $disable, , $source) = array_values($row);
// 检查来源匹配
if (!empty($groupInfo['source']) && !in_array($source, $groupInfo['source'])) {
continue;
}
// 检查频道名称是否匹配
$cleanChsChannelName = cleanChannelName($chsChannelName);
// CGTN 和 CCTV 不进行模糊匹配
if ($channelName === $groupChannelName ||
($fuzzyMatchingEnable && ($cleanChsChannelName === $cleanChsGroupChannelName ||
stripos($cleanChsGroupChannelName, 'CGTN') === false && stripos($cleanChsGroupChannelName, 'CCTV') === false && !empty($cleanChsChannelName) &&
(stripos($cleanChsChannelName, $cleanChsGroupChannelName) !== false || stripos($cleanChsGroupChannelName, $cleanChsChannelName) !== false)))) {
// 更新信息
$streamParts = explode("<br>", $streamUrl);
$streamUrl = array_pop($streamParts);
$extraInfo = $streamParts ? implode("\n", $streamParts) . "\n" : '';
$txtStreamUrl = $streamUrl . (($txtCommentEnabled && strpos($streamUrl, '$') === false) ? "\${$groupTitle}" : "");
$m3uStreamUrl = $streamUrl . (($m3uCommentEnabled && strpos($streamUrl, '$') === false) ? "\${$groupTitle}" : "");
$rowGroupTitle = $templateGroupTitle === 'default' ? $groupTitle : $templateGroupTitle;
$row['groupTitle'] = $rowGroupTitle;
$row['channelName'] = $groupChannelName; // 使用 $groupChannels 中的名称
$row['streamUrl'] = $streamUrl . (($commentEnabled && strpos($streamUrl, '$') === false) ? "\${$groupTitle}" : "");
$newChannelData[] = $row;
if ($disable) continue;
// 生成 M3U 内容
$extInfLine = "#EXTINF:-1" .
($tvgId && $liveTvgIdEnable ? " tvg-id=\"$tvgId\"" : "") .
($tvgName && $liveTvgNameEnable ? " tvg-name=\"$tvgName\"" : "") .
($iconUrl && $liveTvgLogoEnable ? " tvg-logo=\"$iconUrl\"" : "") .
" group-title=\"$rowGroupTitle\"," .
"$groupChannelName"; // 使用 $groupChannels 中的名称
$m3uContent .= $extInfLine . "\n" . $extraInfo . $m3uStreamUrl . "\n";
$groups[$rowGroupTitle][] = "$groupChannelName,$txtStreamUrl";
}
}
}
}
}
$channelData = $newChannelData;
} else {
// 处理没有模板及仅保存修改信息的情况
foreach ($channelData as $row) {
list($groupTitle, $channelName, , $streamUrl, $iconUrl, $tvgId, $tvgName, $disable) = array_values($row);
if ($disable) continue;
// 生成 M3U 内容
$extInfLine = "#EXTINF:-1" .
($tvgId && $liveTvgIdEnable ? " tvg-id=\"$tvgId\"" : "") .
($tvgName && $liveTvgNameEnable ? " tvg-name=\"$tvgName\"" : "") .
($iconUrl && $liveTvgLogoEnable ? " tvg-logo=\"$iconUrl\"" : "") .
($groupTitle ? " group-title=\"$groupTitle\"" : "") .
",$channelName";
$streamParts = explode("<br>", $streamUrl);
$streamUrl = array_pop($streamParts);
$extraInfo = $streamParts ? implode("\n", $streamParts) . "\n" : '';
$m3uContent .= $extInfLine . "\n" . $extraInfo . $streamUrl . "\n";
$groups[$groupTitle ?: "未分组"][] = "$channelName,$streamUrl";
}
}
// 写入 M3U 文件
file_put_contents("{$liveDir}{$fileName}.m3u", $m3uContent);
// 写入 TXT 文件
$txtContent = "";
foreach ($groups as $group => $channels) {
$txtContent .= "$group,#genre#\n" . implode("\n", $channels) . "\n\n";
}
file_put_contents("{$liveDir}{$fileName}.txt", trim($txtContent));
if ($fileName === 'tv') {
// 获取 modifications.csv 数据
$existingData = getExistingData();
// 打开 CSV 文件写入新数据
$channelsFilePath = $liveDir . 'channels.csv';
$channelsFile = fopen($channelsFilePath, 'w');
$modificationsFilePath = $liveDir . 'modifications.csv';
$modificationsFile = fopen($modificationsFilePath, 'w');
$title = ['groupTitle', 'channelName', 'chsChannelName', 'streamUrl', 'iconUrl', 'tvgId', 'tvgName', 'disable', 'modified', 'source', 'tag'];
fputcsv($channelsFile, $title); // 写入表头
fputcsv($modificationsFile, $title); // 写入表头
foreach ($channelData as $row) {
unset($row['resolution'], $row['speed']); // 删除 resolution 跟 speed 键
fputcsv($channelsFile, $row);
// 处理 existingData
if (isset($existingData[$row['tag']])) { // 如果 tag 已存在,移除
unset($existingData[$row['tag']]);
}
if ($row['modified'] == 1) { // 如果 modified 为 1保存至 existingData
$existingData[$row['tag']] = $row;
}
}
// 将 existingData 写入 modifications.csv
foreach ($existingData as $tag => $row) {
fputcsv($modificationsFile, $row);
}
fclose($channelsFile);
fclose($modificationsFile);
}
}
?>