要提交的变更: 修改: 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
376 lines
13 KiB
PHP
376 lines
13 KiB
PHP
<?php
|
||
/**
|
||
* @file index.php
|
||
* @brief 主页处理脚本
|
||
*
|
||
* 该脚本处理来自客户端的请求,根据查询参数获取指定日期和频道的节目信息,
|
||
* 并从 SQLite 数据库中提取或返回默认数据。
|
||
*
|
||
* 作者: Tak
|
||
* GitHub: https://github.com/taksssss/EPG-Server
|
||
* 二次开发: mxdabc
|
||
* Github: https://github.com/mxdabc/epgphp
|
||
*/
|
||
|
||
// 引入公共脚本
|
||
require_once 'public.php';
|
||
|
||
// 获取当前完整的 URL
|
||
$requestUrl = $_SERVER['REQUEST_URI'];
|
||
|
||
// 修正 URL 格式:如果存在多个 `?`,将后续的 `?` 替换为 `&`
|
||
if (substr_count($requestUrl, '?') > 1) {
|
||
$requestUrl = preg_replace('/&/', '?', preg_replace('/\?/', '&', $requestUrl), 1);
|
||
}
|
||
|
||
// 解析 URL 中的查询参数
|
||
parse_str(str_replace('+', '%2B', parse_url($requestUrl, PHP_URL_QUERY)), $query_params);
|
||
|
||
// 获取 URL 中的 token 参数并验证
|
||
$tokenRange = $Config['token_range'] ?? 1;
|
||
$live = $query_params['live'] ?? '';
|
||
if ($tokenRange !== 0) {
|
||
$allowedTokens = array_map('trim', explode(',', $Config['token'] ?? ''));
|
||
$token = $query_params['token'] ?? '';
|
||
if (!in_array($token, $allowedTokens) && (($tokenRange !== 2 && $live) ||
|
||
($tokenRange !== 1 && !$live))) {
|
||
http_response_code(403);
|
||
echo '访问被拒绝:无效的 Token。';
|
||
exit;
|
||
}
|
||
}
|
||
|
||
// 获取请求的 User-Agent 并验证
|
||
$userAgentRange = $Config['user_agent_range'] ?? 0;
|
||
if ($userAgentRange !== 0) {
|
||
$allowedUserAgents = array_map('trim', explode(',', $Config['user_agent'] ?? ''));
|
||
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
|
||
if (!in_array($userAgent, $allowedUserAgents) && (($userAgentRange !== 2 && $live) ||
|
||
($userAgentRange !== 1 && !$live))) {
|
||
http_response_code(403);
|
||
echo '访问被拒绝:无效的 User-Agent。';
|
||
exit;
|
||
}
|
||
}
|
||
|
||
// 禁止输出错误提示
|
||
error_reporting(0);
|
||
|
||
// 初始化响应头信息
|
||
$init = [
|
||
'status' => 200,
|
||
'headers' => [
|
||
'content-type' => 'application/json'
|
||
]
|
||
];
|
||
|
||
// 生成响应
|
||
function makeRes($body, $status = 200, $headers = []) {
|
||
$headers['Access-Control-Allow-Origin'] = '*';
|
||
http_response_code($status);
|
||
foreach ($headers as $key => $value) {
|
||
header("$key: $value");
|
||
}
|
||
echo $body;
|
||
}
|
||
|
||
// 获取当前日期
|
||
function getNowDate() {
|
||
return date('Y-m-d');
|
||
}
|
||
|
||
// 格式化时间
|
||
function getFormatTime($time) {
|
||
if (strlen($time) < 8) {
|
||
return ['date' => getNowDate(), 'time' => ''];
|
||
}
|
||
|
||
$date = substr($time, 0, 4) . '-' . substr($time, 4, 2) . '-' . substr($time, 6, 2);
|
||
$time = strlen($time) >= 12 ? substr($time, 8, 2) . ':' . substr($time, 10, 2) : '';
|
||
|
||
return ['date' => $date, 'time' => $time];
|
||
}
|
||
|
||
// 检查 Memcached 状态
|
||
$memcached_enabled = class_exists('Memcached') && ($memcached = new Memcached())->addServer('127.0.0.1', 11211);
|
||
// 奇怪了,FreeBSD 上我的 Memcached 监听不了 localhost,127.0.0.1就可以。
|
||
|
||
// 检查 Redis 状态
|
||
$redis_enabled = class_exists('Redis') && ($redis = new Redis())->connect('127.0.0.1', 6379);
|
||
if ($redis_enabled && isset($Config['redis_password'])) {
|
||
$redis->auth($Config['redis_password']);
|
||
}
|
||
|
||
// 从数据库读取 diyp、lovetv 数据,兼容未安装 memcached 的情况
|
||
function readEPGData($date, $oriChannelName, $cleanChannelName, $db, $type) {
|
||
// 默认缓存 24 小时,更新数据时清空
|
||
// 按需更改,如 12 小时就是 12 * 3600
|
||
$cache_time = 24 * 3600;
|
||
|
||
// 检查 Memcached 和 Redis 状态
|
||
global $memcached_enabled, $redis_enabled, $memcached, $redis;
|
||
$cache_key = base64_encode("{$date}_{$cleanChannelName}_{$type}");
|
||
|
||
if ($memcached_enabled) {
|
||
// 从 Memcached 缓存中读取数据
|
||
$cached_data = $memcached->get($cache_key);
|
||
if ($cached_data) {
|
||
return $cached_data;
|
||
}
|
||
}
|
||
|
||
if ($redis_enabled) {
|
||
// 从 Redis 缓存中读取数据
|
||
$cached_data = $redis->get($cache_key);
|
||
if ($cached_data) {
|
||
return $cached_data;
|
||
}
|
||
}
|
||
|
||
// 获取数据库类型(mysql 或 sqlite)
|
||
$concat = $db->getAttribute(PDO::ATTR_DRIVER_NAME) === 'mysql'
|
||
? "CONCAT('%', channel, '%')"
|
||
: "'%' || channel || '%'";
|
||
|
||
// 优先精准匹配,其次正向模糊匹配,最后反向模糊匹配
|
||
$stmt = $db->prepare("
|
||
SELECT epg_diyp
|
||
FROM epg_data
|
||
WHERE (
|
||
(channel = :channel
|
||
OR channel LIKE :like_channel
|
||
OR :channel LIKE $concat)
|
||
AND date = :date
|
||
)
|
||
ORDER BY
|
||
CASE
|
||
WHEN channel = :channel THEN 1
|
||
WHEN channel LIKE :like_channel THEN 2
|
||
ELSE 3
|
||
END,
|
||
CASE
|
||
WHEN channel = :channel THEN NULL
|
||
WHEN channel LIKE :like_channel THEN LENGTH(channel)
|
||
ELSE -LENGTH(channel)
|
||
END
|
||
LIMIT 1
|
||
");
|
||
$stmt->execute([
|
||
':date' => $date,
|
||
':channel' => $cleanChannelName,
|
||
':like_channel' => $cleanChannelName . '%'
|
||
]);
|
||
$row = $stmt->fetchColumn();
|
||
|
||
if (!$row) {
|
||
return false;
|
||
}
|
||
|
||
// 在解码和添加 icon 后再编码为 JSON
|
||
$rowArray = json_decode($row, true);
|
||
$iconUrl = iconUrlMatch($rowArray['channel_name']) ?? iconUrlMatch($cleanChannelName) ?? iconUrlMatch($oriChannelName);
|
||
$rowArray = array_merge(
|
||
array_slice($rowArray, 0, array_search('source', array_keys($rowArray)) + 1),
|
||
['icon' => $iconUrl],
|
||
array_slice($rowArray, array_search('source', array_keys($rowArray)) + 1)
|
||
);
|
||
$row = json_encode($rowArray, JSON_UNESCAPED_UNICODE);
|
||
|
||
if ($type === 'diyp') {
|
||
// 如果 Memcached 或 Redis 可用,将结果存储到缓存中
|
||
if ($memcached_enabled) {
|
||
$memcached->set($cache_key, $row, $cache_time);
|
||
}
|
||
if ($redis_enabled) {
|
||
$redis->set($cache_key, $row, $cache_time);
|
||
}
|
||
return $row;
|
||
}
|
||
|
||
if ($type === 'lovetv') {
|
||
$diyp_data = $rowArray;
|
||
$date = $diyp_data['date'];
|
||
$program = array_map(function($epg) use ($date) {
|
||
$start_time = strtotime($date . ' ' . $epg['start']);
|
||
$end_time = strtotime($date . ' ' . $epg['end']);
|
||
$duration = $end_time - $start_time;
|
||
return [
|
||
'st' => $start_time,
|
||
'et' => $end_time,
|
||
'eventType' => '',
|
||
'eventId' => '',
|
||
't' => $epg['title'],
|
||
'showTime' => gmdate('H:i', $duration),
|
||
'duration' => $duration
|
||
];
|
||
}, $diyp_data['epg_data']);
|
||
|
||
// 查找当前节目
|
||
$current_programme = $date === date('Y-m-d') ? findCurrentProgramme($program) : null;
|
||
|
||
// 生成 lovetv 数据
|
||
$lovetv_data = [
|
||
$oriChannelName => [
|
||
'isLive' => $current_programme ? $current_programme['t'] : '',
|
||
'liveSt' => $current_programme ? $current_programme['st'] : 0,
|
||
'channelName' => $diyp_data['channel_name'],
|
||
'lvUrl' => $diyp_data['url'],
|
||
'srcUrl' => $diyp_data['source'],
|
||
'icon' => $diyp_data['icon'],
|
||
'program' => $program
|
||
]
|
||
];
|
||
|
||
$response = json_encode($lovetv_data, JSON_UNESCAPED_UNICODE);
|
||
|
||
// 如果 Memcached 或 Redis 可用,将结果存储到缓存中
|
||
if ($memcached_enabled) {
|
||
$memcached->set($cache_key, $response, $cache_time);
|
||
}
|
||
if ($redis_enabled) {
|
||
$redis->set($cache_key, $response, $cache_time);
|
||
}
|
||
|
||
return $response;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
// 查找当前节目
|
||
function findCurrentProgramme($programmes) {
|
||
$now = time();
|
||
foreach ($programmes as $programme) {
|
||
if ($programme['st'] <= $now && $programme['et'] >= $now) {
|
||
return $programme;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
// 处理直播源请求
|
||
function liveFetchHandler($query_params) {
|
||
global $Config, $liveDir, $serverUrl, $liveFileDir;
|
||
|
||
header('Content-Type: text/plain');
|
||
|
||
// 计算文件路径
|
||
$isValidFile = false;
|
||
if (!empty($query_params['url'])) {
|
||
$url = $query_params['url'];
|
||
$filePath = sprintf('%s/%s.%s', $liveFileDir, md5(urlencode($url)), $query_params['live']);
|
||
if (($query_params['latest'] === '1' && doParseSourceInfo($url)) === true ||
|
||
file_exists($filePath) || doParseSourceInfo($url) === true) { // 判断是否需要获取最新文件
|
||
$isValidFile = true;
|
||
}
|
||
} else {
|
||
$filePath = $liveDir . ($query_params['live'] === 'txt' ? 'tv.txt' : ($query_params['live'] === 'm3u' ? 'tv.m3u' : ''));
|
||
$isValidFile = file_exists($filePath);
|
||
}
|
||
|
||
// 如果文件存在或成功解析了源数据
|
||
if ($isValidFile) {
|
||
$content = file_get_contents($filePath);
|
||
} else {
|
||
echo "文件不存在或无效的 live 类型";
|
||
exit;
|
||
}
|
||
|
||
// 处理 TVG URL 替换
|
||
$tvgUrl = $serverUrl . ($query_params['live'] === 'm3u' ? '/t.xml.gz' : '/');
|
||
if ($query_params['live'] === 'm3u') {
|
||
$content = preg_replace('/(#EXTM3U x-tvg-url=")(.*?)(")/', '$1' . $tvgUrl . '$3', $content, 1);
|
||
}
|
||
|
||
echo $content;
|
||
exit;
|
||
}
|
||
|
||
// 处理请求
|
||
function fetchHandler($query_params) {
|
||
global $init, $db, $Config;
|
||
|
||
// 处理直播源请求
|
||
if (isset($query_params['live'])) {
|
||
liveFetchHandler($query_params);
|
||
}
|
||
|
||
// 获取并清理频道名称,繁体转换成简体
|
||
$oriChannelName = $query_params['ch'] ?? $query_params['channel'] ?? '';
|
||
$cleanChannelName = cleanChannelName($oriChannelName, $t2s = true);
|
||
|
||
$date = isset($query_params['date']) ? getFormatTime(preg_replace('/\D+/', '', $query_params['date']))['date'] : getNowDate();
|
||
|
||
// 频道参数为空时,直接返回 t.xml 文件数据
|
||
if (empty($cleanChannelName)) {
|
||
if ($Config['gen_xml'] === 1) {
|
||
header('Content-Type: application/xml');
|
||
header('Content-Disposition: attachment; filename="t.xml"');
|
||
readfile('./t.xml');
|
||
} else {
|
||
// 输出消息并设置404状态码
|
||
echo "404 Not Found. <br>未生成 xmltv 文件";
|
||
http_response_code(404);
|
||
}
|
||
exit;
|
||
}
|
||
|
||
// 返回 diyp、lovetv 数据
|
||
if (isset($query_params['ch']) || isset($query_params['channel'])) {
|
||
$type = isset($query_params['ch']) ? 'diyp' : 'lovetv';
|
||
$response = readEPGData($date, $oriChannelName, $cleanChannelName, $db, $type);
|
||
if ($response) {
|
||
makeRes($response, $init['status'], $init['headers']);
|
||
exit;
|
||
}
|
||
|
||
// 无法获取到数据时返回默认数据
|
||
$ret_default = !isset($Config['ret_default']) || $Config['ret_default'];
|
||
$iconUrl = iconUrlMatch($cleanChannelName) ?? iconUrlMatch($oriChannelName);
|
||
if ($type === 'diyp') {
|
||
// 返回默认 diyp 数据
|
||
$default_diyp_program_info = [
|
||
'channel_name' => $cleanChannelName,
|
||
'date' => $date,
|
||
'url' => "https://github.com/mxdabc/epgphp",
|
||
'icon' => $iconUrl,
|
||
'epg_data' => !$ret_default ? '' : array_map(function($hour) {
|
||
return [
|
||
'start' => sprintf('%02d:00', $hour),
|
||
'end' => sprintf('%02d:00', ($hour + 1) % 24),
|
||
'title' => '此频道暂无预告或者您配置有误。',
|
||
'desc' => ''
|
||
];
|
||
}, range(0, 23, 1))
|
||
];
|
||
$response = json_encode($default_diyp_program_info, JSON_UNESCAPED_UNICODE);
|
||
} else {
|
||
// 返回默认 lovetv 数据
|
||
$default_lovetv_program_info = [
|
||
$cleanChannelName => [
|
||
'isLive' => '',
|
||
'liveSt' => 0,
|
||
'channelName' => $cleanChannelName,
|
||
'lvUrl' => 'https://github.com/mxdabc/epgphp',
|
||
'icon' => $iconUrl,
|
||
'program' => !$ret_default ? '' : array_map(function($hour) {
|
||
return [
|
||
'st' => strtotime(sprintf('%02d:00', $hour)),
|
||
'et' => strtotime(sprintf('%02d:00', ($hour + 1) % 24)),
|
||
't' => '此频道暂无预告或者您配置有误。',
|
||
'd' => ''
|
||
];
|
||
}, range(0, 23, 1))
|
||
]
|
||
];
|
||
$response = json_encode($default_lovetv_program_info, JSON_UNESCAPED_UNICODE);
|
||
}
|
||
makeRes($response, $init['status'], $init['headers']);
|
||
}
|
||
}
|
||
|
||
// 执行请求处理
|
||
fetchHandler($query_params);
|
||
|
||
?>
|