ghost
1 year ago
114 changed files with 11523 additions and 10996 deletions
@ -0,0 +1,50 @@
@@ -0,0 +1,50 @@
|
||||
# In all environments, the following files are loaded if they exist, |
||||
# the latter taking precedence over the former: |
||||
# |
||||
# * .env contains default values for the environment variables needed by the app |
||||
# * .env.local uncommitted file with local overrides |
||||
# * .env.$APP_ENV committed environment-specific defaults |
||||
# * .env.$APP_ENV.local uncommitted environment-specific overrides |
||||
# |
||||
# Real environment variables win over .env files. |
||||
# |
||||
# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES. |
||||
# https://symfony.com/doc/current/configuration/secrets.html |
||||
# |
||||
# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2). |
||||
# https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration |
||||
|
||||
###> symfony/framework-bundle ### |
||||
APP_ENV=dev |
||||
APP_SECRET=EDITME |
||||
###< symfony/framework-bundle ### |
||||
|
||||
###> doctrine/doctrine-bundle ### |
||||
# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url |
||||
# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml |
||||
# |
||||
DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db" |
||||
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=8.0.32&charset=utf8mb4" |
||||
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4" |
||||
# DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=15&charset=utf8" |
||||
###< doctrine/doctrine-bundle ### |
||||
|
||||
###> symfony/messenger ### |
||||
# Choose one of the transports below |
||||
# MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages |
||||
# MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages |
||||
MESSENGER_TRANSPORT_DSN=doctrine://default?auto_setup=0 |
||||
###< symfony/messenger ### |
||||
|
||||
###> symfony/mailer ### |
||||
# MAILER_DSN=null://null |
||||
###< symfony/mailer ### |
||||
|
||||
###> symfony/crowdin-translation-provider ### |
||||
# CROWDIN_DSN=crowdin://PROJECT_ID:API_TOKEN@ORGANIZATION_DOMAIN.default |
||||
###< symfony/crowdin-translation-provider ### |
||||
|
||||
# YGGtracker |
||||
APP_VERSION='2.0.0' |
||||
|
||||
APP_NAME=YGGtracker |
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
# define your env variables for the test env here |
||||
KERNEL_CLASS='App\Kernel' |
||||
APP_SECRET='$ecretf0rt3st' |
||||
SYMFONY_DEPRECATIONS_HELPER=999999 |
||||
PANTHER_APP_ENV=panther |
||||
PANTHER_ERROR_SCREENSHOT_DIR=./var/error-screenshots |
@ -1,30 +1,22 @@
@@ -1,30 +1,22 @@
|
||||
/.vscode/ |
||||
|
||||
###> symfony/framework-bundle ### |
||||
/.env.local |
||||
/.env.local.php |
||||
/.env.*.local |
||||
/config/secrets/prod/prod.decrypt.private.php |
||||
/public/bundles/ |
||||
/var/ |
||||
/vendor/ |
||||
###< symfony/framework-bundle ### |
||||
|
||||
/database/* |
||||
!/database/yggtracker.mwb |
||||
###> phpunit/phpunit ### |
||||
/phpunit.xml |
||||
.phpunit.result.cache |
||||
###< phpunit/phpunit ### |
||||
|
||||
/src/public/api/*.json |
||||
###> symfony/phpunit-bridge ### |
||||
.phpunit.result.cache |
||||
/phpunit.xml |
||||
###< symfony/phpunit-bridge ### |
||||
|
||||
/src/config/* |
||||
!/src/config/bootstrap.php |
||||
!/src/config/website.json |
||||
!/src/config/locales.json |
||||
!/src/config/themes.json |
||||
!/src/config/sphinx.json |
||||
!/src/config/memcached.json |
||||
!/src/config/database.json |
||||
!/src/config/validator.json |
||||
!/src/config/moderators.json |
||||
!/src/config/nodes.json |
||||
!/src/config/trackers.json |
||||
!/src/config/peers.json |
||||
|
||||
/src/public/sitemap.xml |
||||
|
||||
/src/storage/log/*.log |
||||
|
||||
/composer.lock |
||||
|
||||
*test* |
||||
.vscode |
@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env php |
||||
<?php |
||||
|
||||
use App\Kernel; |
||||
use Symfony\Bundle\FrameworkBundle\Console\Application; |
||||
|
||||
if (!is_file(dirname(__DIR__).'/vendor/autoload_runtime.php')) { |
||||
throw new LogicException('Symfony Runtime is missing. Try running "composer require symfony/runtime".'); |
||||
} |
||||
|
||||
require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; |
||||
|
||||
return function (array $context) { |
||||
$kernel = new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']); |
||||
|
||||
return new Application($kernel); |
||||
}; |
@ -0,0 +1,19 @@
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env php |
||||
<?php |
||||
|
||||
if (!ini_get('date.timezone')) { |
||||
ini_set('date.timezone', 'UTC'); |
||||
} |
||||
|
||||
if (is_file(dirname(__DIR__).'/vendor/phpunit/phpunit/phpunit')) { |
||||
define('PHPUNIT_COMPOSER_INSTALL', dirname(__DIR__).'/vendor/autoload.php'); |
||||
require PHPUNIT_COMPOSER_INSTALL; |
||||
PHPUnit\TextUI\Command::main(); |
||||
} else { |
||||
if (!is_file(dirname(__DIR__).'/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php')) { |
||||
echo "Unable to find the `simple-phpunit.php` script in `vendor/symfony/phpunit-bridge/bin/`.\n"; |
||||
exit(1); |
||||
} |
||||
|
||||
require dirname(__DIR__).'/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php'; |
||||
} |
@ -1,24 +1,108 @@
@@ -1,24 +1,108 @@
|
||||
{ |
||||
"name": "yggverse/yggtracker", |
||||
"description": "Public BitTorrent tracker for Yggdrasil network", |
||||
"description": "BitTorrent tracker for Yggdrasil network", |
||||
"type": "project", |
||||
"license": "MIT", |
||||
"minimum-stability": "stable", |
||||
"prefer-stable": true, |
||||
"require": { |
||||
"php": "^8.1", |
||||
"yggverse/cache": ">=0.3.0", |
||||
"yggverse/parser": ">=0.4.0", |
||||
"php": ">=8.1", |
||||
"ext-ctype": "*", |
||||
"ext-iconv": "*", |
||||
"christeredvartsen/php-bittorrent": "^2.0", |
||||
"doctrine/annotations": "^2.0", |
||||
"doctrine/doctrine-bundle": "^2.10", |
||||
"doctrine/doctrine-migrations-bundle": "^3.2", |
||||
"doctrine/orm": "^2.16", |
||||
"jdenticon/jdenticon": "^1.0", |
||||
"christeredvartsen/php-bittorrent": "^2.0" |
||||
"phpdocumentor/reflection-docblock": "^5.3", |
||||
"phpstan/phpdoc-parser": "^1.24", |
||||
"symfony/asset": "6.3.*", |
||||
"symfony/console": "6.3.*", |
||||
"symfony/crowdin-translation-provider": "6.3.*", |
||||
"symfony/doctrine-messenger": "6.3.*", |
||||
"symfony/dotenv": "6.3.*", |
||||
"symfony/expression-language": "6.3.*", |
||||
"symfony/flex": "^2", |
||||
"symfony/form": "6.3.*", |
||||
"symfony/framework-bundle": "6.3.*", |
||||
"symfony/http-client": "6.3.*", |
||||
"symfony/intl": "6.3.*", |
||||
"symfony/mailer": "6.3.*", |
||||
"symfony/mime": "6.3.*", |
||||
"symfony/monolog-bundle": "^3.0", |
||||
"symfony/notifier": "6.3.*", |
||||
"symfony/process": "6.3.*", |
||||
"symfony/property-access": "6.3.*", |
||||
"symfony/property-info": "6.3.*", |
||||
"symfony/runtime": "6.3.*", |
||||
"symfony/security-bundle": "6.3.*", |
||||
"symfony/serializer": "6.3.*", |
||||
"symfony/string": "6.3.*", |
||||
"symfony/translation": "6.3.*", |
||||
"symfony/twig-bundle": "6.3.*", |
||||
"symfony/validator": "6.3.*", |
||||
"symfony/web-link": "6.3.*", |
||||
"symfony/yaml": "6.3.*", |
||||
"twig/extra-bundle": "^2.12|^3.0", |
||||
"twig/twig": "^2.12|^3.0" |
||||
}, |
||||
"config": { |
||||
"allow-plugins": { |
||||
"php-http/discovery": true, |
||||
"symfony/flex": true, |
||||
"symfony/runtime": true |
||||
}, |
||||
"sort-packages": true |
||||
}, |
||||
"license": "MIT", |
||||
"autoload": { |
||||
"psr-4": { |
||||
"Yggverse\\Yggtracker\\": "src/" |
||||
"App\\": "src/" |
||||
} |
||||
}, |
||||
"authors": [ |
||||
{ |
||||
"name": "YGGverse" |
||||
"autoload-dev": { |
||||
"psr-4": { |
||||
"App\\Tests\\": "tests/" |
||||
} |
||||
}, |
||||
"replace": { |
||||
"symfony/polyfill-ctype": "*", |
||||
"symfony/polyfill-iconv": "*", |
||||
"symfony/polyfill-php72": "*", |
||||
"symfony/polyfill-php73": "*", |
||||
"symfony/polyfill-php74": "*", |
||||
"symfony/polyfill-php80": "*", |
||||
"symfony/polyfill-php81": "*" |
||||
}, |
||||
"scripts": { |
||||
"auto-scripts": { |
||||
"cache:clear": "symfony-cmd", |
||||
"assets:install %PUBLIC_DIR%": "symfony-cmd" |
||||
}, |
||||
"post-install-cmd": [ |
||||
"@auto-scripts" |
||||
], |
||||
"minimum-stability": "alpha" |
||||
"post-update-cmd": [ |
||||
"@auto-scripts" |
||||
] |
||||
}, |
||||
"conflict": { |
||||
"symfony/symfony": "*" |
||||
}, |
||||
"extra": { |
||||
"symfony": { |
||||
"allow-contrib": false, |
||||
"require": "6.3.*" |
||||
} |
||||
}, |
||||
"require-dev": { |
||||
"phpunit/phpunit": "^9.5", |
||||
"symfony/browser-kit": "6.3.*", |
||||
"symfony/css-selector": "6.3.*", |
||||
"symfony/debug-bundle": "6.3.*", |
||||
"symfony/maker-bundle": "^1.0", |
||||
"symfony/phpunit-bridge": "^6.3", |
||||
"symfony/stopwatch": "6.3.*", |
||||
"symfony/web-profiler-bundle": "6.3.*" |
||||
} |
||||
} |
||||
|
@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
|
||||
<?php |
||||
|
||||
return [ |
||||
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true], |
||||
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], |
||||
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], |
||||
Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true], |
||||
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], |
||||
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], |
||||
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true], |
||||
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], |
||||
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], |
||||
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], |
||||
]; |
@ -0,0 +1,19 @@
@@ -0,0 +1,19 @@
|
||||
framework: |
||||
cache: |
||||
# Unique name of your app: used to compute stable namespaces for cache keys. |
||||
#prefix_seed: your_vendor_name/app_name |
||||
|
||||
# The "app" cache stores to the filesystem by default. |
||||
# The data in this cache should persist between deploys. |
||||
# Other options include: |
||||
|
||||
# Redis |
||||
#app: cache.adapter.redis |
||||
#default_redis_provider: redis://localhost |
||||
|
||||
# APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues) |
||||
#app: cache.adapter.apcu |
||||
|
||||
# Namespaced pools use the above "app" backend by default |
||||
#pools: |
||||
#my.dedicated.cache: null |
@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
|
||||
when@dev: |
||||
debug: |
||||
# Forwards VarDumper Data clones to a centralized server allowing to inspect dumps on CLI or in your browser. |
||||
# See the "server:dump" command to start a new server. |
||||
dump_destination: "tcp://%env(VAR_DUMPER_SERVER)%" |
@ -0,0 +1,48 @@
@@ -0,0 +1,48 @@
|
||||
doctrine: |
||||
dbal: |
||||
url: '%env(resolve:DATABASE_URL)%' |
||||
|
||||
# IMPORTANT: You MUST configure your server version, |
||||
# either here or in the DATABASE_URL env var (see .env file) |
||||
#server_version: '15' |
||||
|
||||
profiling_collect_backtrace: '%kernel.debug%' |
||||
orm: |
||||
auto_generate_proxy_classes: true |
||||
enable_lazy_ghost_objects: true |
||||
report_fields_where_declared: true |
||||
validate_xml_mapping: true |
||||
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware |
||||
auto_mapping: true |
||||
mappings: |
||||
App: |
||||
is_bundle: false |
||||
dir: '%kernel.project_dir%/src/Entity' |
||||
prefix: 'App\Entity' |
||||
alias: App |
||||
|
||||
when@test: |
||||
doctrine: |
||||
dbal: |
||||
# "TEST_TOKEN" is typically set by ParaTest |
||||
dbname_suffix: '_test%env(default::TEST_TOKEN)%' |
||||
|
||||
when@prod: |
||||
doctrine: |
||||
orm: |
||||
auto_generate_proxy_classes: false |
||||
proxy_dir: '%kernel.build_dir%/doctrine/orm/Proxies' |
||||
query_cache_driver: |
||||
type: pool |
||||
pool: doctrine.system_cache_pool |
||||
result_cache_driver: |
||||
type: pool |
||||
pool: doctrine.result_cache_pool |
||||
|
||||
framework: |
||||
cache: |
||||
pools: |
||||
doctrine.result_cache_pool: |
||||
adapter: cache.app |
||||
doctrine.system_cache_pool: |
||||
adapter: cache.system |
@ -0,0 +1,6 @@
@@ -0,0 +1,6 @@
|
||||
doctrine_migrations: |
||||
migrations_paths: |
||||
# namespace is arbitrary but should be different from App\Migrations |
||||
# as migrations classes should NOT be autoloaded |
||||
'DoctrineMigrations': '%kernel.project_dir%/migrations' |
||||
enable_profiler: false |
@ -0,0 +1,25 @@
@@ -0,0 +1,25 @@
|
||||
# see https://symfony.com/doc/current/reference/configuration/framework.html |
||||
framework: |
||||
secret: '%env(APP_SECRET)%' |
||||
#csrf_protection: true |
||||
http_method_override: false |
||||
handle_all_throwables: true |
||||
|
||||
# Enables session support. Note that the session will ONLY be started if you read or write from it. |
||||
# Remove or comment this section to explicitly disable session support. |
||||
session: |
||||
handler_id: null |
||||
cookie_secure: auto |
||||
cookie_samesite: lax |
||||
storage_factory_id: session.storage.factory.native |
||||
|
||||
#esi: true |
||||
#fragments: true |
||||
php_errors: |
||||
log: true |
||||
|
||||
when@test: |
||||
framework: |
||||
test: true |
||||
session: |
||||
storage_factory_id: session.storage.factory.mock_file |
@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
|
||||
framework: |
||||
mailer: |
||||
dsn: '%env(MAILER_DSN)%' |
@ -0,0 +1,24 @@
@@ -0,0 +1,24 @@
|
||||
framework: |
||||
messenger: |
||||
failure_transport: failed |
||||
|
||||
transports: |
||||
# https://symfony.com/doc/current/messenger.html#transport-configuration |
||||
async: |
||||
dsn: '%env(MESSENGER_TRANSPORT_DSN)%' |
||||
options: |
||||
use_notify: true |
||||
check_delayed_interval: 60000 |
||||
retry_strategy: |
||||
max_retries: 3 |
||||
multiplier: 2 |
||||
failed: 'doctrine://default?queue_name=failed' |
||||
# sync: 'sync://' |
||||
|
||||
routing: |
||||
Symfony\Component\Mailer\Messenger\SendEmailMessage: async |
||||
Symfony\Component\Notifier\Message\ChatMessage: async |
||||
Symfony\Component\Notifier\Message\SmsMessage: async |
||||
|
||||
# Route your messages to the transports |
||||
# 'App\Message\YourMessage': async |
@ -0,0 +1,61 @@
@@ -0,0 +1,61 @@
|
||||
monolog: |
||||
channels: |
||||
- deprecation # Deprecations are logged in the dedicated "deprecation" channel when it exists |
||||
|
||||
when@dev: |
||||
monolog: |
||||
handlers: |
||||
main: |
||||
type: stream |
||||
path: "%kernel.logs_dir%/%kernel.environment%.log" |
||||
level: debug |
||||
channels: ["!event"] |
||||
# uncomment to get logging in your browser |
||||
# you may have to allow bigger header sizes in your Web server configuration |
||||
#firephp: |
||||
# type: firephp |
||||
# level: info |
||||
#chromephp: |
||||
# type: chromephp |
||||
# level: info |
||||
console: |
||||
type: console |
||||
process_psr_3_messages: false |
||||
channels: ["!event", "!doctrine", "!console"] |
||||
|
||||
when@test: |
||||
monolog: |
||||
handlers: |
||||
main: |
||||
type: fingers_crossed |
||||
action_level: error |
||||
handler: nested |
||||
excluded_http_codes: [404, 405] |
||||
channels: ["!event"] |
||||
nested: |
||||
type: stream |
||||
path: "%kernel.logs_dir%/%kernel.environment%.log" |
||||
level: debug |
||||
|
||||
when@prod: |
||||
monolog: |
||||
handlers: |
||||
main: |
||||
type: fingers_crossed |
||||
action_level: error |
||||
handler: nested |
||||
excluded_http_codes: [404, 405] |
||||
buffer_size: 50 # How many messages should be saved? Prevent memory leaks |
||||
nested: |
||||
type: stream |
||||
path: php://stderr |
||||
level: debug |
||||
formatter: monolog.formatter.json |
||||
console: |
||||
type: console |
||||
process_psr_3_messages: false |
||||
channels: ["!event", "!doctrine"] |
||||
deprecation: |
||||
type: stream |
||||
channels: [deprecation] |
||||
path: php://stderr |
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
framework: |
||||
notifier: |
||||
chatter_transports: |
||||
texter_transports: |
||||
crowdin: '%env(CROWDIN_DSN)%' |
||||
channel_policy: |
||||
# use chat/slack, chat/telegram, sms/twilio or sms/nexmo |
||||
urgent: ['email'] |
||||
high: ['email'] |
||||
medium: ['email'] |
||||
low: ['email'] |
||||
admin_recipients: |
||||
- { email: admin@example.com } |
@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
|
||||
framework: |
||||
router: |
||||
utf8: true |
||||
|
||||
# Configure how to generate URLs in non-HTTP contexts, such as CLI commands. |
||||
# See https://symfony.com/doc/current/routing.html#generating-urls-in-commands |
||||
#default_uri: http://localhost |
||||
|
||||
when@prod: |
||||
framework: |
||||
router: |
||||
strict_requirements: null |
@ -0,0 +1,39 @@
@@ -0,0 +1,39 @@
|
||||
security: |
||||
# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords |
||||
password_hashers: |
||||
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' |
||||
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider |
||||
providers: |
||||
users_in_memory: { memory: null } |
||||
firewalls: |
||||
dev: |
||||
pattern: ^/(_(profiler|wdt)|css|images|js)/ |
||||
security: false |
||||
main: |
||||
lazy: true |
||||
provider: users_in_memory |
||||
|
||||
# activate different ways to authenticate |
||||
# https://symfony.com/doc/current/security.html#the-firewall |
||||
|
||||
# https://symfony.com/doc/current/security/impersonating_user.html |
||||
# switch_user: true |
||||
|
||||
# Easy way to control access for large sections of your site |
||||
# Note: Only the *first* access control that matches will be used |
||||
access_control: |
||||
# - { path: ^/admin, roles: ROLE_ADMIN } |
||||
# - { path: ^/profile, roles: ROLE_USER } |
||||
|
||||
when@test: |
||||
security: |
||||
password_hashers: |
||||
# By default, password hashers are resource intensive and take time. This is |
||||
# important to generate secure password hashes. In tests however, secure hashes |
||||
# are not important, waste resources and increase test times. The following |
||||
# reduces the work factor to the lowest possible values. |
||||
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: |
||||
algorithm: auto |
||||
cost: 4 # Lowest possible value for bcrypt |
||||
time_cost: 3 # Lowest possible value for argon |
||||
memory_cost: 10 # Lowest possible value for argon |
@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
|
||||
framework: |
||||
default_locale: en |
||||
translator: |
||||
default_path: '%kernel.project_dir%/translations' |
||||
fallbacks: |
||||
- en |
||||
# providers: |
||||
# crowdin: |
||||
# dsn: '%env(CROWDIN_DSN)%' |
||||
# loco: |
||||
# dsn: '%env(LOCO_DSN)%' |
||||
# lokalise: |
||||
# dsn: '%env(LOKALISE_DSN)%' |
||||
# phrase: |
||||
# dsn: '%env(PHRASE_DSN)%' |
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
twig: |
||||
default_path: '%kernel.project_dir%/templates' |
||||
globals: |
||||
version: '%app.version%' |
||||
name: '%app.name%' |
||||
|
||||
when@test: |
||||
twig: |
||||
strict_variables: true |
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
framework: |
||||
validation: |
||||
email_validation_mode: html5 |
||||
|
||||
# Enables validator auto-mapping support. |
||||
# For instance, basic validation constraints will be inferred from Doctrine's metadata. |
||||
#auto_mapping: |
||||
# App\Entity\: [] |
||||
|
||||
when@test: |
||||
framework: |
||||
validation: |
||||
not_compromised_password: false |
@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
|
||||
when@dev: |
||||
web_profiler: |
||||
toolbar: true |
||||
intercept_redirects: false |
||||
|
||||
framework: |
||||
profiler: |
||||
only_exceptions: false |
||||
collect_serializer_data: true |
||||
|
||||
when@test: |
||||
web_profiler: |
||||
toolbar: false |
||||
intercept_redirects: false |
||||
|
||||
framework: |
||||
profiler: { collect: false } |
@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
|
||||
<?php |
||||
|
||||
if (file_exists(dirname(__DIR__).'/var/cache/prod/App_KernelProdContainer.preload.php')) { |
||||
require dirname(__DIR__).'/var/cache/prod/App_KernelProdContainer.preload.php'; |
||||
} |
@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
|
||||
controllers: |
||||
resource: |
||||
path: ../src/Controller/ |
||||
namespace: App\Controller |
||||
type: attribute |
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
when@dev: |
||||
_errors: |
||||
resource: '@FrameworkBundle/Resources/config/routing/errors.xml' |
||||
prefix: /_error |
@ -0,0 +1,8 @@
@@ -0,0 +1,8 @@
|
||||
when@dev: |
||||
web_profiler_wdt: |
||||
resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml' |
||||
prefix: /_wdt |
||||
|
||||
web_profiler_profiler: |
||||
resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml' |
||||
prefix: /_profiler |
@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
|
||||
# This file is the entry point to configure your own services. |
||||
# Files in the packages/ subdirectory configure your dependencies. |
||||
|
||||
# Put parameters here that don't need to change on each machine where the app is deployed |
||||
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration |
||||
parameters: |
||||
app.version: '%env(APP_VERSION)%' |
||||
app.name: '%env(APP_NAME)%' |
||||
|
||||
services: |
||||
# default configuration for services in *this* file |
||||
_defaults: |
||||
autowire: true # Automatically injects dependencies in your services. |
||||
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. |
||||
|
||||
# makes classes in src/ available to be used as services |
||||
# this creates a service per class whose id is the fully-qualified class name |
||||
App\: |
||||
resource: '../src/' |
||||
exclude: |
||||
- '../src/DependencyInjection/' |
||||
- '../src/Entity/' |
||||
- '../src/Kernel.php' |
||||
|
||||
# add more service definitions when explicit configuration is needed |
||||
# please note that last definitions always *replace* previous ones |
Binary file not shown.
@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
|
||||
version: '3' |
||||
|
||||
services: |
||||
###> doctrine/doctrine-bundle ### |
||||
database: |
||||
ports: |
||||
- "5432" |
||||
###< doctrine/doctrine-bundle ### |
||||
|
||||
###> symfony/mailer ### |
||||
mailer: |
||||
image: schickling/mailcatcher |
||||
ports: ["1025", "1080"] |
||||
###< symfony/mailer ### |
@ -0,0 +1,21 @@
@@ -0,0 +1,21 @@
|
||||
version: '3' |
||||
|
||||
services: |
||||
###> doctrine/doctrine-bundle ### |
||||
database: |
||||
image: postgres:${POSTGRES_VERSION:-15}-alpine |
||||
environment: |
||||
POSTGRES_DB: ${POSTGRES_DB:-app} |
||||
# You should definitely change the password in production |
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-!ChangeMe!} |
||||
POSTGRES_USER: ${POSTGRES_USER:-app} |
||||
volumes: |
||||
- database_data:/var/lib/postgresql/data:rw |
||||
# You may use a bind-mounted host directory instead, so that it is harder to accidentally remove the volume and lose all your data! |
||||
# - ./docker/db/data:/var/lib/postgresql/data:rw |
||||
###< doctrine/doctrine-bundle ### |
||||
|
||||
volumes: |
||||
###> doctrine/doctrine-bundle ### |
||||
database_data: |
||||
###< doctrine/doctrine-bundle ### |
@ -1,10 +0,0 @@
@@ -1,10 +0,0 @@
|
||||
@reboot searchd |
||||
@reboot indexer --all --rotate |
||||
|
||||
* * * * * indexer magnet --rotate > /dev/null 2>&1 |
||||
|
||||
* * * * * /usr/bin/php /YGGtracker/src/crontab/scrape.php > /dev/null 2>&1 |
||||
* * * * * /usr/bin/php /YGGtracker/src/crontab/export/push.php > /dev/null 2>&1 |
||||
0 5 * * * /usr/bin/php /YGGtracker/src/crontab/import/feed.php > /dev/null 2>&1 |
||||
0 0 * * * /usr/bin/php /YGGtracker/src/crontab/export/feed.php > /dev/null 2>&1 |
||||
0 0 * * * /usr/bin/php /YGGtracker/src/crontab/sitemap.php > /dev/null 2>&1 |
@ -1,29 +0,0 @@
@@ -1,29 +0,0 @@
|
||||
server { |
||||
listen [::]:80 default; |
||||
|
||||
allow 0200::/7; |
||||
deny all; |
||||
|
||||
root /var/www/html; |
||||
|
||||
index index.html index.htm index.nginx-debian.html index.php; |
||||
|
||||
server_name _; |
||||
|
||||
location / { |
||||
try_files $uri $uri/ =404 @yggtracker; |
||||
} |
||||
|
||||
location ~ \.php$ { |
||||
include snippets/fastcgi-php.conf; |
||||
fastcgi_pass unix:/run/php/php8.1-fpm.sock; |
||||
} |
||||
|
||||
location ~ /\. { |
||||
deny all; |
||||
} |
||||
|
||||
location @yggtracker { |
||||
rewrite ^/(.*)$ /index.php?$1 last; |
||||
} |
||||
} |
@ -1,71 +0,0 @@
@@ -1,71 +0,0 @@
|
||||
source yggtracker |
||||
{ |
||||
type = mysql |
||||
|
||||
sql_port = 3306 |
||||
sql_host = localhost |
||||
sql_user = |
||||
sql_pass = |
||||
sql_db = |
||||
} |
||||
|
||||
source magnet : yggtracker |
||||
{ |
||||
sql_query = \ |
||||
SELECT `magnet`.`timeAdded`, \ |
||||
`magnet`.`timeUpdated`, \ |
||||
`magnet`.`magnetId`, \ |
||||
`magnet`.`title`, \ |
||||
`magnet`.`preview`, \ |
||||
`magnet`.`description`, \ |
||||
`magnet`.`dn`, \ |
||||
(SELECT GROUP_CONCAT(DISTINCT `infoHash`.`value`) \ |
||||
FROM `infoHash` \ |
||||
JOIN `magnetToInfoHash` ON (`magnetToInfoHash`.`magnetId` = `magnet`.`magnetId`) \ |
||||
WHERE `infoHash`.`infoHashId` = `magnetToInfoHash`.`infoHashId`) AS `infoHash`, \ |
||||
(SELECT GROUP_CONCAT(DISTINCT `keywordTopic`.`value`) \ |
||||
FROM `keywordTopic` \ |
||||
JOIN `magnetToKeywordTopic` ON (`magnetToKeywordTopic`.`magnetId` = `magnet`.`magnetId`) \ |
||||
WHERE `keywordTopic`.`keywordTopicId` = `magnetToKeywordTopic`.`keywordTopicId`) AS `keywords`, \ |
||||
(SELECT GROUP_CONCAT(DISTINCT `magnetComment`.`value`) \ |
||||
FROM `magnetComment` \ |
||||
WHERE `magnetComment`.`magnetId` = `magnet`.`magnetId`) AS `comments` \ |
||||
FROM `magnet`\ |
||||
|
||||
sql_attr_uint = magnetId |
||||
} |
||||
|
||||
index magnet |
||||
{ |
||||
source = magnet |
||||
path = /var/lib/sphinxsearch/data/magnet |
||||
|
||||
morphology = stem_cz, stem_ar, stem_enru |
||||
|
||||
min_word_len = 2 |
||||
min_prefix_len = 2 |
||||
|
||||
html_strip = 1 |
||||
|
||||
index_exact_words = 1 |
||||
} |
||||
|
||||
indexer |
||||
{ |
||||
mem_limit = 256M |
||||
} |
||||
|
||||
searchd |
||||
{ |
||||
listen = 127.0.0.1:9306:mysql41 |
||||
log = /var/log/sphinxsearch/searchd.log |
||||
query_log = /var/log/sphinxsearch/query.log |
||||
pid_file = /run/sphinxsearch/searchd.pid |
||||
binlog_path = /var/lib/sphinxsearch/data |
||||
read_timeout = 5 |
||||
max_children = 30 |
||||
seamless_rotate = 1 |
||||
preopen_indexes = 1 |
||||
unlink_old = 1 |
||||
workers = threads # for RT to work |
||||
} |
@ -0,0 +1,38 @@
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
||||
<!-- https://phpunit.readthedocs.io/en/latest/configuration.html --> |
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd" |
||||
backupGlobals="false" |
||||
colors="true" |
||||
bootstrap="tests/bootstrap.php" |
||||
convertDeprecationsToExceptions="false" |
||||
> |
||||
<php> |
||||
<ini name="display_errors" value="1" /> |
||||
<ini name="error_reporting" value="-1" /> |
||||
<server name="APP_ENV" value="test" force="true" /> |
||||
<server name="SHELL_VERBOSITY" value="-1" /> |
||||
<server name="SYMFONY_PHPUNIT_REMOVE" value="" /> |
||||
<server name="SYMFONY_PHPUNIT_VERSION" value="9.5" /> |
||||
</php> |
||||
|
||||
<testsuites> |
||||
<testsuite name="Project Test Suite"> |
||||
<directory>tests</directory> |
||||
</testsuite> |
||||
</testsuites> |
||||
|
||||
<coverage processUncoveredFiles="true"> |
||||
<include> |
||||
<directory suffix=".php">src</directory> |
||||
</include> |
||||
</coverage> |
||||
|
||||
<listeners> |
||||
<listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener" /> |
||||
</listeners> |
||||
|
||||
<extensions> |
||||
</extensions> |
||||
</phpunit> |
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
<?php |
||||
|
||||
use App\Kernel; |
||||
|
||||
require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; |
||||
|
||||
return function (array $context) { |
||||
return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']); |
||||
}; |
@ -0,0 +1,20 @@
@@ -0,0 +1,20 @@
|
||||
<?php |
||||
|
||||
namespace App\Controller; |
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; |
||||
use Symfony\Component\Routing\Annotation\Route; |
||||
use Symfony\Component\HttpFoundation\Response; |
||||
use Symfony\Component\HttpFoundation\Request; |
||||
|
||||
class HomeController extends AbstractController |
||||
{ |
||||
#[Route( |
||||
'/{_locale}/', |
||||
name: 'home_index' |
||||
)] |
||||
public function index(Request $request): Response |
||||
{ |
||||
return $this->render('default/home/index.html.twig'); |
||||
} |
||||
} |
@ -0,0 +1,67 @@
@@ -0,0 +1,67 @@
|
||||
<?php |
||||
|
||||
namespace App\Controller; |
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; |
||||
use Symfony\Component\Routing\Annotation\Route; |
||||
use Symfony\Component\HttpFoundation\Response; |
||||
use Symfony\Component\HttpFoundation\Request; |
||||
|
||||
class PageController extends AbstractController |
||||
{ |
||||
#[Route( |
||||
'/{_locale}/page/submit', |
||||
name: 'page_submit' |
||||
)] |
||||
public function submit(): Response |
||||
{ |
||||
return $this->render('default/page/submit.html.twig', [ |
||||
// @TODO |
||||
]); |
||||
} |
||||
|
||||
#[Route( |
||||
'/{_locale}/page/stars', |
||||
name: 'page_stars' |
||||
)] |
||||
public function stars(): Response |
||||
{ |
||||
// @TODO |
||||
} |
||||
|
||||
#[Route( |
||||
'/{_locale}/page/views', |
||||
name: 'page_views' |
||||
)] |
||||
public function views(): Response |
||||
{ |
||||
// @TODO |
||||
} |
||||
|
||||
#[Route( |
||||
'/{_locale}/page/downloads', |
||||
name: 'page_downloads' |
||||
)] |
||||
public function downloads(): Response |
||||
{ |
||||
// @TODO |
||||
} |
||||
|
||||
#[Route( |
||||
'/{_locale}/page/comments', |
||||
name: 'page_comments' |
||||
)] |
||||
public function comments(): Response |
||||
{ |
||||
// @TODO |
||||
} |
||||
|
||||
#[Route( |
||||
'/{_locale}/page/editions', |
||||
name: 'page_editions' |
||||
)] |
||||
public function editions(): Response |
||||
{ |
||||
// @TODO |
||||
} |
||||
} |
@ -0,0 +1,76 @@
@@ -0,0 +1,76 @@
|
||||
<?php |
||||
|
||||
namespace App\Controller; |
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; |
||||
use Symfony\Component\Routing\Annotation\Route; |
||||
use Symfony\Component\HttpFoundation\Response; |
||||
use Symfony\Component\HttpFoundation\Request; |
||||
|
||||
use App\Service\User; |
||||
|
||||
class ProfileController extends AbstractController |
||||
{ |
||||
#[Route( |
||||
'/{_locale}/profile', |
||||
name: 'profile_index' |
||||
)] |
||||
public function index(Request $request, User $user): Response |
||||
{ |
||||
return $this->render( |
||||
'default/profile/index.html.twig', |
||||
[ |
||||
'user' => $user->init($request->getClientIp()) |
||||
] |
||||
); |
||||
} |
||||
|
||||
#[Route( |
||||
'/{_locale}/profile/setting', |
||||
name: 'profile_setting' |
||||
)] |
||||
public function setting(): Response |
||||
{ |
||||
// @TODO |
||||
return $this->render( |
||||
'default/profile/setting.html.twig' |
||||
); |
||||
} |
||||
|
||||
public function module(string $route = ''): Response |
||||
{ |
||||
return $this->render( |
||||
'default/profile/module.html.twig', |
||||
[ |
||||
'route' => $route, |
||||
'stars' => 0, |
||||
'views' => 0, |
||||
'comments' => 0, |
||||
'downloads' => 0, |
||||
'editions' => 0, |
||||
'identicon' => $this->_getIdenticon( |
||||
'@TODO', |
||||
17, |
||||
[ |
||||
'backgroundColor' => 'rgba(255, 255, 255, 0)', |
||||
] |
||||
) |
||||
] |
||||
); |
||||
} |
||||
|
||||
private function _getIdenticon( |
||||
mixed $id, |
||||
int $size, |
||||
array $style, |
||||
string $format = 'webp') : string |
||||
{ |
||||
$identicon = new \Jdenticon\Identicon(); |
||||
|
||||
$identicon->setValue($id); |
||||
$identicon->setSize($size); |
||||
$identicon->setStyle($style); |
||||
|
||||
return $identicon->getImageDataUri($format); |
||||
} |
||||
} |
@ -0,0 +1,31 @@
@@ -0,0 +1,31 @@
|
||||
<?php |
||||
|
||||
namespace App\Controller; |
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; |
||||
use Symfony\Component\Routing\Annotation\Route; |
||||
use Symfony\Component\HttpFoundation\Response; |
||||
use Symfony\Component\HttpFoundation\Request; |
||||
|
||||
class SearchController extends AbstractController |
||||
{ |
||||
#[Route( |
||||
'/{_locale}/search', |
||||
name: 'search_index' |
||||
)] |
||||
public function index(Request $request): Response |
||||
{ |
||||
$query = $request->query->get('query'); |
||||
|
||||
return $this->render('default/search/index.html.twig', [ |
||||
'query' => $query |
||||
]); |
||||
} |
||||
|
||||
public function module(string $query = ''): Response |
||||
{ |
||||
return $this->render('default/search/module.html.twig', [ |
||||
'query' => $query, |
||||
]); |
||||
} |
||||
} |
@ -0,0 +1,27 @@
@@ -0,0 +1,27 @@
|
||||
<?php |
||||
|
||||
namespace App\Entity; |
||||
|
||||
use App\Repository\PageRepository; |
||||
use Doctrine\ORM\Mapping as ORM; |
||||
|
||||
#[ORM\Entity(repositoryClass: PageRepository::class)] |
||||
class Page |
||||
{ |
||||
#[ORM\Id] |
||||
#[ORM\GeneratedValue] |
||||
#[ORM\Column] |
||||
private ?int $id = null; |
||||
|
||||
public function getId(): ?int |
||||
{ |
||||
return $this->id; |
||||
} |
||||
|
||||
public function setId(string $id): static |
||||
{ |
||||
$this->id = $id; |
||||
|
||||
return $this; |
||||
} |
||||
} |
@ -0,0 +1,147 @@
@@ -0,0 +1,147 @@
|
||||
<?php |
||||
|
||||
namespace App\Entity; |
||||
|
||||
use App\Repository\UserRepository; |
||||
use Doctrine\ORM\Mapping as ORM; |
||||
|
||||
#[ORM\Entity(repositoryClass: UserRepository::class)] |
||||
class User |
||||
{ |
||||
#[ORM\Id] |
||||
#[ORM\GeneratedValue] |
||||
#[ORM\Column] |
||||
private ?int $id = null; |
||||
|
||||
#[ORM\Column(length: 255)] |
||||
private ?string $address = null; |
||||
|
||||
#[ORM\Column] |
||||
private ?int $added = null; |
||||
|
||||
#[ORM\Column] |
||||
private ?int $updated = null; |
||||
|
||||
#[ORM\Column] |
||||
private ?int $visited = null; |
||||
|
||||
#[ORM\Column] |
||||
private ?bool $public = null; |
||||
|
||||
#[ORM\Column] |
||||
private ?bool $moderator = null; |
||||
|
||||
#[ORM\Column] |
||||
private ?bool $approved = null; |
||||
|
||||
#[ORM\Column] |
||||
private ?bool $status = null; |
||||
|
||||
public function getId(): ?int |
||||
{ |
||||
return $this->id; |
||||
} |
||||
|
||||
public function setId(string $id): static |
||||
{ |
||||
$this->id = $id; |
||||
|
||||
return $this; |
||||
} |
||||
|
||||
public function getAddress(): ?string |
||||
{ |
||||
return $this->address; |
||||
} |
||||
|
||||
public function setAddress(string $address): static |
||||
{ |
||||
$this->address = $address; |
||||
|
||||
return $this; |
||||
} |
||||
|
||||
public function getAdded(): ?int |
||||
{ |
||||
return $this->added; |
||||
} |
||||
|
||||
public function setAdded(int $added): static |
||||
{ |
||||
$this->added = $added; |
||||
|
||||
return $this; |
||||
} |
||||
|
||||
public function getUpdated(): ?int |
||||
{ |
||||
return $this->updated; |
||||
} |
||||
|
||||
public function setUpdated(int $updated): static |
||||
{ |
||||
$this->updated = $updated; |
||||
|
||||
return $this; |
||||
} |
||||
|
||||
public function getVisited(): ?int |
||||
{ |
||||
return $this->visited; |
||||
} |
||||
|
||||
public function setVisited(int $visited): static |
||||
{ |
||||
$this->visited = $visited; |
||||
|
||||
return $this; |
||||
} |
||||
|
||||
public function isPublic(): ?bool |
||||
{ |
||||
return $this->public; |
||||
} |
||||
|
||||
public function setPublic(bool $public): static |
||||
{ |
||||
$this->public = $public; |
||||
|
||||
return $this; |
||||
} |
||||
|
||||
public function isModerator(): ?bool |
||||
{ |
||||
return $this->moderator; |
||||
} |
||||
|
||||
public function setModerator(bool $moderator): static |
||||
{ |
||||
$this->moderator = $moderator; |
||||
|
||||
return $this; |
||||
} |
||||
|
||||
public function isApproved(): ?bool |
||||
{ |
||||
return $this->approved; |
||||
} |
||||
|
||||
public function setApproved(bool $approved): static |
||||
{ |
||||
$this->approved = $approved; |
||||
|
||||
return $this; |
||||
} |
||||
|
||||
public function isStatus(): ?bool |
||||
{ |
||||
return $this->status; |
||||
} |
||||
|
||||
public function setStatus(bool $status): static |
||||
{ |
||||
$this->status = $status; |
||||
|
||||
return $this; |
||||
} |
||||
} |
@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
|
||||
<?php |
||||
|
||||
namespace App; |
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; |
||||
use Symfony\Component\HttpKernel\Kernel as BaseKernel; |
||||
|
||||
class Kernel extends BaseKernel |
||||
{ |
||||
use MicroKernelTrait; |
||||
} |
@ -0,0 +1,48 @@
@@ -0,0 +1,48 @@
|
||||
<?php |
||||
|
||||
namespace App\Repository; |
||||
|
||||
use App\Entity\Page; |
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; |
||||
use Doctrine\Persistence\ManagerRegistry; |
||||
|
||||
/** |
||||
* @extends ServiceEntityRepository<Page> |
||||
* |
||||
* @method Page|null find($id, $lockMode = null, $lockVersion = null) |
||||
* @method Page|null findOneBy(array $criteria, array $orderBy = null) |
||||
* @method Page[] findAll() |
||||
* @method Page[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) |
||||
*/ |
||||
class PageRepository extends ServiceEntityRepository |
||||
{ |
||||
public function __construct(ManagerRegistry $registry) |
||||
{ |
||||
parent::__construct($registry, Page::class); |
||||
} |
||||
|
||||
// /** |
||||
// * @return Page[] Returns an array of Page objects |
||||
// */ |
||||
// public function findByExampleField($value): array |
||||
// { |
||||
// return $this->createQueryBuilder('p') |
||||
// ->andWhere('p.exampleField = :val') |
||||
// ->setParameter('val', $value) |
||||
// ->orderBy('p.id', 'ASC') |
||||
// ->setMaxResults(10) |
||||
// ->getQuery() |
||||
// ->getResult() |
||||
// ; |
||||
// } |
||||
|
||||
// public function findOneBySomeField($value): ?Page |
||||
// { |
||||
// return $this->createQueryBuilder('p') |
||||
// ->andWhere('p.exampleField = :val') |
||||
// ->setParameter('val', $value) |
||||
// ->getQuery() |
||||
// ->getOneOrNullResult() |
||||
// ; |
||||
// } |
||||
} |
@ -0,0 +1,48 @@
@@ -0,0 +1,48 @@
|
||||
<?php |
||||
|
||||
namespace App\Repository; |
||||
|
||||
use App\Entity\User; |
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; |
||||
use Doctrine\Persistence\ManagerRegistry; |
||||
|
||||
/** |
||||
* @extends ServiceEntityRepository<User> |
||||
* |
||||
* @method User|null find($id, $lockMode = null, $lockVersion = null) |
||||
* @method User|null findOneBy(array $criteria, array $orderBy = null) |
||||
* @method User[] findAll() |
||||
* @method User[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) |
||||
*/ |
||||
class UserRepository extends ServiceEntityRepository |
||||
{ |
||||
public function __construct(ManagerRegistry $registry) |
||||
{ |
||||
parent::__construct($registry, User::class); |
||||
} |
||||
|
||||
// /** |
||||
// * @return User[] Returns an array of User objects |
||||
// */ |
||||
// public function findByExampleField($value): array |
||||
// { |
||||
// return $this->createQueryBuilder('u') |
||||
// ->andWhere('u.exampleField = :val') |
||||
// ->setParameter('val', $value) |
||||
// ->orderBy('u.id', 'ASC') |
||||
// ->setMaxResults(10) |
||||
// ->getQuery() |
||||
// ->getResult() |
||||
// ; |
||||
// } |
||||
|
||||
// public function findOneBySomeField($value): ?User |
||||
// { |
||||
// return $this->createQueryBuilder('u') |
||||
// ->andWhere('u.exampleField = :val') |
||||
// ->setParameter('val', $value) |
||||
// ->getQuery() |
||||
// ->getOneOrNullResult() |
||||
// ; |
||||
// } |
||||
} |
@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
|
||||
<?php |
||||
|
||||
namespace App\Service; |
||||
|
||||
class User |
||||
{ |
||||
public function init(string $address): string |
||||
{ |
||||
// @TODO |
||||
return $address; |
||||
} |
||||
} |
@ -1,151 +0,0 @@
@@ -1,151 +0,0 @@
|
||||
<?php |
||||
|
||||
class AppControllerIndex |
||||
{ |
||||
private $_database; |
||||
private $_validator; |
||||
private $_website; |
||||
private $_session; |
||||
|
||||
public function __construct( |
||||
AppModelDatabase $database, |
||||
AppModelValidator $validator, |
||||
AppModelWebsite $website, |
||||
AppModelSession $session |
||||
) |
||||
{ |
||||
$this->_database = $database; |
||||
$this->_validator = $validator; |
||||
$this->_website = $website; |
||||
$this->_session = $session; |
||||
} |
||||
|
||||
private function _initUser(string $address) |
||||
{ |
||||
$error = []; |
||||
if (!$this->_validator->host($address, $error)) |
||||
{ |
||||
$this->_response( |
||||
sprintf( |
||||
_('Error - %s'), |
||||
$this->_website->getName() |
||||
), |
||||
_('406'), |
||||
$error, |
||||
406 |
||||
); |
||||
} |
||||
|
||||
try |
||||
{ |
||||
$this->_database->beginTransaction(); |
||||
|
||||
$user = $this->_database->getUser( |
||||
$this->_database->initUserId( |
||||
$address, |
||||
$this->_website->getDefaultUserStatus(), |
||||
$this->_website->getDefaultUserApproved(), |
||||
time() |
||||
) |
||||
); |
||||
|
||||
$this->_database->commit(); |
||||
} |
||||
|
||||
catch (Exception $error) |
||||
{ |
||||
$this->_database->rollback(); |
||||
|
||||
$this->_response( |
||||
sprintf( |
||||
_('Error - %s'), |
||||
$this->_website->getName() |
||||
), |
||||
_('500'), |
||||
$error, |
||||
500 |
||||
); |
||||
} |
||||
|
||||
// Access denied |
||||
if (!$user->status) |
||||
{ |
||||
$this->_response( |
||||
sprintf( |
||||
_('Error - %s'), |
||||
$this->_website->getName() |
||||
), |
||||
_('403'), |
||||
_('Access denied'), |
||||
403 |
||||
); |
||||
} |
||||
} |
||||
|
||||
public function render() |
||||
{ |
||||
$user = $this->_initUser( |
||||
$this->_session->getAddress() |
||||
); |
||||
|
||||
$page = isset($_GET['page']) ? (int) $_GET['page'] : 1; |
||||
|
||||
$pages = []; |
||||
|
||||
require_once __DIR__ . '/module/pagination.php'; |
||||
|
||||
$appControllerModulePagination = new appControllerModulePagination(); |
||||
|
||||
require_once __DIR__ . '/module/head.php'; |
||||
|
||||
$appControllerModuleHead = new AppControllerModuleHead( |
||||
$this->_website->getUrl(), |
||||
$page > 1 ? |
||||
sprintf( |
||||
_('Page %s - BitTorrent Registry for Yggdrasil - %s'), |
||||
$page, |
||||
$this->_website->getName() |
||||
) : |
||||
sprintf( |
||||
_('%s - BitTorrent Registry for Yggdrasil'), |
||||
$this->_website->getName() |
||||
), |
||||
[ |
||||
[ |
||||
'rel' => 'stylesheet', |
||||
'type' => 'text/css', |
||||
'href' => sprintf( |
||||
'assets/theme/default/css/common.css?%s', |
||||
CSS_VERSION |
||||
), |
||||
], |
||||
[ |
||||
'rel' => 'stylesheet', |
||||
'type' => 'text/css', |
||||
'href' => sprintf( |
||||
'assets/theme/default/css/framework.css?%s', |
||||
CSS_VERSION |
||||
), |
||||
], |
||||
] |
||||
); |
||||
|
||||
require_once __DIR__ . '/module/profile.php'; |
||||
|
||||
$appControllerModuleProfile = new AppControllerModuleProfile( |
||||
$this->_database, |
||||
$this->_website, |
||||
$this->_session |
||||
); |
||||
|
||||
require_once __DIR__ . '/module/header.php'; |
||||
|
||||
$appControllerModuleHeader = new AppControllerModuleHeader(); |
||||
|
||||
require_once __DIR__ . '/module/footer.php'; |
||||
|
||||
$appControllerModuleFooter = new AppControllerModuleFooter(); |
||||
|
||||
include __DIR__ . '/../view/theme/default/index.phtml'; |
||||
} |
||||
} |
@ -1,13 +0,0 @@
@@ -1,13 +0,0 @@
|
||||
<?php |
||||
|
||||
class AppControllerModuleFooter |
||||
{ |
||||
public function render() |
||||
{ |
||||
$trackers = Environment::config('trackers'); |
||||
|
||||
$api = Environment::config('website')->api->export; |
||||
|
||||
include __DIR__ . '../../../view/theme/default/module/footer.phtml'; |
||||
} |
||||
} |
@ -1,54 +0,0 @@
@@ -1,54 +0,0 @@
|
||||
<?php |
||||
|
||||
class AppControllerModuleHead |
||||
{ |
||||
private $_title; |
||||
private $_base; |
||||
private $_links = []; |
||||
|
||||
public function __construct(string $base, string $title, array $links = []) |
||||
{ |
||||
$this->setBase($base); |
||||
$this->setTitle($title); |
||||
|
||||
foreach ($links as $link) |
||||
{ |
||||
$this->addLink( |
||||
$link['rel'], |
||||
$link['type'], |
||||
$link['href'], |
||||
); |
||||
} |
||||
} |
||||
|
||||
public function setBase(string $base) : void |
||||
{ |
||||
$this->_base = $base; |
||||
} |
||||
|
||||
public function setTitle(string $title) : void |
||||
{ |
||||
$this->_title = $title; |
||||
} |
||||
|
||||
public function addLink(string $rel, string $type, string $href) : void |
||||
{ |
||||
$this->_links[] = (object) |
||||
[ |
||||
'rel' => $rel, |
||||
'type' => $type, |
||||
'href' => $href, |
||||
]; |
||||
} |
||||
|
||||
public function render() |
||||
{ |
||||
$base = $this->_base; |
||||
|
||||
$links = $this->_links; |
||||
|
||||
$title = htmlentities($this->_title); |
||||
|
||||
include __DIR__ . '../../../view/theme/default/module/head.phtml'; |
||||
} |
||||
} |
@ -1,19 +0,0 @@
@@ -1,19 +0,0 @@
|
||||
<?php |
||||
|
||||
class AppControllerModuleHeader |
||||
{ |
||||
public function render() |
||||
{ |
||||
$name = str_replace( |
||||
'YGG', |
||||
'<span>YGG</span>', |
||||
Environment::config('website')->name |
||||
); |
||||
|
||||
require_once __DIR__ . '/search.php'; |
||||
|
||||
$appControllerModuleSearch = new AppControllerModuleSearch(); |
||||
|
||||
include __DIR__ . '../../../view/theme/default/module/header.phtml'; |
||||
} |
||||
} |
@ -1,9 +0,0 @@
@@ -1,9 +0,0 @@
|
||||
<?php |
||||
|
||||
class AppControllerCommonPage |
||||
{ |
||||
public function render(int $pageId) |
||||
{ |
||||
include __DIR__ . '../../../view/theme/default/common/page.phtml'; |
||||
} |
||||
} |
@ -1,43 +0,0 @@
@@ -1,43 +0,0 @@
|
||||
<?php |
||||
|
||||
class AppControllerModulePagination |
||||
{ |
||||
public function render(string $url, int $total, int $limit) |
||||
{ |
||||
if ($total > $limit) |
||||
{ |
||||
parse_str($url, $query); |
||||
|
||||
$pagination->page = isset($query['total']) ? (int) $query['total'] : 1; |
||||
$pagination->pages = ceil($total / $limit); |
||||
|
||||
// Previous |
||||
if ($page > 1) |
||||
{ |
||||
$query['page'] = $page - 1; |
||||
|
||||
$pagination->back = sprintf('%s', WEBSITE_URL, http_build_query($query)); |
||||
} |
||||
|
||||
else |
||||
{ |
||||
$pagination->back = false; |
||||
} |
||||
|
||||
// Next |
||||
if ($page < ceil($total / $limit)) |
||||
{ |
||||
$query['page'] = $page + 1; |
||||
|
||||
$pagination->next = sprintf('%s', WEBSITE_URL, http_build_query($query)); |
||||
} |
||||
|
||||
else |
||||
{ |
||||
$pagination->next = false; |
||||
} |
||||
|
||||
// Render |
||||
} |
||||
} |
||||
} |
@ -1,54 +0,0 @@
@@ -1,54 +0,0 @@
|
||||
<?php |
||||
|
||||
class AppControllerModuleProfile |
||||
{ |
||||
private $_database; |
||||
private $_website; |
||||
private $_session; |
||||
|
||||
public function __construct( |
||||
AppModelDatabase $database, |
||||
AppModelWebsite $website, |
||||
AppModelSession $session) |
||||
{ |
||||
$this->_database = $database; |
||||
$this->_website = $website; |
||||
$this->_session = $session; |
||||
} |
||||
|
||||
public function render() |
||||
{ |
||||
$route = isset($_GET['_route_']) ? (string) $_GET['_route_'] : ''; |
||||
|
||||
$user = $this->_database->getUser( |
||||
$this->_database->initUserId( |
||||
$this->_session->getAddress(), |
||||
$this->_website->getDefaultUserStatus(), |
||||
$this->_website->getDefaultUserApproved(), |
||||
time() |
||||
) |
||||
); |
||||
|
||||
$stars = $this->_database->findUserPageStarsDistinctTotalByValue($user->userId, true); |
||||
$views = $this->_database->findUserPageViewsDistinctTotal($user->userId); |
||||
$downloads = 0; // @TODO $this->_database->findUserPageDownloadsDistinctTotal($user->userId); |
||||
$comments = $this->_database->findUserPageCommentsDistinctTotal($user->userId); |
||||
$editions = 0; // @TODO $this->_database->findUserPageEditionsDistinctTotal($user->userId); |
||||
|
||||
$address = $user->address; |
||||
|
||||
$icon = new Jdenticon\Identicon(); |
||||
|
||||
$icon->setValue($user->address); |
||||
$icon->setSize(16); |
||||
$icon->setStyle( |
||||
[ |
||||
'backgroundColor' => 'rgba(255, 255, 255, 0)', |
||||
] |
||||
); |
||||
|
||||
$identicon = $icon->getImageDataUri('webp'); |
||||
|
||||
include __DIR__ . '../../../view/theme/default/module/profile.phtml'; |
||||
} |
||||
} |
@ -1,24 +0,0 @@
@@ -1,24 +0,0 @@
|
||||
<?php |
||||
|
||||
class AppControllerModuleSearch |
||||
{ |
||||
public function render() |
||||
{ |
||||
$query = empty($_GET['query']) ? false : urldecode($_GET['query']); |
||||
$locale = empty($_GET['locale']) ? 'all' : urldecode($_GET['locale']); |
||||
|
||||
$locales = []; |
||||
|
||||
foreach (Environment::config('locales') as $key => $value) |
||||
{ |
||||
$locales[$key] = (object) |
||||
[ |
||||
'key' => $key, |
||||
'value' => $value[0], |
||||
'active' => $key === $locale // false !== stripos($_SERVER['HTTP_ACCEPT_LANGUAGE'], $key) ? true : false, |
||||
]; |
||||
} |
||||
|
||||
include __DIR__ . '../../../view/theme/default/module/search.phtml'; |
||||
} |
||||
} |
@ -1,428 +0,0 @@
@@ -1,428 +0,0 @@
|
||||
<?php |
||||
|
||||
class AppControllerPage |
||||
{ |
||||
private $_database; |
||||
private $_validator; |
||||
private $_locale; |
||||
private $_website; |
||||
private $_session; |
||||
private $_request; |
||||
|
||||
public function __construct( |
||||
AppModelDatabase $database, |
||||
AppModelValidator $validator, |
||||
AppModelLocale $locale, |
||||
AppModelWebsite $website, |
||||
AppModelSession $session, |
||||
AppModelRequest $request |
||||
) |
||||
{ |
||||
$this->_database = $database; |
||||
$this->_validator = $validator; |
||||
$this->_locale = $locale; |
||||
$this->_website = $website; |
||||
$this->_session = $session; |
||||
$this->_request = $request; |
||||
} |
||||
|
||||
private function _response(string $title, string $h1, mixed $data, int $code = 200) |
||||
{ |
||||
require_once __DIR__ . '/response.php'; |
||||
|
||||
if (is_array($data)) |
||||
{ |
||||
$data = implode('<br />', $data); |
||||
} |
||||
|
||||
$appControllerResponse = new AppControllerResponse( |
||||
$title, |
||||
$h1, |
||||
$data, |
||||
$code |
||||
); |
||||
|
||||
$appControllerResponse->render(); |
||||
|
||||
exit; |
||||
} |
||||
|
||||
private function _initUser(string $address) |
||||
{ |
||||
$error = []; |
||||
if (!$this->_validator->host($address, $error)) |
||||
{ |
||||
$this->_response( |
||||
sprintf( |
||||
_('Error - %s'), |
||||
$this->_website->getName() |
||||
), |
||||
_('406'), |
||||
$error, |
||||
406 |
||||
); |
||||
} |
||||
|
||||
try |
||||
{ |
||||
$this->_database->beginTransaction(); |
||||
|
||||
$user = $this->_database->getUser( |
||||
$this->_database->initUserId( |
||||
$address, |
||||
$this->_website->getDefaultUserStatus(), |
||||
$this->_website->getDefaultUserApproved(), |
||||
time() |
||||
) |
||||
); |
||||
|
||||
$this->_database->commit(); |
||||
} |
||||
|
||||
catch (Exception $error) |
||||
{ |
||||
$this->_database->rollback(); |
||||
|
||||
$this->_response( |
||||
sprintf( |
||||
_('Error - %s'), |
||||
$this->_website->getName() |
||||
), |
||||
_('500'), |
||||
$error, |
||||
500 |
||||
); |
||||
} |
||||
|
||||
// Access denied |
||||
if (!$user->status) |
||||
{ |
||||
$this->_response( |
||||
sprintf( |
||||
_('Error - %s'), |
||||
$this->_website->getName() |
||||
), |
||||
_('403'), |
||||
_('Access denied'), |
||||
403 |
||||
); |
||||
} |
||||
} |
||||
|
||||
private function _initLocale(string $value) |
||||
{ |
||||
if (!$locale = $this->_database->findLocale($value)) |
||||
{ |
||||
$locale = $this->_database->getLocale( |
||||
$this->_database->addLocale( |
||||
$value |
||||
) |
||||
); |
||||
} |
||||
|
||||
return $locale; |
||||
} |
||||
|
||||
private function _initPage(int $pageId = 0) |
||||
{ |
||||
if (!$page = $this->_database->getPage($pageId)) |
||||
{ |
||||
$page = $this->_database->getPage( |
||||
$this->_database->addPage( |
||||
time() |
||||
) |
||||
); |
||||
} |
||||
|
||||
return $page; |
||||
} |
||||
|
||||
private function _initText(string $value, string $mime = 'text/plain') |
||||
{ |
||||
if (!$text = $this->_database->findText($mime, md5($value))) |
||||
{ |
||||
$text = $this->_database->getText( |
||||
$this->_database->addText( |
||||
$mime, |
||||
md5($value), |
||||
$value, |
||||
time() |
||||
) |
||||
); |
||||
} |
||||
|
||||
return $text; |
||||
} |
||||
|
||||
private function _commitPageTitle(int $pageId, int $userId, int $localeId, string $text, string $mime = 'text/plain') |
||||
{ |
||||
$textId = $this->_initText( |
||||
$text, |
||||
$mime |
||||
)->textId; |
||||
|
||||
if (!$this->_database->findPageTitleLatest($pageId, |
||||
$userId, |
||||
$localeId, |
||||
$textId)) |
||||
{ |
||||
$this->_database->addPageTitle( |
||||
$pageId, |
||||
$userId, |
||||
$localeId, |
||||
$textId, |
||||
time() |
||||
); |
||||
} |
||||
} |
||||
|
||||
public function renderFormSubmit() |
||||
{ |
||||
// Init user |
||||
$user = $this->_initUser( |
||||
$this->_session->getAddress() |
||||
); |
||||
|
||||
// Init page |
||||
if ($this->_request->get('pageId')) |
||||
{ |
||||
$page = $this->_initPage( |
||||
(int) $this->_request->get('pageId') |
||||
); |
||||
} |
||||
|
||||
else if ($this->_request->post('pageId')) |
||||
{ |
||||
$page = $this->_initPage( |
||||
(int) $this->_request->post('pageId') |
||||
); |
||||
} |
||||
|
||||
else |
||||
{ |
||||
$page = $this->_initPage(); |
||||
} |
||||
|
||||
// Init locale |
||||
if ($this->_locale->codeExists($this->_request->get('locale'))) |
||||
{ |
||||
$localeCode = (int) $this->_request->get('locale'); |
||||
} |
||||
|
||||
else if ($this->_locale->codeExists($this->_request->post('locale'))) |
||||
{ |
||||
$localeCode = (int) $this->_request->post('locale'); |
||||
} |
||||
|
||||
else |
||||
{ |
||||
$localeCode = $this->_website->getDefaultLocale(); |
||||
|
||||
if (!empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) // @TODO environment |
||||
{ |
||||
foreach ($this->_locale->getList() as $value) |
||||
{ |
||||
if (false !== stripos($_SERVER['HTTP_ACCEPT_LANGUAGE'], $value->code)) |
||||
{ |
||||
$localeCode = $value->code; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
$locale = $this->_initLocale($localeCode); |
||||
|
||||
// Init form |
||||
$form = (object) |
||||
[ |
||||
'pageId' => (object) |
||||
[ |
||||
'error' => [], |
||||
'type' => 'hidden', |
||||
'attribute' => (object) |
||||
[ |
||||
'value' => $page->pageId, |
||||
] |
||||
], |
||||
'locale' => (object) |
||||
[ |
||||
'error' => [], |
||||
'type' => 'select', |
||||
'options' => $this->_locale->getList(), |
||||
'value' => $locale->value, |
||||
'placeholder' => _('Page content language'), |
||||
], |
||||
'title' => (object) |
||||
[ |
||||
'error' => [], |
||||
'type' => 'text', |
||||
'attribute' => (object) |
||||
[ |
||||
'value' => null, |
||||
'required' => $this->_validator->getPageTitleRequired(), |
||||
'minlength' => $this->_validator->getPageTitleLengthMin(), |
||||
'maxlength' => $this->_validator->getPageTitleLengthMax(), |
||||
'placeholder' => sprintf( |
||||
_('Page subject (%s-%s chars)'), |
||||
number_format($this->_validator->getPageTitleLengthMin()), |
||||
number_format($this->_validator->getPageTitleLengthMax()) |
||||
), |
||||
] |
||||
], |
||||
'description' => (object) |
||||
[ |
||||
'error' => [], |
||||
'type' => 'textarea', |
||||
'attribute' => (object) |
||||
[ |
||||
'value' => null, |
||||
'required' => $this->_validator->getPageDescriptionRequired(), |
||||
'minlength' => $this->_validator->getPageDescriptionLengthMin(), |
||||
'maxlength' => $this->_validator->getPageDescriptionLengthMax(), |
||||
'placeholder' => sprintf( |
||||
_('Page description text (%s-%s chars)'), |
||||
number_format($this->_validator->getPageDescriptionLengthMin()), |
||||
number_format($this->_validator->getPageDescriptionLengthMax()) |
||||
), |
||||
] |
||||
], |
||||
'keywords' => (object) |
||||
[ |
||||
'error' => [], |
||||
'type' => 'textarea', |
||||
'attribute' => (object) |
||||
[ |
||||
'value' => null, |
||||
'required' => $this->_validator->getPageKeywordsRequired(), |
||||
'placeholder' => sprintf( |
||||
_('Page keywords (%s-%s total / %s-%s chars per item)'), |
||||
number_format($this->_validator->getPageKeywordsQuantityMin()), |
||||
number_format($this->_validator->getPageKeywordsQuantityMax()), |
||||
number_format($this->_validator->getPageKeywordLengthMin()), |
||||
number_format($this->_validator->getPageKeywordLengthMax()) |
||||
), |
||||
] |
||||
], |
||||
'sensitive' => (object) |
||||
[ |
||||
'error' => [], |
||||
'type' => 'checkbox', |
||||
'attribute' => (object) |
||||
[ |
||||
'value' => null, |
||||
'placeholder' => _('Apply NSFW filters for this publication'), |
||||
] |
||||
] |
||||
]; |
||||
|
||||
// Submit request |
||||
if ($this->_request->hasPost()) |
||||
{ |
||||
/// Title |
||||
if ($title = $this->_request->post('title')) |
||||
{ |
||||
$error = []; |
||||
|
||||
if (!$this->_validator->pageTitle($title, $error)) |
||||
{ |
||||
$form->title->error[] = $error; |
||||
} |
||||
|
||||
else |
||||
{ |
||||
$this->_commitPageTitle( |
||||
$page->pageId, |
||||
$user->userId, |
||||
$locale->localeId, |
||||
$title |
||||
); |
||||
} |
||||
|
||||
$form->title->attribute->value = htmlentities($title); |
||||
} |
||||
|
||||
if (isset($_POST['description'])) |
||||
{ |
||||
$error = []; |
||||
|
||||
if (!$this->_validator->pageDescription($_POST['description'], $error)) |
||||
{ |
||||
$form->description->error[] = $error; |
||||
} |
||||
|
||||
$form->description->attribute->value = htmlentities($_POST['description']); |
||||
} |
||||
|
||||
if (isset($_POST['keywords'])) |
||||
{ |
||||
$error = []; |
||||
|
||||
if (!$this->_validator->pageKeywords($_POST['keywords'], $error)) |
||||
{ |
||||
$form->keywords->error[] = $error; |
||||
} |
||||
|
||||
$form->keywords->attribute->value = htmlentities($_POST['keywords']); |
||||
} |
||||
|
||||
if (isset($_POST['sensitive'])) |
||||
{ |
||||
$form->sensitive->attribute->value = (bool) $_POST['sensitive']; |
||||
} |
||||
|
||||
// Request valid |
||||
if (empty($error)) |
||||
{ |
||||
// @TODO redirect |
||||
} |
||||
} |
||||
|
||||
// Render template |
||||
require_once __DIR__ . '/module/head.php'; |
||||
|
||||
$appControllerModuleHead = new AppControllerModuleHead( |
||||
$this->_website->getUrl(), |
||||
sprintf( |
||||
_('Submit - %s'), |
||||
$this->_website->getName() |
||||
), |
||||
[ |
||||
[ |
||||
'rel' => 'stylesheet', |
||||
'type' => 'text/css', |
||||
'href' => sprintf( |
||||
'assets/theme/default/css/common.css?%s', |
||||
CSS_VERSION |
||||
), |
||||
], |
||||
[ |
||||
'rel' => 'stylesheet', |
||||
'type' => 'text/css', |
||||
'href' => sprintf( |
||||
'assets/theme/default/css/framework.css?%s', |
||||
CSS_VERSION |
||||
), |
||||
], |
||||
] |
||||
); |
||||
|
||||
require_once __DIR__ . '/module/profile.php'; |
||||
|
||||
$appControllerModuleProfile = new AppControllerModuleProfile( |
||||
$this->_database, |
||||
$this->_website, |
||||
$this->_session |
||||
); |
||||
|
||||
require_once __DIR__ . '/module/header.php'; |
||||
|
||||
$appControllerModuleHeader = new AppControllerModuleHeader(); |
||||
|
||||
require_once __DIR__ . '/module/footer.php'; |
||||
|
||||
$appControllerModuleFooter = new AppControllerModuleFooter(); |
||||
|
||||
include __DIR__ . '../../view/theme/default/page/form/submit.phtml'; |
||||
} |
||||
} |
@ -1,65 +0,0 @@
@@ -1,65 +0,0 @@
|
||||
<?php |
||||
|
||||
class AppControllerResponse |
||||
{ |
||||
private $_title; |
||||
private $_h1; |
||||
private $_text; |
||||
private $_code; |
||||
|
||||
public function __construct(string $title, string $h1, string $text, int $code = 200) |
||||
{ |
||||
$this->_title = $title; |
||||
$this->_h1 = $h1; |
||||
$this->_text = $text; |
||||
$this->_code = $code; |
||||
} |
||||
|
||||
public function render() |
||||
{ |
||||
header( |
||||
sprintf( |
||||
'HTTP/1.0 %s Not Found', |
||||
$this->_code |
||||
) |
||||
); |
||||
|
||||
$h1 = $this->_h1; |
||||
$text = $this->_text; |
||||
|
||||
require_once __DIR__ . '/module/head.php'; |
||||
|
||||
$appControllerModuleHead = new AppControllerModuleHead( |
||||
Environment::config('website')->url, |
||||
$this->_title, |
||||
[ |
||||
[ |
||||
'rel' => 'stylesheet', |
||||
'type' => 'text/css', |
||||
'href' => sprintf( |
||||
'assets/theme/default/css/common.css?%s', |
||||
CSS_VERSION |
||||
), |
||||
], |
||||
[ |
||||
'rel' => 'stylesheet', |
||||
'type' => 'text/css', |
||||
'href' => sprintf( |
||||
'assets/theme/default/css/framework.css?%s', |
||||
CSS_VERSION |
||||
), |
||||
], |
||||
] |
||||
); |
||||
|
||||
require_once __DIR__ . '/module/header.php'; |
||||
|
||||
$appControllerModuleHeader = new AppControllerModuleHeader(); |
||||
|
||||
require_once __DIR__ . '/module/footer.php'; |
||||
|
||||
$appControllerModuleFooter = new AppControllerModuleFooter(); |
||||
|
||||
include __DIR__ . '../../view/theme/default/response.phtml'; |
||||
} |
||||
} |
@ -1,117 +0,0 @@
@@ -1,117 +0,0 @@
|
||||
<?php |
||||
|
||||
class AppControllerUser |
||||
{ |
||||
private $_database; |
||||
private $_validator; |
||||
private $_website; |
||||
|
||||
private $_user; |
||||
|
||||
public function __construct( |
||||
AppModelDatabase $database, |
||||
AppModelValidator $validator, |
||||
AppModelWebsite $website |
||||
) |
||||
{ |
||||
$this->_database = $database; |
||||
$this->_validator = $validator; |
||||
$this->_website = $website; |
||||
} |
||||
|
||||
private function _response(string $title, string $h1, mixed $data, int $code = 200) |
||||
{ |
||||
require_once __DIR__ . '/response.php'; |
||||
|
||||
if (is_array($data)) |
||||
{ |
||||
$data = implode('<br />', $data); |
||||
} |
||||
|
||||
$appControllerResponse = new AppControllerResponse( |
||||
$title, |
||||
$h1, |
||||
$data, |
||||
$code |
||||
); |
||||
|
||||
$appControllerResponse->render(); |
||||
|
||||
exit; |
||||
} |
||||
|
||||
public function getIdenticon(int $size) |
||||
{ |
||||
$icon = new Jdenticon\Identicon(); |
||||
|
||||
$icon->setValue($this->_user->public ? $this->_user->address : $this->_user->userId); |
||||
$icon->setSize($size); |
||||
$icon->setStyle( |
||||
[ |
||||
'backgroundColor' => 'rgba(255, 255, 255, 0)', |
||||
] |
||||
); |
||||
|
||||
return $icon->getImageDataUri('webp'); |
||||
} |
||||
|
||||
public function getUser() |
||||
{ |
||||
return $this->_user; |
||||
} |
||||
|
||||
public function getPublic() |
||||
{ |
||||
return $this->_user->public; |
||||
} |
||||
|
||||
public function getAddress() |
||||
{ |
||||
return $this->_user->address; |
||||
} |
||||
|
||||
public function findUserPageStarsDistinctTotalByValue(bool $value) : int |
||||
{ |
||||
return $this->_database->findUserPageStarsDistinctTotal( |
||||
$this->_user->userId, |
||||
$value |
||||
); |
||||
} |
||||
|
||||
public function findUserPageViewsDistinctTotal() : int |
||||
{ |
||||
return $this->_database->findUserPageViewsDistinctTotal( |
||||
$this->_user->userId |
||||
); |
||||
} |
||||
|
||||
public function findUserPageDownloadsDistinctTotal() : int |
||||
{ |
||||
return $this->_database->findUserPageDownloadsDistinctTotal( |
||||
$this->_user->userId |
||||
); |
||||
} |
||||
|
||||
public function findUserPageCommentsDistinctTotal() : int |
||||
{ |
||||
return $this->_database->findUserPageCommentsDistinctTotal( |
||||
$this->_user->userId |
||||
); |
||||
} |
||||
|
||||
public function findUserPageEditionsDistinctTotal() : int |
||||
{ |
||||
return $this->_database->findUserPageEditionsDistinctTotal( |
||||
$this->_user->userId |
||||
); |
||||
} |
||||
|
||||
public function updateUserPublic(bool $public, int $time) : int |
||||
{ |
||||
return $this->_database->updateUserPublic( |
||||
$this->_user->userId, |
||||
$public, |
||||
$time |
||||
); |
||||
} |
||||
} |
@ -1,37 +0,0 @@
@@ -1,37 +0,0 @@
|
||||
<?php |
||||
|
||||
class AppModelLocale { |
||||
|
||||
private $_locales = []; |
||||
|
||||
public function __construct(object $locales) |
||||
{ |
||||
foreach ($locales as $code => $value) |
||||
{ |
||||
$this->_locales[] = (object) |
||||
[ |
||||
'code' => $code, |
||||
'value' => $value[0], |
||||
'active' => false, |
||||
]; |
||||
} |
||||
} |
||||
|
||||
public function getList() : object |
||||
{ |
||||
return (object) $this->_locales; |
||||
} |
||||
|
||||
public function codeExists(string $code) : bool |
||||
{ |
||||
foreach ($this->_locales as $locale) |
||||
{ |
||||
if ($locale->code === $code) |
||||
{ |
||||
return true; |
||||
} |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
} |
@ -1,104 +0,0 @@
@@ -1,104 +0,0 @@
|
||||
<?php |
||||
|
||||
class AppModelRequest { |
||||
|
||||
private array $_get; |
||||
private array $_post; |
||||
private array $_files; |
||||
private array $_server; |
||||
|
||||
public function __construct(array $get, array $post, array $files, array $server) |
||||
{ |
||||
$this->_get = $get; |
||||
$this->_post = $post; |
||||
$this->_files = $files; |
||||
$this->_server = $server; |
||||
} |
||||
|
||||
public function get(string $key, mixed $value = null) : mixed |
||||
{ |
||||
if ($value) |
||||
{ |
||||
$this->_get[$key] = $value; |
||||
} |
||||
|
||||
if (isset($this->_get[$key])) |
||||
{ |
||||
return $this->_get[$key]; |
||||
} |
||||
|
||||
else |
||||
{ |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
public function post(string $key, mixed $value = null) : mixed |
||||
{ |
||||
if ($value) |
||||
{ |
||||
$this->_get[$key] = $value; |
||||
} |
||||
|
||||
if (isset($this->_post[$key])) |
||||
{ |
||||
return $this->_post[$key]; |
||||
} |
||||
|
||||
else |
||||
{ |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
public function files(string $key, mixed $value = null) : mixed |
||||
{ |
||||
if ($value) |
||||
{ |
||||
$this->_get[$key] = $value; |
||||
} |
||||
|
||||
if (isset($this->_files[$key])) |
||||
{ |
||||
return $this->_files[$key]; |
||||
} |
||||
|
||||
else |
||||
{ |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
public function server(string $key, mixed $value = null) : mixed |
||||
{ |
||||
if ($value) |
||||
{ |
||||
$this->_get[$key] = $value; |
||||
} |
||||
|
||||
if (isset($this->_get[$key])) |
||||
{ |
||||
return $this->_get[$key]; |
||||
} |
||||
|
||||
else |
||||
{ |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
public function hasPost() : bool |
||||
{ |
||||
return !empty($this->_post); |
||||
} |
||||
|
||||
public function hasGet() : bool |
||||
{ |
||||
return !empty($this->_get); |
||||
} |
||||
|
||||
public function hasFiles() : bool |
||||
{ |
||||
return !empty($this->_files); |
||||
} |
||||
} |
@ -1,111 +0,0 @@
@@ -1,111 +0,0 @@
|
||||
<?php |
||||
|
||||
class AppModelSphinx { |
||||
|
||||
private $_sphinx; |
||||
|
||||
public function __construct(string $host, int $port) |
||||
{ |
||||
$this->_sphinx = new PDO('mysql:host=' . $host . ';port=' . $port . ';charset=utf8', false, false, [PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8']); |
||||
$this->_sphinx->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); |
||||
$this->_sphinx->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ); |
||||
} |
||||
|
||||
public function searchMagnetsTotal(string $keyword, string $mode = 'default', array $stopWords = []) : int |
||||
{ |
||||
$query = $this->_sphinx->prepare('SELECT COUNT(*) AS `total` FROM `magnet` WHERE MATCH(?)'); |
||||
|
||||
$query->execute( |
||||
[ |
||||
self::_match($keyword, $mode, $stopWords) |
||||
] |
||||
); |
||||
|
||||
return $query->fetch()->total; |
||||
} |
||||
|
||||
public function searchMagnets(string $keyword, int $start, int $limit, int $maxMatches, string $mode = 'default', array $stopWords = []) |
||||
{ |
||||
$query = $this->_sphinx->prepare("SELECT * |
||||
|
||||
FROM `magnet` |
||||
|
||||
WHERE MATCH(?) |
||||
|
||||
ORDER BY `magnetId` DESC, WEIGHT() DESC |
||||
|
||||
LIMIT " . (int) ($start >= $maxMatches ? ($maxMatches > 0 ? $maxMatches - 1 : 0) : $start) . "," . (int) $limit . " |
||||
|
||||
OPTION `max_matches`=" . (int) ($maxMatches >= 1 ? $maxMatches : 1)); |
||||
|
||||
$query->execute( |
||||
[ |
||||
self::_match($keyword, $mode, $stopWords) |
||||
] |
||||
); |
||||
|
||||
return $query->fetchAll(); |
||||
} |
||||
|
||||
private static function _match(string $keyword, string $mode = 'default', array $stopWords = []) : string |
||||
{ |
||||
$keyword = trim($keyword); |
||||
|
||||
if (empty($keyword)) |
||||
{ |
||||
return $keyword; |
||||
} |
||||
|
||||
$keyword = str_replace(['"'], ' ', $keyword); |
||||
$keyword = preg_replace('/[\W]/ui', ' ', $keyword); |
||||
$keyword = preg_replace('/[\s]+/ui', ' ', $keyword); |
||||
$keyword = trim($keyword); |
||||
|
||||
switch ($mode) |
||||
{ |
||||
case 'similar': |
||||
|
||||
$result = []; |
||||
|
||||
$keyword = preg_replace('/[\d]/ui', ' ', $keyword); |
||||
$keyword = preg_replace('/[\s]+/ui', ' ', $keyword); |
||||
$keyword = trim($keyword); |
||||
|
||||
foreach ((array) explode(' ', $keyword) as $value) |
||||
{ |
||||
if (mb_strlen($value) > 5) |
||||
{ |
||||
if (!in_array(mb_strtolower($value), array_map('strtolower', $stopWords))) |
||||
{ |
||||
$result[] = sprintf('@title "%s" | @dn "%s"', $value, $value); |
||||
} |
||||
} |
||||
} |
||||
|
||||
if (empty($result)) |
||||
{ |
||||
return '*'; |
||||
} |
||||
else |
||||
{ |
||||
return implode(' | ', $result); |
||||
} |
||||
|
||||
break; |
||||
|
||||
default: |
||||
|
||||
$result = []; |
||||
|
||||
foreach ((array) explode(' ', $keyword) as $value) |
||||
{ |
||||
if (!in_array(mb_strtolower($value), $stopWords)) |
||||
{ |
||||
$result[] = sprintf('@"*%s*"', $value); |
||||
} |
||||
} |
||||
|
||||
return implode(' | ', $result); |
||||
} |
||||
} |
||||
} |
@ -1,46 +0,0 @@
@@ -1,46 +0,0 @@
|
||||
<?php |
||||
|
||||
class AppModelWebsite |
||||
{ |
||||
private $_config; |
||||
|
||||
public function __construct(object $config) |
||||
{ |
||||
$this->_config = $config; |
||||
} |
||||
|
||||
public function getConfig() : object |
||||
{ |
||||
return $this->_config->name; |
||||
} |
||||
|
||||
public function getName() : string |
||||
{ |
||||
return $this->_config->name; |
||||
} |
||||
|
||||
public function getUrl() : string |
||||
{ |
||||
return $this->_config->url; |
||||
} |
||||
|
||||
public function getDefaultLocale() : string |
||||
{ |
||||
return $this->_config->default->locale; |
||||
} |
||||
|
||||
public function getDefaultUserStatus() : bool |
||||
{ |
||||
return $this->_config->default->user->status; |
||||
} |
||||
|
||||
public function getDefaultUserApproved() : bool |
||||
{ |
||||
return $this->_config->default->user->approved; |
||||
} |
||||
|
||||
public function getApiExportEnabled() : bool |
||||
{ |
||||
return $this->_config->api->export->enabled; |
||||
} |
||||
} |
@ -1,32 +0,0 @@
@@ -1,32 +0,0 @@
|
||||
<!DOCTYPE html> |
||||
<html> |
||||
<?php $appControllerModuleHead->render() ?> |
||||
<body> |
||||
<?php $appControllerModuleHeader->render() ?> |
||||
<main> |
||||
<div class="container"> |
||||
<div class="row"> |
||||
<div class="column width-100"> |
||||
<?php $appControllerModuleProfile->render() ?> |
||||
<?php if ($pages) { ?> |
||||
<?php foreach ($pages as $page) { ?> |
||||
<?php $appControllerModulePage->render($page->pageId) ?> |
||||
<?php } ?> |
||||
<?php $appControllerModulePagination->render() ?> |
||||
<?php } else { ?> |
||||
<div class="padding-16 margin-y-8 border-radius-3 background-color-night text-center"> |
||||
<h1 class="margin-b-8"> |
||||
<?php echo _('Nothing found') ?> |
||||
</h1> |
||||
<div class="text-color-night"> |
||||
<?php echo _('* share your magnet links to change it') ?> |
||||
</div> |
||||
</div> |
||||
<?php } ?> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</main> |
||||
<?php $appControllerModuleFooter->render() ?> |
||||
</body> |
||||
</html> |
@ -1,27 +0,0 @@
@@ -1,27 +0,0 @@
|
||||
<footer> |
||||
<div class="container"> |
||||
<div class="row"> |
||||
<div class="column width-100 text-center margin-y-8"> |
||||
<?php foreach ($trackers as $i => $tracker) { ?> |
||||
<a href="<?php echo $tracker->announce ?>"><?php echo sprintf('Tracker %s', $i + 1) ?></a> |
||||
/ |
||||
<a href="<?php echo $tracker->stats ?>"><?php echo _('Stats') ?></a> |
||||
| |
||||
<?php } ?> |
||||
<a href="faq"><?php echo _('F.A.Q') ?></a> |
||||
| |
||||
<a href="node"><?php echo _('Node') ?></a> |
||||
| |
||||
<a rel="nofollow" href="rss"><?php echo _('RSS') ?></a> |
||||
<?php if ($api) { ?> |
||||
| |
||||
<a rel="nofollow" href="api/manifest.json"><?php echo _('API') ?></a> |
||||
<?php } ?> |
||||
| |
||||
<a href="https://github.com/YGGverse/YGGtracker"><?php echo _('GitHub') ?></a> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</footer> |
||||
</body> |
||||
</html> |
@ -1,7 +0,0 @@
@@ -1,7 +0,0 @@
|
||||
<head> |
||||
<base href="<?php echo $base ?>" /> |
||||
<title><?php echo $title ?></title> |
||||
<?php foreach ($links as $link) { ?> |
||||
<link rel="<?php echo $link->rel ?>" type="<?php echo $link->type ?>" href="<?php echo $link->href ?>" /> |
||||
<?php } ?> |
||||
</head> |
@ -1,10 +0,0 @@
@@ -1,10 +0,0 @@
|
||||
<header> |
||||
<div class="container"> |
||||
<div class="row margin-t-8 text-center"> |
||||
<a class="logo" href=""> |
||||
<?php echo $name ?> |
||||
</a> |
||||
<?php $appControllerModuleSearch->render() ?> |
||||
</div> |
||||
</div> |
||||
</header> |
@ -1,126 +0,0 @@
@@ -1,126 +0,0 @@
|
||||
<a name="magnet-<?php echo $pageId ?>"></a> |
||||
<div class="margin-y-8 border-radius-3 background-color-night <?php echo !$approved ? 'opacity-06 opacity-hover-1' : false ?>"> |
||||
<div class="padding-16 <?php echo $sensitive ? 'blur-2 blur-hover-0' : false ?>"> |
||||
<a href="<?php echo sprintf('%s/magnet.php?magnetId=%s', WEBSITE_URL, $pageId) ?>"> |
||||
<h2 class="margin-b-8"><?php echo $title ?></h2> |
||||
<?php if ($leechers && !$seeders) { ?> |
||||
<span class="label label-green margin-x-4 font-size-10 position-relative top--2 cursor-default" |
||||
title="<?php echo _('Active leechers waiting for seeds') ?>"> |
||||
<?php echo _('wanted') ?> |
||||
</span> |
||||
<?php } ?> |
||||
</a> |
||||
<div class="float-right opacity-0 parent-hover-opacity-09"> |
||||
<?php if (!$approved) { ?> |
||||
<span class="margin-l-8" title="<?php echo _('Waiting for approve') ?>"> |
||||
<svg class="width-13px" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-hourglass-split" viewBox="0 0 16 16"> |
||||
<path d="M2.5 15a.5.5 0 1 1 0-1h1v-1a4.5 4.5 0 0 1 2.557-4.06c.29-.139.443-.377.443-.59v-.7c0-.213-.154-.451-.443-.59A4.5 4.5 0 0 1 3.5 3V2h-1a.5.5 0 0 1 0-1h11a.5.5 0 0 1 0 1h-1v1a4.5 4.5 0 0 1-2.557 4.06c-.29.139-.443.377-.443.59v.7c0 .213.154.451.443.59A4.5 4.5 0 0 1 12.5 13v1h1a.5.5 0 0 1 0 1h-11zm2-13v1c0 .537.12 1.045.337 1.5h6.326c.216-.455.337-.963.337-1.5V2h-7zm3 6.35c0 .701-.478 1.236-1.011 1.492A3.5 3.5 0 0 0 4.5 13s.866-1.299 3-1.48V8.35zm1 0v3.17c2.134.181 3 1.48 3 1.48a3.5 3.5 0 0 0-1.989-3.158C8.978 9.586 8.5 9.052 8.5 8.351z"/> |
||||
</svg> |
||||
</span> |
||||
<?php } ?> |
||||
<a class="text-color-green margin-l-12" href="<?php echo WEBSITE_URL ?>/edit.php?magnetId=<?php echo $magnet->magnetId ?>" title="<?php echo _('Edit') ?>"> |
||||
<svg class="text-color-green" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pencil-square" viewBox="0 0 16 16"> |
||||
<path d="M15.502 1.94a.5.5 0 0 1 0 .706L14.459 3.69l-2-2L13.502.646a.5.5 0 0 1 .707 0l1.293 1.293zm-1.75 2.456-2-2L4.939 9.21a.5.5 0 0 0-.121.196l-.805 2.414a.25.25 0 0 0 .316.316l2.414-.805a.5.5 0 0 0 .196-.12l6.813-6.814z"/> |
||||
<path fill-rule="evenodd" d="M1 13.5A1.5 1.5 0 0 0 2.5 15h11a1.5 1.5 0 0 0 1.5-1.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5v-11a.5.5 0 0 1 .5-.5H9a.5.5 0 0 0 0-1H2.5A1.5 1.5 0 0 0 1 2.5v11z"/> |
||||
</svg> |
||||
</a> |
||||
</div> |
||||
<?php if ($magnet->preview) { ?> |
||||
<div class="margin-y-8"><?php echo $magnet->preview ?></div> |
||||
<?php } ?> |
||||
<?php if ($magnet->keywords) { ?> |
||||
<div class="margin-y-8"> |
||||
<?php foreach ($magnet->keywords as $keyword) { ?> |
||||
<small> |
||||
<a href="<?php echo WEBSITE_URL ?>/search.php?query=<?php echo urlencode($keyword) ?>">#<?php echo htmlentities($keyword) ?></a> |
||||
</small> |
||||
<?php } ?> |
||||
</div> |
||||
<?php } ?> |
||||
<div class="width-100 padding-y-4"></div> |
||||
<span class="margin-t-8 margin-r-8 cursor-default"> |
||||
<sup> |
||||
<?php echo $magnet->timeUpdated ? _('Updated') : _('Added') ?> |
||||
<?php echo $magnet->timeUpdated ? $magnet->timeUpdated : $magnet->timeAdded ?> |
||||
</sup> |
||||
</span> |
||||
<span class="margin-t-8 margin-r-8 cursor-default opacity-0 parent-hover-opacity-09" title="<?php echo _('Seeds') ?>"> |
||||
<svg class="width-13px" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-up" viewBox="0 0 16 16"> |
||||
<path fill-rule="evenodd" d="M8 15a.5.5 0 0 0 .5-.5V2.707l3.146 3.147a.5.5 0 0 0 .708-.708l-4-4a.5.5 0 0 0-.708 0l-4 4a.5.5 0 1 0 .708.708L7.5 2.707V14.5a.5.5 0 0 0 .5.5z"/> |
||||
</svg> |
||||
<sup><?php echo $magnet->seeders ?></sup> |
||||
</span> |
||||
<span class="margin-t-8 margin-r-8 cursor-default opacity-0 parent-hover-opacity-09" title="<?php echo _('Peers') ?>"> |
||||
<svg class="width-13px" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-down" viewBox="0 0 16 16"> |
||||
<path fill-rule="evenodd" d="M8 1a.5.5 0 0 1 .5.5v11.793l3.146-3.147a.5.5 0 0 1 .708.708l-4 4a.5.5 0 0 1-.708 0l-4-4a.5.5 0 0 1 .708-.708L7.5 13.293V1.5A.5.5 0 0 1 8 1z"/> |
||||
</svg> |
||||
<sup><?php echo $magnet->completed ?></sup> |
||||
</span> |
||||
<span class="margin-t-8 margin-r-8 cursor-default opacity-0 parent-hover-opacity-09" title="<?php echo _('Leechers') ?>"> |
||||
<svg class="width-13px" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-cup-hot" viewBox="0 0 16 16"> |
||||
<path fill-rule="evenodd" d="M.5 6a.5.5 0 0 0-.488.608l1.652 7.434A2.5 2.5 0 0 0 4.104 16h5.792a2.5 2.5 0 0 0 2.44-1.958l.131-.59a3 3 0 0 0 1.3-5.854l.221-.99A.5.5 0 0 0 13.5 6H.5ZM13 12.5a2.01 2.01 0 0 1-.316-.025l.867-3.898A2.001 2.001 0 0 1 13 12.5ZM2.64 13.825 1.123 7h11.754l-1.517 6.825A1.5 1.5 0 0 1 9.896 15H4.104a1.5 1.5 0 0 1-1.464-1.175Z"/> |
||||
<path d="m4.4.8-.003.004-.014.019a4.167 4.167 0 0 0-.204.31 2.327 2.327 0 0 0-.141.267c-.026.06-.034.092-.037.103v.004a.593.593 0 0 0 .091.248c.075.133.178.272.308.445l.01.012c.118.158.26.347.37.543.112.2.22.455.22.745 0 .188-.065.368-.119.494a3.31 3.31 0 0 1-.202.388 5.444 5.444 0 0 1-.253.382l-.018.025-.005.008-.002.002A.5.5 0 0 1 3.6 4.2l.003-.004.014-.019a4.149 4.149 0 0 0 .204-.31 2.06 2.06 0 0 0 .141-.267c.026-.06.034-.092.037-.103a.593.593 0 0 0-.09-.252A4.334 4.334 0 0 0 3.6 2.8l-.01-.012a5.099 5.099 0 0 1-.37-.543A1.53 1.53 0 0 1 3 1.5c0-.188.065-.368.119-.494.059-.138.134-.274.202-.388a5.446 5.446 0 0 1 .253-.382l.025-.035A.5.5 0 0 1 4.4.8Zm3 0-.003.004-.014.019a4.167 4.167 0 0 0-.204.31 2.327 2.327 0 0 0-.141.267c-.026.06-.034.092-.037.103v.004a.593.593 0 0 0 .091.248c.075.133.178.272.308.445l.01.012c.118.158.26.347.37.543.112.2.22.455.22.745 0 .188-.065.368-.119.494a3.31 3.31 0 0 1-.202.388 5.444 5.444 0 0 1-.253.382l-.018.025-.005.008-.002.002A.5.5 0 0 1 6.6 4.2l.003-.004.014-.019a4.149 4.149 0 0 0 .204-.31 2.06 2.06 0 0 0 .141-.267c.026-.06.034-.092.037-.103a.593.593 0 0 0-.09-.252A4.334 4.334 0 0 0 6.6 2.8l-.01-.012a5.099 5.099 0 0 1-.37-.543A1.53 1.53 0 0 1 6 1.5c0-.188.065-.368.119-.494.059-.138.134-.274.202-.388a5.446 5.446 0 0 1 .253-.382l.025-.035A.5.5 0 0 1 7.4.8Zm3 0-.003.004-.014.019a4.077 4.077 0 0 0-.204.31 2.337 2.337 0 0 0-.141.267c-.026.06-.034.092-.037.103v.004a.593.593 0 0 0 .091.248c.075.133.178.272.308.445l.01.012c.118.158.26.347.37.543.112.2.22.455.22.745 0 .188-.065.368-.119.494a3.198 3.198 0 0 1-.202.388 5.385 5.385 0 0 1-.252.382l-.019.025-.005.008-.002.002A.5.5 0 0 1 9.6 4.2l.003-.004.014-.019a4.149 4.149 0 0 0 .204-.31 2.06 2.06 0 0 0 .141-.267c.026-.06.034-.092.037-.103a.593.593 0 0 0-.09-.252A4.334 4.334 0 0 0 9.6 2.8l-.01-.012a5.099 5.099 0 0 1-.37-.543A1.53 1.53 0 0 1 9 1.5c0-.188.065-.368.119-.494.059-.138.134-.274.202-.388a5.446 5.446 0 0 1 .253-.382l.025-.035A.5.5 0 0 1 10.4.8Z"/> |
||||
</svg> |
||||
<sup><?php echo $magnet->leechers ?></sup> |
||||
</span> |
||||
<?php if ($magnet->directs) { ?> |
||||
<span class="margin-t-8 margin-r-8 cursor-default opacity-0 parent-hover-opacity-09" title="<?php echo _('Direct') ?>"> |
||||
<svg class="width-13px" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-database" viewBox="0 0 16 16"> |
||||
<path d="M4.318 2.687C5.234 2.271 6.536 2 8 2s2.766.27 3.682.687C12.644 3.125 13 3.627 13 4c0 .374-.356.875-1.318 1.313C10.766 5.729 9.464 6 8 6s-2.766-.27-3.682-.687C3.356 4.875 3 4.373 3 4c0-.374.356-.875 1.318-1.313ZM13 5.698V7c0 .374-.356.875-1.318 1.313C10.766 8.729 9.464 9 8 9s-2.766-.27-3.682-.687C3.356 7.875 3 7.373 3 7V5.698c.271.202.58.378.904.525C4.978 6.711 6.427 7 8 7s3.022-.289 4.096-.777A4.92 4.92 0 0 0 13 5.698ZM14 4c0-1.007-.875-1.755-1.904-2.223C11.022 1.289 9.573 1 8 1s-3.022.289-4.096.777C2.875 2.245 2 2.993 2 4v9c0 1.007.875 1.755 1.904 2.223C4.978 15.71 6.427 16 8 16s3.022-.289 4.096-.777C13.125 14.755 14 14.007 14 13V4Zm-1 4.698V10c0 .374-.356.875-1.318 1.313C10.766 11.729 9.464 12 8 12s-2.766-.27-3.682-.687C3.356 10.875 3 10.373 3 10V8.698c.271.202.58.378.904.525C4.978 9.71 6.427 10 8 10s3.022-.289 4.096-.777A4.92 4.92 0 0 0 13 8.698Zm0 3V13c0 .374-.356.875-1.318 1.313C10.766 14.729 9.464 15 8 15s-2.766-.27-3.682-.687C3.356 13.875 3 13.373 3 13v-1.302c.271.202.58.378.904.525C4.978 12.71 6.427 13 8 13s3.022-.289 4.096-.777c.324-.147.633-.323.904-.525Z"/> |
||||
</svg> |
||||
<sup><?php echo $magnet->directs ?></sup> |
||||
</span> |
||||
<?php } ?> |
||||
<span class="float-right margin-l-12"> |
||||
<a rel="nofollow" href="<?php echo sprintf('%s/action.php?target=magnet&toggle=star&magnetId=%s&callback=%s', |
||||
WEBSITE_URL, |
||||
$magnet->magnetId, |
||||
base64_encode(sprintf('%s/search.php?%s#magnet-%s', |
||||
WEBSITE_URL, |
||||
($request->query ? sprintf('&query=%s', urlencode($request->query)) : false). |
||||
($request->page ? sprintf('&page=%s', urlencode($request->page)) : false), |
||||
$magnet->magnetId))) ?>" title="<?php echo _('Star') ?>"> |
||||
<?php if ($magnet->star->status) { ?> |
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-star-fill" viewBox="0 0 16 16"> |
||||
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/> |
||||
</svg> |
||||
<?php } else { ?> |
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-star" viewBox="0 0 16 16"> |
||||
<path d="M2.866 14.85c-.078.444.36.791.746.593l4.39-2.256 4.389 2.256c.386.198.824-.149.746-.592l-.83-4.73 3.522-3.356c.33-.314.16-.888-.282-.95l-4.898-.696L8.465.792a.513.513 0 0 0-.927 0L5.354 5.12l-4.898.696c-.441.062-.612.636-.283.95l3.523 3.356-.83 4.73zm4.905-2.767-3.686 1.894.694-3.957a.565.565 0 0 0-.163-.505L1.71 6.745l4.052-.576a.525.525 0 0 0 .393-.288L8 2.223l1.847 3.658a.525.525 0 0 0 .393.288l4.052.575-2.906 2.77a.565.565 0 0 0-.163.506l.694 3.957-3.686-1.894a.503.503 0 0 0-.461 0z"/> |
||||
</svg> |
||||
<?php } ?> |
||||
</a> |
||||
<sup><?php echo $magnet->star->total ?></sup> |
||||
</span> |
||||
<?php if ($magnet->comments) { ?> |
||||
<span class="float-right margin-l-12"> |
||||
<a rel="nofollow" href="<?php echo WEBSITE_URL ?>/magnet.php?magnetId=<?php echo $magnet->magnetId ?>#comment" title="<?php echo _('Comment') ?>"> |
||||
<?php if ($magnet->comment->status) { ?> |
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-chat-fill" viewBox="0 0 16 16"> |
||||
<path d="M8 15c4.418 0 8-3.134 8-7s-3.582-7-8-7-8 3.134-8 7c0 1.76.743 3.37 1.97 4.6-.097 1.016-.417 2.13-.771 2.966-.079.186.074.394.273.362 2.256-.37 3.597-.938 4.18-1.234A9.06 9.06 0 0 0 8 15z"/> |
||||
</svg> |
||||
<?php } else { ?> |
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-chat" viewBox="0 0 16 16"> |
||||
<path d="M2.678 11.894a1 1 0 0 1 .287.801 10.97 10.97 0 0 1-.398 2c1.395-.323 2.247-.697 2.634-.893a1 1 0 0 1 .71-.074A8.06 8.06 0 0 0 8 14c3.996 0 7-2.807 7-6 0-3.192-3.004-6-7-6S1 4.808 1 8c0 1.468.617 2.83 1.678 3.894zm-.493 3.905a21.682 21.682 0 0 1-.713.129c-.2.032-.352-.176-.273-.362a9.68 9.68 0 0 0 .244-.637l.003-.01c.248-.72.45-1.548.524-2.319C.743 11.37 0 9.76 0 8c0-3.866 3.582-7 8-7s8 3.134 8 7-3.582 7-8 7a9.06 9.06 0 0 1-2.347-.306c-.52.263-1.639.742-3.468 1.105z"/> |
||||
</svg> |
||||
<?php } ?> |
||||
</a> |
||||
<sup><?php echo $magnet->comment->total ?></sup> |
||||
</span> |
||||
<?php } ?> |
||||
<span class="float-right margin-l-12"> |
||||
<a rel="nofollow" href="<?php echo WEBSITE_URL ?>/download.php?magnetId=<?php echo $magnet->magnetId ?>" title="<?php echo _('Download') ?>"> |
||||
<?php if ($magnet->download->status) { ?> |
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-down-circle-fill" viewBox="0 0 16 16"> |
||||
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM8.5 4.5a.5.5 0 0 0-1 0v5.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V4.5z"/> |
||||
</svg> |
||||
<?php } else { ?> |
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-down-circle" viewBox="0 0 16 16"> |
||||
<path fill-rule="evenodd" d="M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8zm15 0A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM8.5 4.5a.5.5 0 0 0-1 0v5.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V4.5z"/> |
||||
</svg> |
||||
<?php } ?> |
||||
</a> |
||||
<sup><?php echo $magnet->download->total ?></sup> |
||||
</span> |
||||
</div> |
||||
</div> |
@ -1,15 +0,0 @@
@@ -1,15 +0,0 @@
|
||||
<div class="row"> |
||||
<div class="column width-100 text-right"> |
||||
<?php echo sprintf(_('page %s / %s'), $pagination->page, $pagination->total) ?> |
||||
<?php if ($pagination->back) { ?> |
||||
<a class="button margin-l-8" rel="nofollow" href="<?php echo $pagination->back ?>"> |
||||
<?php echo _('back') ?> |
||||
</a> |
||||
<?php } ?> |
||||
<?php if ($pagination->next) { ?> |
||||
<a class="button margin-l-4" rel="nofollow" href="<?php echo $pagination->next ?>"> |
||||
<?php echo _('next') ?> |
||||
</a> |
||||
<?php } ?> |
||||
</div> |
||||
</div> |
@ -1,20 +0,0 @@
@@ -1,20 +0,0 @@
|
||||
<form class="margin-t-8" name="search" method="get" action="search"> |
||||
<input class="min-width-200-px" type="text" name="query" value="<?php echo $query ?>" placeholder="<?php echo _('Keyword, file, extension, hash...') ?>" /> |
||||
<select class="min-width-120-px" type="text" name="locale"> |
||||
<option value=""> |
||||
<?php echo _('All languages') ?> |
||||
</option> |
||||
<?php foreach ($locales as $locale) { ?> |
||||
<?php if ($locale->active) { ?> |
||||
<option value="<?php echo $locale->key ?>" selected="selected"> |
||||
<?php echo $locale->value ?> |
||||
</option> |
||||
<?php } else { ?> |
||||
<option value="<?php echo $locale->key ?>"> |
||||
<?php echo $locale->value ?> |
||||
</option> |
||||
<?php } ?> |
||||
<?php } ?> |
||||
</select> |
||||
<input type="submit" value="<?php echo _('Search') ?>" /> |
||||
</form> |
@ -1,140 +0,0 @@
@@ -1,140 +0,0 @@
|
||||
<!DOCTYPE html> |
||||
<html> |
||||
<?php $appControllerModuleHead->render() ?> |
||||
<body> |
||||
<?php $appControllerModuleHeader->render() ?> |
||||
<main> |
||||
<div class="container"> |
||||
<div class="row"> |
||||
<div class="column width-100"> |
||||
<?php $appControllerModuleProfile->render() ?> |
||||
<div class="padding-16 margin-y-8 border-radius-3 background-color-night"> |
||||
<div class="margin-b-24 padding-b-16 border-bottom-default"> |
||||
<h1><?php echo _('Submit') ?></h1> |
||||
</div> |
||||
<form class="margin-t-8" name="submit" method="post" enctype="multipart/form-data" action="submit"> |
||||
<input type="hidden" name="pageId" value="<?php echo $form->pageId->attribute->value ?>" /> |
||||
<div class="margin-b-16"> |
||||
<label for="locale"> |
||||
<?php echo _('Content language') ?> |
||||
</label> |
||||
<sub class="opacity-0 parent-hover-opacity-09" |
||||
title="<?php echo $form->locale->placeholder ?>"> |
||||
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" fill="currentColor" class="bi bi-info-circle-fill" viewBox="0 0 16 16"> |
||||
<path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm.93-9.412-1 4.705c-.07.34.029.533.304.533.194 0 .487-.07.686-.246l-.088.416c-.287.346-.92.598-1.465.598-.703 0-1.002-.422-.808-1.319l.738-3.468c.064-.293.006-.399-.287-.47l-.451-.081.082-.381 2.29-.287zM8 5.5a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/> |
||||
</svg> |
||||
</sub> |
||||
<select class="width-100 margin-t-8" type="text" name="locale" id="locale"> |
||||
<?php foreach ($form->locale->options as $locale) { ?> |
||||
<?php if ($locale->active) { ?> |
||||
<option value="<?php echo $locale->code ?>" selected="selected"> |
||||
<?php echo $locale->value ?> |
||||
</option> |
||||
<?php } else { ?> |
||||
<option value="<?php echo $locale->code ?>"> |
||||
<?php echo $locale->value ?> |
||||
</option> |
||||
<?php } ?> |
||||
<?php } ?> |
||||
</select> |
||||
</div> |
||||
<div class="margin-b-16"> |
||||
<label for="title"> |
||||
<?php echo _('Title') ?> |
||||
</label> |
||||
<sub class="opacity-0 parent-hover-opacity-09" |
||||
title="<?php echo $form->title->attribute->placeholder ?>"> |
||||
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" fill="currentColor" class="bi bi-info-circle-fill" viewBox="0 0 16 16"> |
||||
<path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm.93-9.412-1 4.705c-.07.34.029.533.304.533.194 0 .487-.07.686-.246l-.088.416c-.287.346-.92.598-1.465.598-.703 0-1.002-.422-.808-1.319l.738-3.468c.064-.293.006-.399-.287-.47l-.451-.081.082-.381 2.29-.287zM8 5.5a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/> |
||||
</svg> |
||||
</sub> |
||||
<?php foreach ($form->title->error as $errors) { ?> |
||||
<?php foreach ($errors as $error) { ?> |
||||
<div class="text-color-red margin-y-8"> |
||||
<?php echo $error ?> |
||||
</div> |
||||
<?php } ?> |
||||
<?php } ?> |
||||
<input class="width-100 margin-t-8" |
||||
type="text" |
||||
name="title" |
||||
id="title" |
||||
<?php echo $form->title->attribute->required ? 'required="required"' : false ?> |
||||
value="<?php echo $form->title->attribute->value ?>" |
||||
placeholder="<?php echo $form->title->attribute->placeholder ?>" |
||||
minlength="<?php echo $form->title->attribute->minlength ?>" |
||||
maxlength="<?php echo $form->title->attribute->maxlength ?>" /> |
||||
</div> |
||||
<div class="margin-y-8 padding-t-4"> |
||||
<label for="description"> |
||||
<?php echo _('Description') ?> |
||||
</label> |
||||
<sub class="opacity-0 parent-hover-opacity-09" |
||||
title="<?php echo $form->description->attribute->placeholder ?>"> |
||||
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" fill="currentColor" class="bi bi-info-circle-fill" viewBox="0 0 16 16"> |
||||
<path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm.93-9.412-1 4.705c-.07.34.029.533.304.533.194 0 .487-.07.686-.246l-.088.416c-.287.346-.92.598-1.465.598-.703 0-1.002-.422-.808-1.319l.738-3.468c.064-.293.006-.399-.287-.47l-.451-.081.082-.381 2.29-.287zM8 5.5a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/> |
||||
</svg> |
||||
</sub> |
||||
<?php foreach ($form->description->error as $errors) { ?> |
||||
<?php foreach ($errors as $error) { ?> |
||||
<div class="text-color-red margin-y-8"> |
||||
<?php echo $error ?> |
||||
</div> |
||||
<?php } ?> |
||||
<?php } ?> |
||||
<textarea class="width-100 margin-t-8" |
||||
name="description" |
||||
id="description" |
||||
<?php echo $form->description->attribute->required ? 'required="required"' : false ?> |
||||
placeholder="<?php echo $form->description->attribute->placeholder ?>" |
||||
minlength="<?php echo $form->description->attribute->minlength ?>" |
||||
maxlength="<?php echo $form->description->attribute->maxlength ?>"><?php echo $form->description->attribute->value ?></textarea> |
||||
</div> |
||||
<div class="margin-y-8 padding-t-4"> |
||||
<label for="keywords"> |
||||
<?php echo _('Keywords') ?> |
||||
</label> |
||||
<sub class="opacity-0 parent-hover-opacity-09" |
||||
title="<?php echo $form->keywords->attribute->placeholder ?>"> |
||||
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" fill="currentColor" class="bi bi-info-circle-fill" viewBox="0 0 16 16"> |
||||
<path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm.93-9.412-1 4.705c-.07.34.029.533.304.533.194 0 .487-.07.686-.246l-.088.416c-.287.346-.92.598-1.465.598-.703 0-1.002-.422-.808-1.319l.738-3.468c.064-.293.006-.399-.287-.47l-.451-.081.082-.381 2.29-.287zM8 5.5a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/> |
||||
</svg> |
||||
</sub> |
||||
<?php foreach ($form->keywords->error as $errors) { ?> |
||||
<?php foreach ($errors as $error) { ?> |
||||
<div class="text-color-red margin-y-8"> |
||||
<?php echo $error ?> |
||||
</div> |
||||
<?php } ?> |
||||
<?php } ?> |
||||
<textarea class="width-100 margin-t-8" |
||||
name="keywords" |
||||
<?php echo $form->keywords->attribute->required ? 'required="required"' : false ?> |
||||
placeholder="<?php echo $form->keywords->attribute->placeholder ?>" |
||||
minlength="<?php echo $form->keywords->attribute->minlength ?>" |
||||
maxlength="<?php echo $form->keywords->attribute->maxlength ?>"><?php echo $form->keywords->attribute->value ?></textarea> |
||||
</div> |
||||
<div class="margin-y-16"> |
||||
<input type="checkbox" name="sensitive" id="sensitive" value="true" <?php echo $form->sensitive->attribute->value ? 'checked="checked"' : false ?> /> |
||||
<label for="sensitive"> |
||||
<?php echo _('Sensitive') ?> |
||||
</label> |
||||
<sub class="opacity-0 parent-hover-opacity-09" |
||||
title="<?php echo $form->sensitive->attribute->placeholder ?>"> |
||||
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" fill="currentColor" class="bi bi-info-circle-fill" viewBox="0 0 16 16"> |
||||
<path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm.93-9.412-1 4.705c-.07.34.029.533.304.533.194 0 .487-.07.686-.246l-.088.416c-.287.346-.92.598-1.465.598-.703 0-1.002-.422-.808-1.319l.738-3.468c.064-.293.006-.399-.287-.47l-.451-.081.082-.381 2.29-.287zM8 5.5a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/> |
||||
</svg> |
||||
</sub> |
||||
</div> |
||||
<div class="text-right"> |
||||
<input class="button-green" type="submit" value="<?php echo _('Submit') ?>" /> |
||||
</div> |
||||
</form> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</main> |
||||
<?php $appControllerModuleFooter->render() ?> |
||||
</body> |
||||
</html> |
@ -1,24 +0,0 @@
@@ -1,24 +0,0 @@
|
||||
<!DOCTYPE html> |
||||
<html> |
||||
<?php $appControllerModuleHead->render() ?> |
||||
<body> |
||||
<?php $appControllerModuleHeader->render() ?> |
||||
<main> |
||||
<div class="container"> |
||||
<div class="row"> |
||||
<div class="column width-100"> |
||||
<div class="padding-16 margin-y-8 border-radius-3 background-color-night text-center"> |
||||
<h1 class="margin-b-8"> |
||||
<?php echo $h1 ?> |
||||
</h1> |
||||
<div> |
||||
<?php echo $text ?> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</main> |
||||
<?php $appControllerModuleFooter->render() ?> |
||||
</body> |
||||
</html> |
@ -1,160 +0,0 @@
@@ -1,160 +0,0 @@
|
||||
<?php |
||||
|
||||
// PHP |
||||
declare(strict_types=1); |
||||
|
||||
// Debug |
||||
ini_set('display_errors', '1'); |
||||
ini_set('display_startup_errors', '1'); |
||||
error_reporting(E_ALL); |
||||
|
||||
// Application |
||||
define('APP_VERSION', '2.0.0'); |
||||
define('API_VERSION', APP_VERSION); |
||||
define('CSS_VERSION', APP_VERSION); |
||||
|
||||
// Environment |
||||
require_once __DIR__ . '/../library/environment.php'; |
||||
|
||||
// Autoload |
||||
require_once __DIR__ . '/../../vendor/autoload.php'; |
||||
|
||||
// Route |
||||
parse_str($_SERVER['QUERY_STRING'], $request); |
||||
|
||||
if (isset($request['_route_'])) |
||||
{ |
||||
switch ($request['_route_']) |
||||
{ |
||||
case 'stars': |
||||
|
||||
require_once __DIR__ . '/../app/controller/stars.php'; |
||||
|
||||
$appControllerStars = new AppControllerStars(); |
||||
|
||||
$appControllerStars->render(); |
||||
|
||||
break; |
||||
|
||||
case 'views': |
||||
|
||||
require_once __DIR__ . '/../app/controller/views.php'; |
||||
|
||||
$appControllerViews = new AppControllerViews(); |
||||
|
||||
$appControllerViews->render(); |
||||
|
||||
break; |
||||
|
||||
case 'downloads': |
||||
|
||||
require_once __DIR__ . '/../app/controller/downloads.php'; |
||||
|
||||
$appControllerDownloads = new AppControllerDownloads(); |
||||
|
||||
$appControllerDownloads->render(); |
||||
|
||||
break; |
||||
|
||||
case 'comments': |
||||
|
||||
require_once __DIR__ . '/../app/controller/comments.php'; |
||||
|
||||
$appControllerComments = new AppControllerComments(); |
||||
|
||||
$appControllerComments->render(); |
||||
|
||||
break; |
||||
|
||||
case 'editions': |
||||
|
||||
require_once __DIR__ . '/../app/controller/editions.php'; |
||||
|
||||
$appControllerEditions = new AppControllerEditions(); |
||||
|
||||
$appControllerEditions->render(); |
||||
|
||||
break; |
||||
|
||||
case 'submit': |
||||
|
||||
require_once __DIR__ . '/../app/model/database.php'; |
||||
require_once __DIR__ . '/../app/model/validator.php'; |
||||
require_once __DIR__ . '/../app/model/locale.php'; |
||||
require_once __DIR__ . '/../app/model/website.php'; |
||||
require_once __DIR__ . '/../app/model/session.php'; |
||||
require_once __DIR__ . '/../app/model/request.php'; |
||||
|
||||
require_once __DIR__ . '/../app/controller/page.php'; |
||||
|
||||
$appControllerPage = new AppControllerPage( |
||||
new AppModelDatabase( |
||||
Environment::config('database') |
||||
), |
||||
new AppModelValidator( |
||||
Environment::config('validator') |
||||
), |
||||
new AppModelLocale( |
||||
Environment::config('locales') |
||||
), |
||||
new AppModelWebsite( |
||||
Environment::config('website') |
||||
), |
||||
new AppModelSession( |
||||
$_SERVER['REMOTE_ADDR'] |
||||
), |
||||
new AppModelRequest( |
||||
$_GET, |
||||
$_POST, |
||||
$_FILES |
||||
) |
||||
); |
||||
|
||||
$appControllerPage->renderFormSubmit(); |
||||
|
||||
break; |
||||
|
||||
default: |
||||
|
||||
require_once __DIR__ . '/../app/controller/response.php'; |
||||
|
||||
$appControllerResponse = new AppControllerResponse( |
||||
sprintf( |
||||
_('404 - Not found - %s'), |
||||
Environment::config('website')->name |
||||
), |
||||
_('404'), |
||||
_('Page not found'), |
||||
404 |
||||
); |
||||
|
||||
$appControllerResponse->render(); |
||||
} |
||||
} |
||||
|
||||
else |
||||
{ |
||||
require_once __DIR__ . '/../app/model/database.php'; |
||||
require_once __DIR__ . '/../app/model/validator.php'; |
||||
require_once __DIR__ . '/../app/model/website.php'; |
||||
require_once __DIR__ . '/../app/model/session.php'; |
||||
|
||||
require_once __DIR__ . '/../app/controller/index.php'; |
||||
|
||||
$appControllerIndex = new AppControllerIndex( |
||||
new AppModelDatabase( |
||||
Environment::config('database') |
||||
), |
||||
new AppModelValidator( |
||||
Environment::config('validator') |
||||
), |
||||
new AppModelWebsite( |
||||
Environment::config('website') |
||||
), |
||||
new AppModelSession( |
||||
$_SERVER['REMOTE_ADDR'] |
||||
) |
||||
); |
||||
|
||||
$appControllerIndex->render(); |
||||
} |
@ -1,7 +0,0 @@
@@ -1,7 +0,0 @@
|
||||
{ |
||||
"port":3306, |
||||
"host":"127.0.0.1", |
||||
"name":"", |
||||
"user":"", |
||||
"password":"" |
||||
} |
@ -1,6 +0,0 @@
@@ -1,6 +0,0 @@
|
||||
{ |
||||
"port": 11211, |
||||
"host": "127.0.0.1", |
||||
"namespace": "", |
||||
"timeout": 3600 |
||||
} |
@ -1,12 +0,0 @@
@@ -1,12 +0,0 @@
|
||||
[ |
||||
{ |
||||
"description":"YGGtracker instance #1 running latest stable release", |
||||
"url":"http://[201:23b4:991a:634d:8359:4521:5576:15b7]/yggtracker", |
||||
"manifest":"http://[201:23b4:991a:634d:8359:4521:5576:15b7]/yggtracker/api/manifest.json" |
||||
}, |
||||
{ |
||||
"description":"YGGtracker instance #2 running latest stable release", |
||||
"url":"http://[200:e6fd:bb3c:b354:cd3a:f939:753e:cd72]/yggtracker", |
||||
"manifest":"http://[200:e6fd:bb3c:b354:cd3a:f939:753e:cd72]/yggtracker/api/manifest.json" |
||||
} |
||||
] |
@ -1,7 +0,0 @@
@@ -1,7 +0,0 @@
|
||||
[ |
||||
{ |
||||
"description":"YGGtracker public peer instance without traffic limit", |
||||
"url":"http://[201:23b4:991a:634d:8359:4521:5576:15b7]/yggstate", |
||||
"address":"tls://94.140.114.241:4708" |
||||
} |
||||
] |
@ -1,4 +0,0 @@
@@ -1,4 +0,0 @@
|
||||
{ |
||||
"port":9306, |
||||
"host":"127.0.0.1" |
||||
} |
@ -1,23 +0,0 @@
@@ -1,23 +0,0 @@
|
||||
[ |
||||
{ |
||||
"description":"YGGtracker instance, yggdrasil-only connections", |
||||
"url":"http://[201:23b4:991a:634d:8359:4521:5576:15b7]/yggtracker", |
||||
"announce":"http://[201:23b4:991a:634d:8359:4521:5576:15b7]:2023/announce", |
||||
"stats":"http://[201:23b4:991a:634d:8359:4521:5576:15b7]:2023/stats", |
||||
"scrape":"http://[201:23b4:991a:634d:8359:4521:5576:15b7]:2023/scrape" |
||||
}, |
||||
{ |
||||
"description":"Yggdrasil-only torrent tracker, operated by jeff", |
||||
"url":false, |
||||
"announce":"http://[200:1e2f:e608:eb3a:2bf:1e62:87ba:e2f7]/announce", |
||||
"stats":"http://[200:1e2f:e608:eb3a:2bf:1e62:87ba:e2f7]/stats", |
||||
"scrape":"http://[200:1e2f:e608:eb3a:2bf:1e62:87ba:e2f7]/scrape" |
||||
}, |
||||
{ |
||||
"description":"Yggdrasil torrent tracker, operated by R4SAS", |
||||
"url":false, |
||||
"announce":"http://[316:c51a:62a3:8b9::5]/announce", |
||||
"stats":"http://[316:c51a:62a3:8b9::5]/stats", |
||||
"scrape":"http://[316:c51a:62a3:8b9::5]/scrape" |
||||
} |
||||
] |
@ -1,74 +0,0 @@
@@ -1,74 +0,0 @@
|
||||
{ |
||||
"host": |
||||
{ |
||||
"regex": "/^0{0,1}[2-3][a-f0-9]{0,2}:/" |
||||
}, |
||||
"page": |
||||
{ |
||||
"title": |
||||
{ |
||||
"required": true, |
||||
"length": |
||||
{ |
||||
"min": 10, |
||||
"max": 255 |
||||
}, |
||||
"regex": "/.*/ui" |
||||
}, |
||||
"description": |
||||
{ |
||||
"required": false, |
||||
"length": |
||||
{ |
||||
"min": 0, |
||||
"max": 10000 |
||||
}, |
||||
"regex": "/.*/ui" |
||||
}, |
||||
"keyword": |
||||
{ |
||||
"length": |
||||
{ |
||||
"min": 0, |
||||
"max": 140 |
||||
}, |
||||
"regex": "/[\\w]+/ui" |
||||
}, |
||||
"keywords": |
||||
{ |
||||
"required": false, |
||||
"quantity": |
||||
{ |
||||
"min": 0, |
||||
"max": 20 |
||||
} |
||||
}, |
||||
"image": |
||||
{ |
||||
"required": false, |
||||
"mime": [ |
||||
"image/png", |
||||
"image/gif", |
||||
"image/jpeg", |
||||
"image/webp" |
||||
], |
||||
"quantity": |
||||
{ |
||||
"min": 0, |
||||
"max": 20 |
||||
} |
||||
}, |
||||
"torrent": |
||||
{ |
||||
"required": true, |
||||
"mime": [ |
||||
"application/x-bittorrent" |
||||
], |
||||
"quantity": |
||||
{ |
||||
"min": 0, |
||||
"max": 20 |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,23 +0,0 @@
@@ -1,23 +0,0 @@
|
||||
{ |
||||
"name":"YGGtracker", |
||||
"scheme":"", |
||||
"host":"", |
||||
"port":"", |
||||
"path":"", |
||||
"default": |
||||
{ |
||||
"locale":"en-US", |
||||
"user": |
||||
{ |
||||
"status": true, |
||||
"approved": false |
||||
} |
||||
}, |
||||
"api": |
||||
{ |
||||
"export": |
||||
{ |
||||
"enabled" : true |
||||
} |
||||
} |
||||
} |
@ -1,558 +0,0 @@
@@ -1,558 +0,0 @@
|
||||
<?php |
||||
|
||||
// Lock multi-thread execution |
||||
$semaphore = sem_get(crc32('yggtracker.crontab.export.feed'), 1); |
||||
|
||||
if (false === sem_acquire($semaphore, true)) |
||||
{ |
||||
exit (_('yggtracker.crontab.export.feed process locked by another thread.')); |
||||
} |
||||
|
||||
// Bootstrap |
||||
require_once __DIR__ . '/../../config/bootstrap.php'; |
||||
|
||||
// Init Debug |
||||
$debug = |
||||
[ |
||||
'dump' => [], |
||||
'time' => [ |
||||
'ISO8601' => date('c'), |
||||
'total' => microtime(true), |
||||
], |
||||
'http' => |
||||
[ |
||||
'total' => 0, |
||||
], |
||||
'memory' => |
||||
[ |
||||
'start' => memory_get_usage(), |
||||
'total' => 0, |
||||
'peaks' => 0 |
||||
], |
||||
]; |
||||
|
||||
// Define public registry |
||||
$public = [ |
||||
'user' => [], |
||||
'magnet' => [], |
||||
]; |
||||
|
||||
// Begin export |
||||
try |
||||
{ |
||||
// Init API folder if not exists |
||||
@mkdir(__DIR__ . '/../public/api'); |
||||
|
||||
// Delete cached feeds |
||||
@unlink(__DIR__ . '/../public/api/manifest.json'); |
||||
|
||||
@unlink(__DIR__ . '/../public/api/users.json'); |
||||
@unlink(__DIR__ . '/../public/api/magnets.json'); |
||||
@unlink(__DIR__ . '/../public/api/magnetComments.json'); |
||||
@unlink(__DIR__ . '/../public/api/magnetDownloads.json'); |
||||
@unlink(__DIR__ . '/../public/api/magnetStars.json'); |
||||
@unlink(__DIR__ . '/../public/api/magnetViews.json'); |
||||
|
||||
if (API_EXPORT_ENABLED) |
||||
{ |
||||
// Manifest |
||||
$manifest = |
||||
[ |
||||
'updated' => time(), |
||||
'version' => (string) API_VERSION, |
||||
|
||||
'settings' => (object) |
||||
[ |
||||
'YGGDRASIL_HOST_REGEX' => (string) YGGDRASIL_HOST_REGEX, |
||||
|
||||
'NODE_RULE_SUBJECT' => (string) NODE_RULE_SUBJECT, |
||||
'NODE_RULE_LANGUAGES' => (string) NODE_RULE_LANGUAGES, |
||||
|
||||
'USER_DEFAULT_APPROVED' => (bool) USER_DEFAULT_APPROVED, |
||||
'USER_AUTO_APPROVE_ON_MAGNET_APPROVE' => (bool) USER_AUTO_APPROVE_ON_MAGNET_APPROVE, |
||||
'USER_AUTO_APPROVE_ON_COMMENT_APPROVE' => (bool) USER_AUTO_APPROVE_ON_COMMENT_APPROVE, |
||||
'USER_DEFAULT_IDENTICON' => (string) USER_DEFAULT_IDENTICON, |
||||
'USER_IDENTICON_FIELD' => (string) USER_IDENTICON_FIELD, |
||||
|
||||
'MAGNET_DEFAULT_APPROVED' => (bool) MAGNET_DEFAULT_APPROVED, |
||||
'MAGNET_DEFAULT_PUBLIC' => (bool) MAGNET_DEFAULT_PUBLIC, |
||||
'MAGNET_DEFAULT_COMMENTS' => (bool) MAGNET_DEFAULT_COMMENTS, |
||||
'MAGNET_DEFAULT_SENSITIVE' => (bool) MAGNET_DEFAULT_SENSITIVE, |
||||
|
||||
'MAGNET_EDITOR_LOCK_TIMEOUT' => (int) MAGNET_EDITOR_LOCK_TIMEOUT, |
||||
|
||||
'MAGNET_TITLE_MIN_LENGTH' => (int) MAGNET_TITLE_MIN_LENGTH, |
||||
'MAGNET_TITLE_MAX_LENGTH' => (int) MAGNET_TITLE_MAX_LENGTH, |
||||
'MAGNET_TITLE_REGEX' => (string) MAGNET_TITLE_REGEX, |
||||
|
||||
'MAGNET_PREVIEW_MIN_LENGTH' => (int) MAGNET_PREVIEW_MIN_LENGTH, |
||||
'MAGNET_PREVIEW_MAX_LENGTH' => (int) MAGNET_PREVIEW_MAX_LENGTH, |
||||
'MAGNET_PREVIEW_REGEX' => (string) MAGNET_PREVIEW_REGEX, |
||||
|
||||
'MAGNET_DESCRIPTION_MIN_LENGTH' => (int) MAGNET_DESCRIPTION_MIN_LENGTH, |
||||
'MAGNET_DESCRIPTION_MAX_LENGTH' => (int) MAGNET_DESCRIPTION_MAX_LENGTH, |
||||
'MAGNET_DESCRIPTION_REGEX' => (string) MAGNET_DESCRIPTION_REGEX, |
||||
|
||||
'MAGNET_DN_MIN_LENGTH' => (int) MAGNET_DN_MIN_LENGTH, |
||||
'MAGNET_DN_MAX_LENGTH' => (int) MAGNET_DN_MAX_LENGTH, |
||||
'MAGNET_DN_REGEX' => (string) MAGNET_DN_REGEX, |
||||
|
||||
'MAGNET_KT_MIN_LENGTH' => (int) MAGNET_KT_MIN_LENGTH, |
||||
'MAGNET_KT_MAX_LENGTH' => (int) MAGNET_KT_MAX_LENGTH, |
||||
'MAGNET_KT_MIN_QUANTITY' => (int) MAGNET_KT_MIN_QUANTITY, |
||||
'MAGNET_KT_MAX_QUANTITY' => (int) MAGNET_KT_MAX_QUANTITY, |
||||
'MAGNET_KT_REGEX' => (string) MAGNET_KT_REGEX, |
||||
|
||||
'MAGNET_TR_MIN_QUANTITY' => (int) MAGNET_TR_MIN_QUANTITY, |
||||
'MAGNET_TR_MAX_QUANTITY' => (int) MAGNET_TR_MAX_QUANTITY, |
||||
|
||||
'MAGNET_AS_MIN_QUANTITY' => (int) MAGNET_AS_MIN_QUANTITY, |
||||
'MAGNET_AS_MAX_QUANTITY' => (int) MAGNET_AS_MAX_QUANTITY, |
||||
|
||||
'MAGNET_WS_MIN_QUANTITY' => (int) MAGNET_WS_MIN_QUANTITY, |
||||
'MAGNET_WS_MAX_QUANTITY' => (int) MAGNET_WS_MAX_QUANTITY, |
||||
|
||||
'MAGNET_COMMENT_DEFAULT_APPROVED' => (bool) MAGNET_COMMENT_DEFAULT_APPROVED, |
||||
'MAGNET_COMMENT_DEFAULT_PUBLIC' => (bool) MAGNET_COMMENT_DEFAULT_PUBLIC, |
||||
'MAGNET_COMMENT_DEFAULT_PUBLIC' => (bool) MAGNET_COMMENT_DEFAULT_PUBLIC, |
||||
'MAGNET_COMMENT_MIN_LENGTH' => (int) MAGNET_COMMENT_MIN_LENGTH, |
||||
'MAGNET_COMMENT_MAX_LENGTH' => (int) MAGNET_COMMENT_MAX_LENGTH, |
||||
|
||||
'MAGNET_STOP_WORDS_SIMILAR' => (object) MAGNET_STOP_WORDS_SIMILAR, |
||||
|
||||
'API_USER_AGENT' => (string) API_USER_AGENT, |
||||
|
||||
'API_EXPORT_ENABLED' => (bool) API_EXPORT_ENABLED, |
||||
'API_EXPORT_PUSH_ENABLED' => (bool) API_EXPORT_PUSH_ENABLED, |
||||
'API_EXPORT_USERS_ENABLED' => (bool) API_EXPORT_USERS_ENABLED, |
||||
'API_EXPORT_MAGNETS_ENABLED' => (bool) API_EXPORT_MAGNETS_ENABLED, |
||||
'API_EXPORT_MAGNET_DOWNLOADS_ENABLED' => (bool) API_EXPORT_MAGNET_DOWNLOADS_ENABLED, |
||||
'API_EXPORT_MAGNET_COMMENTS_ENABLED' => (bool) API_EXPORT_MAGNET_COMMENTS_ENABLED, |
||||
'API_EXPORT_MAGNET_STARS_ENABLED' => (bool) API_EXPORT_MAGNET_STARS_ENABLED, |
||||
'API_EXPORT_MAGNET_STARS_ENABLED' => (bool) API_EXPORT_MAGNET_STARS_ENABLED, |
||||
'API_EXPORT_MAGNET_VIEWS_ENABLED' => (bool) API_EXPORT_MAGNET_VIEWS_ENABLED, |
||||
|
||||
'API_IMPORT_ENABLED' => (bool) API_IMPORT_ENABLED, |
||||
'API_IMPORT_PUSH_ENABLED' => (bool) API_IMPORT_PUSH_ENABLED, |
||||
'API_IMPORT_USERS_ENABLED' => (bool) API_IMPORT_USERS_ENABLED, |
||||
|
||||
'API_IMPORT_USERS_APPROVED_ONLY' => (bool) API_IMPORT_USERS_APPROVED_ONLY, |
||||
'API_IMPORT_MAGNETS_ENABLED' => (bool) API_IMPORT_MAGNETS_ENABLED, |
||||
'API_IMPORT_MAGNETS_APPROVED_ONLY' => (bool) API_IMPORT_MAGNETS_APPROVED_ONLY, |
||||
'API_IMPORT_MAGNET_DOWNLOADS_ENABLED' => (bool) API_IMPORT_MAGNET_DOWNLOADS_ENABLED, |
||||
'API_IMPORT_MAGNET_COMMENTS_ENABLED' => (bool) API_IMPORT_MAGNET_COMMENTS_ENABLED, |
||||
'API_IMPORT_MAGNET_COMMENTS_APPROVED_ONLY' => (bool) API_IMPORT_MAGNET_COMMENTS_APPROVED_ONLY, |
||||
'API_IMPORT_MAGNET_STARS_ENABLED' => (bool) API_IMPORT_MAGNET_STARS_ENABLED, |
||||
'API_IMPORT_MAGNET_VIEWS_ENABLED' => (bool) API_IMPORT_MAGNET_VIEWS_ENABLED, |
||||
], |
||||
'totals' => (object) |
||||
[ |
||||
'magnets' => (object) |
||||
[ |
||||
'total' => $db->getMagnetsTotal(), |
||||
'distributed' => $db->getMagnetsTotalByUsersPublic(true), |
||||
'local' => $db->getMagnetsTotalByUsersPublic(false), |
||||
], |
||||
'downloads' => (object) |
||||
[ |
||||
'total' => $db->getMagnetDownloadsTotal(), |
||||
'distributed' => $db->findMagnetDownloadsTotalByUsersPublic(true), |
||||
'local' => $db->findMagnetDownloadsTotalByUsersPublic(false), |
||||
], |
||||
'comments' => (object) |
||||
[ |
||||
'total' => $db->getMagnetCommentsTotal(), |
||||
'distributed' => $db->findMagnetCommentsTotalByUsersPublic(true), |
||||
'local' => $db->findMagnetCommentsTotalByUsersPublic(false), |
||||
], |
||||
'stars' => (object) |
||||
[ |
||||
'total' => $db->getMagnetStarsTotal(), |
||||
'distributed' => $db->findMagnetStarsTotalByUsersPublic(true), |
||||
'local' => $db->findMagnetStarsTotalByUsersPublic(false), |
||||
], |
||||
'views' => (object) |
||||
[ |
||||
'total' => $db->getMagnetViewsTotal(), |
||||
'distributed' => $db->findMagnetViewsTotalByUsersPublic(true), |
||||
'local' => $db->findMagnetViewsTotalByUsersPublic(false), |
||||
], |
||||
], |
||||
'import' => (object) |
||||
[ |
||||
'push' => API_IMPORT_PUSH_ENABLED ? sprintf('%s/api/push.php', WEBSITE_URL) : false, |
||||
], |
||||
'export' => (object) |
||||
[ |
||||
'users' => API_EXPORT_USERS_ENABLED ? sprintf('%s/api/users.json', WEBSITE_URL) : false, |
||||
'magnets' => API_EXPORT_MAGNETS_ENABLED ? sprintf('%s/api/magnets.json', WEBSITE_URL) : false, |
||||
'magnetDownloads' => API_EXPORT_MAGNET_DOWNLOADS_ENABLED ? sprintf('%s/api/magnetDownloads.json', WEBSITE_URL) : false, |
||||
'magnetComments' => API_EXPORT_MAGNET_COMMENTS_ENABLED ? sprintf('%s/api/magnetComments.json', WEBSITE_URL) : false, |
||||
'magnetStars' => API_EXPORT_MAGNET_STARS_ENABLED ? sprintf('%s/api/magnetStars.json', WEBSITE_URL) : false, |
||||
'magnetViews' => API_EXPORT_MAGNET_VIEWS_ENABLED ? sprintf('%s/api/magnetViews.json', WEBSITE_URL) : false, |
||||
], |
||||
'trackers' => (object) json_decode(file_get_contents(__DIR__ . '/../../config/trackers.json')), |
||||
'nodes' => (object) json_decode(file_get_contents(__DIR__ . '/../../config/nodes.json')), |
||||
'peers' => (object) json_decode(file_get_contents(__DIR__ . '/../../config/peers.json')), |
||||
]; |
||||
|
||||
/// Dump feed |
||||
if ($handle = fopen(__DIR__ . '/../../public/api/manifest.json', 'w+')) |
||||
{ |
||||
fwrite($handle, json_encode($manifest)); |
||||
fclose($handle); |
||||
|
||||
chmod(__DIR__ . '/../../public/api/manifest.json', 0774); |
||||
} |
||||
|
||||
// Users |
||||
if (API_EXPORT_USERS_ENABLED) |
||||
{ |
||||
$users = []; |
||||
|
||||
foreach ($db->getUsers() as $user) |
||||
{ |
||||
// Dump public data only |
||||
if ($user->public) |
||||
{ |
||||
$users[] = (object) |
||||
[ |
||||
'userId' => (int) $user->userId, |
||||
'address' => (string) $user->address, |
||||
'timeAdded' => (int) $user->timeAdded, |
||||
'timeUpdated' => (int) $user->timeUpdated, |
||||
'approved' => (bool) $user->approved, |
||||
'magnets' => (int) $db->findMagnetsTotalByUserId($user->userId), |
||||
'downloads' => (int) $db->findMagnetDownloadsTotalByUserId($user->userId), |
||||
'comments' => (int) $db->findMagnetCommentsTotalByUserId($user->userId), |
||||
'stars' => (int) $db->findMagnetStarsTotalByUserId($user->userId), |
||||
'views' => (int) $db->findMagnetViewsTotalByUserId($user->userId), |
||||
]; |
||||
} |
||||
|
||||
// Cache public status |
||||
$public['user'][$user->userId] = (bool) $user->public; |
||||
} |
||||
|
||||
/// Dump users feed |
||||
if ($handle = fopen(__DIR__ . '/../../public/api/users.json', 'w+')) |
||||
{ |
||||
fwrite($handle, json_encode($users)); |
||||
fclose($handle); |
||||
|
||||
chmod(__DIR__ . '/../../public/api/users.json', 0774); |
||||
} |
||||
} |
||||
|
||||
// Magnets |
||||
if (API_EXPORT_MAGNETS_ENABLED) |
||||
{ |
||||
$magnets = []; |
||||
|
||||
foreach ($db->getMagnets($user->userId) as $magnet) |
||||
{ |
||||
// Dump public data only |
||||
if ($magnet->public && |
||||
$public['user'][$magnet->userId]) // After upgrade, some users have not updated their public status. |
||||
// Remote node have warning on import, because user info still hidden to init new profile there. |
||||
// Stop magnets export without public profile available, even magnet is public. |
||||
{ |
||||
// Info Hash |
||||
$xt = []; |
||||
foreach ($db->findMagnetToInfoHashByMagnetId($magnet->magnetId) as $result) |
||||
{ |
||||
if ($infoHash = $db->getInfoHash($result->infoHashId)) |
||||
{ |
||||
$xt[] = (object) [ |
||||
'version' => (float) $infoHash->version, |
||||
'value' => (string) $infoHash->value, |
||||
]; |
||||
} |
||||
} |
||||
|
||||
// Keyword Topic |
||||
$kt = []; |
||||
|
||||
foreach ($db->findKeywordTopicByMagnetId($magnet->magnetId) as $result) |
||||
{ |
||||
$kt[] = $db->getKeywordTopic($result->keywordTopicId)->value; |
||||
} |
||||
|
||||
// Address Tracker |
||||
$tr = []; |
||||
foreach ($db->findAddressTrackerByMagnetId($magnet->magnetId) as $result) |
||||
{ |
||||
$addressTracker = $db->getAddressTracker($result->addressTrackerId); |
||||
|
||||
$scheme = $db->getScheme($addressTracker->schemeId); |
||||
$host = $db->getHost($addressTracker->hostId); |
||||
$port = $db->getPort($addressTracker->portId); |
||||
$uri = $db->getUri($addressTracker->uriId); |
||||
|
||||
// Yggdrasil host only |
||||
if (!Valid::host($host->value)) |
||||
{ |
||||
continue; |
||||
} |
||||
|
||||
$tr[] = $port->value ? sprintf('%s://%s:%s%s', $scheme->value, |
||||
$host->value, |
||||
$port->value, |
||||
$uri->value) : sprintf('%s://%s%s', $scheme->value, |
||||
$host->value, |
||||
$uri->value); |
||||
} |
||||
|
||||
// Acceptable Source |
||||
$as = []; |
||||
|
||||
foreach ($db->findAcceptableSourceByMagnetId($magnet->magnetId) as $result) |
||||
{ |
||||
$acceptableSource = $db->getAcceptableSource($result->acceptableSourceId); |
||||
|
||||
$scheme = $db->getScheme($acceptableSource->schemeId); |
||||
$host = $db->getHost($acceptableSource->hostId); |
||||
$port = $db->getPort($acceptableSource->portId); |
||||
$uri = $db->getUri($acceptableSource->uriId); |
||||
|
||||
// Yggdrasil host only |
||||
if (!Valid::host($host->value)) |
||||
{ |
||||
continue; |
||||
} |
||||
|
||||
$as[] = $port->value ? sprintf('%s://%s:%s%s', $scheme->value, |
||||
$host->value, |
||||
$port->value, |
||||
$uri->value) : sprintf('%s://%s%s', $scheme->value, |
||||
$host->value, |
||||
$uri->value); |
||||
} |
||||
|
||||
// Exact Source |
||||
$xs = []; |
||||
|
||||
foreach ($db->findExactSourceByMagnetId($magnet->magnetId) as $result) |
||||
{ |
||||
$eXactSource = $db->getExactSource($result->eXactSourceId); |
||||
|
||||
$scheme = $db->getScheme($eXactSource->schemeId); |
||||
$host = $db->getHost($eXactSource->hostId); |
||||
$port = $db->getPort($eXactSource->portId); |
||||
$uri = $db->getUri($eXactSource->uriId); |
||||
|
||||
// Yggdrasil host only |
||||
if (!Valid::host($host->value)) |
||||
{ |
||||
continue; |
||||
} |
||||
|
||||
$xs[] = $port->value ? sprintf('%s://%s:%s%s', $scheme->value, |
||||
$host->value, |
||||
$port->value, |
||||
$uri->value) : sprintf('%s://%s%s', $scheme->value, |
||||
$host->value, |
||||
$uri->value); |
||||
} |
||||
|
||||
$magnets[] = (object) |
||||
[ |
||||
'magnetId' => (int) $magnet->magnetId, |
||||
'userId' => (int) $magnet->userId, |
||||
'title' => (string) $magnet->title, |
||||
'preview' => (string) $magnet->preview, |
||||
'description' => (string) $magnet->description, |
||||
'comments' => (bool) $magnet->comments, |
||||
'sensitive' => (bool) $magnet->sensitive, |
||||
'approved' => (bool) $magnet->approved, |
||||
'timeAdded' => (int) $magnet->timeAdded, |
||||
'timeUpdated' => (int) $magnet->timeUpdated, |
||||
'dn' => (string) $magnet->dn, |
||||
'xl' => (float) $magnet->xl, |
||||
'xt' => (object) $xt, |
||||
'kt' => (object) $kt, |
||||
'tr' => (object) $tr, |
||||
'as' => (object) $as, |
||||
'xs' => (object) $xs, |
||||
]; |
||||
} |
||||
|
||||
// Cache public status |
||||
if (!empty($public['user'][$magnet->userId])) |
||||
{ |
||||
$public['magnet'][$magnet->magnetId] = (bool) $magnet->public; |
||||
} else { |
||||
$public['magnet'][$magnet->magnetId] = false; |
||||
} |
||||
} |
||||
|
||||
/// Dump magnets feed |
||||
if ($handle = fopen(__DIR__ . '/../../public/api/magnets.json', 'w+')) |
||||
{ |
||||
fwrite($handle, json_encode($magnets)); |
||||
fclose($handle); |
||||
|
||||
chmod(__DIR__ . '/../../public/api/magnets.json', 0774); |
||||
} |
||||
} |
||||
|
||||
// Magnet downloads |
||||
if (API_EXPORT_MAGNET_DOWNLOADS_ENABLED) |
||||
{ |
||||
$magnetDownloads = []; |
||||
|
||||
foreach ($db->getMagnetDownloads() as $magnetDownload) |
||||
{ |
||||
// Dump public data only |
||||
if (!empty($public['magnet'][$magnetDownload->magnetId]) && |
||||
!empty($public['user'][$magnetDownload->userId])) |
||||
{ |
||||
$magnetDownloads[] = (object) |
||||
[ |
||||
'magnetDownloadId' => (int) $magnetDownload->magnetDownloadId, |
||||
'userId' => (int) $magnetDownload->userId, |
||||
'magnetId' => (int) $magnetDownload->magnetId, |
||||
'timeAdded' => (int) $magnetDownload->timeAdded, |
||||
]; |
||||
} |
||||
} |
||||
|
||||
/// Dump feed |
||||
if ($handle = fopen(__DIR__ . '/../../public/api/magnetDownloads.json', 'w+')) |
||||
{ |
||||
fwrite($handle, json_encode($magnetDownloads)); |
||||
fclose($handle); |
||||
|
||||
chmod(__DIR__ . '/../../public/api/magnetDownloads.json', 0774); |
||||
} |
||||
} |
||||
|
||||
// Magnet comments |
||||
if (API_EXPORT_MAGNET_COMMENTS_ENABLED) |
||||
{ |
||||
$magnetComments = []; |
||||
|
||||
foreach ($db->getMagnetComments() as $magnetComment) |
||||
{ |
||||
// Dump public data only |
||||
if (!empty($public['magnet'][$magnetComment->magnetId]) && |
||||
!empty($public['user'][$magnetComment->userId])) |
||||
{ |
||||
$magnetComments[] = (object) |
||||
[ |
||||
'magnetCommentId' => (int) $magnetComment->magnetCommentId, |
||||
'magnetCommentIdParent' => $magnetComment->magnetCommentIdParent, |
||||
'userId' => (int) $magnetComment->userId, |
||||
'magnetId' => (int) $magnetComment->magnetId, |
||||
'timeAdded' => (int) $magnetComment->timeAdded, |
||||
'approved' => (bool) $magnetComment->approved, |
||||
'value' => (string) $magnetComment->value |
||||
]; |
||||
} |
||||
} |
||||
|
||||
/// Dump feed |
||||
if ($handle = fopen(__DIR__ . '/../../public/api/magnetComments.json', 'w+')) |
||||
{ |
||||
fwrite($handle, json_encode($magnetComments)); |
||||
fclose($handle); |
||||
|
||||
chmod(__DIR__ . '/../../public/api/magnetComments.json', 0774); |
||||
} |
||||
} |
||||
|
||||
// Magnet stars |
||||
if (API_EXPORT_MAGNET_STARS_ENABLED) |
||||
{ |
||||
$magnetStars = []; |
||||
|
||||
foreach ($db->getMagnetStars() as $magnetStar) |
||||
{ |
||||
// Dump public data only |
||||
if (!empty($public['magnet'][$magnetStar->magnetId]) && |
||||
!empty($public['user'][$magnetStar->userId])) |
||||
{ |
||||
$magnetStars[] = (object) |
||||
[ |
||||
'magnetStarId' => (int) $magnetStar->magnetStarId, |
||||
'userId' => (int) $magnetStar->userId, |
||||
'magnetId' => (int) $magnetStar->magnetId, |
||||
'value' => (bool) $magnetStar->value, |
||||
'timeAdded' => (int) $magnetStar->timeAdded, |
||||
]; |
||||
} |
||||
} |
||||
|
||||
/// Dump feed |
||||
if ($handle = fopen(__DIR__ . '/../../public/api/magnetStars.json', 'w+')) |
||||
{ |
||||
fwrite($handle, json_encode($magnetStars)); |
||||
fclose($handle); |
||||
|
||||
chmod(__DIR__ . '/../../public/api/magnetStars.json', 0774); |
||||
} |
||||
} |
||||
|
||||
// Magnet views |
||||
if (API_EXPORT_MAGNET_VIEWS_ENABLED) |
||||
{ |
||||
$magnetViews = []; |
||||
|
||||
foreach ($db->getMagnetViews() as $magnetView) |
||||
{ |
||||
// Dump public data only |
||||
if (!empty($public['magnet'][$magnetView->magnetId]) && |
||||
!empty($public['user'][$magnetView->userId])) |
||||
{ |
||||
$magnetViews[] = (object) |
||||
[ |
||||
'magnetViewId' => (int) $magnetView->magnetViewId, |
||||
'userId' => (int) $magnetView->userId, |
||||
'magnetId' => (int) $magnetView->magnetId, |
||||
'timeAdded' => (int) $magnetView->timeAdded, |
||||
]; |
||||
} |
||||
} |
||||
|
||||
/// Dump feed |
||||
if ($handle = fopen(__DIR__ . '/../../public/api/magnetViews.json', 'w+')) |
||||
{ |
||||
fwrite($handle, json_encode($magnetViews)); |
||||
fclose($handle); |
||||
|
||||
chmod(__DIR__ . '/../../public/api/magnetViews.json', 0774); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} catch (EXception $e) { |
||||
|
||||
var_dump($e); |
||||
} |
||||
|
||||
// Debug output |
||||
$debug['time']['total'] = microtime(true) - $debug['time']['total']; |
||||
|
||||
$debug['memory']['total'] = memory_get_usage() - $debug['memory']['start']; |
||||
$debug['memory']['peaks'] = memory_get_peak_usage(); |
||||
|
||||
$debug['db']['total']['select'] = $db->getDebug()->query->select->total; |
||||
$debug['db']['total']['insert'] = $db->getDebug()->query->insert->total; |
||||
$debug['db']['total']['update'] = $db->getDebug()->query->update->total; |
||||
$debug['db']['total']['delete'] = $db->getDebug()->query->delete->total; |
||||
|
||||
print_r($debug); |
||||
|
||||
// Debug log |
||||
if (LOG_CRONTAB_EXPORT_FEED_ENABLED) |
||||
{ |
||||
@mkdir(LOG_DIRECTORY, 0774, true); |
||||
|
||||
if ($handle = fopen(LOG_DIRECTORY . '/' . LOG_CRONTAB_EXPORT_FEED_FILENAME, 'a+')) |
||||
{ |
||||
fwrite($handle, print_r($debug, true)); |
||||
fclose($handle); |
||||
|
||||
chmod(LOG_DIRECTORY . '/' . LOG_CRONTAB_EXPORT_FEED_FILENAME, 0774); |
||||
} |
||||
} |
@ -1,506 +0,0 @@
@@ -1,506 +0,0 @@
|
||||
<?php |
||||
|
||||
// Lock multi-thread execution |
||||
$semaphore = sem_get(crc32('yggtracker.crontab.export.push'), 1); |
||||
|
||||
if (false === sem_acquire($semaphore, true)) |
||||
{ |
||||
exit (_('yggtracker.crontab.export.push process locked by another thread.')); |
||||
} |
||||
|
||||
// Bootstrap |
||||
require_once __DIR__ . '/../../config/bootstrap.php'; |
||||
|
||||
// Init Debug |
||||
$debug = |
||||
[ |
||||
'dump' => [], |
||||
'time' => [ |
||||
'ISO8601' => date('c'), |
||||
'total' => microtime(true), |
||||
], |
||||
'http' => |
||||
[ |
||||
'total' => 0, |
||||
], |
||||
'memory' => |
||||
[ |
||||
'start' => memory_get_usage(), |
||||
'total' => 0, |
||||
'peaks' => 0 |
||||
], |
||||
]; |
||||
|
||||
// Define public registry |
||||
$public = [ |
||||
'user' => [], |
||||
'magnet' => [], |
||||
]; |
||||
|
||||
// Push export enabled |
||||
if (API_EXPORT_PUSH_ENABLED) |
||||
{ |
||||
// Get push queue from memory pool |
||||
foreach((array) $memoryApiExportPush = $memory->get('api.export.push') as $id => $push) |
||||
{ |
||||
// Init request |
||||
$request = []; |
||||
|
||||
// User request |
||||
if (!empty($push->userId) && API_EXPORT_USERS_ENABLED) |
||||
{ |
||||
// Get user info |
||||
if ($user = $db->getUser($push->userId)) |
||||
{ |
||||
// Dump public data only |
||||
if ($user->public) |
||||
{ |
||||
$request['user'] = (object) |
||||
[ |
||||
'userId' => (int) $user->userId, |
||||
'address' => (string) $user->address, |
||||
'timeAdded' => (int) $user->timeAdded, |
||||
'timeUpdated' => (int) $user->timeUpdated, |
||||
'approved' => (bool) $user->approved, |
||||
]; |
||||
|
||||
// Cache public status |
||||
$public['user'][$user->userId] = (bool) $user->public; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Magnet request |
||||
if (!empty($push->magnetId) && API_EXPORT_MAGNETS_ENABLED) |
||||
{ |
||||
// Get magnet info |
||||
if ($magnet = $db->getMagnet($push->magnetId)) |
||||
{ |
||||
if ($magnet->public && |
||||
$public['user'][$magnet->userId]) // After upgrade, some users have not updated their public status. |
||||
// Remote node have warning on import, because user info still hidden to init new profile there. |
||||
// Stop magnets export without public profile available, even magnet is public. |
||||
{ |
||||
// Info Hash |
||||
$xt = []; |
||||
foreach ($db->findMagnetToInfoHashByMagnetId($magnet->magnetId) as $result) |
||||
{ |
||||
if ($infoHash = $db->getInfoHash($result->infoHashId)) |
||||
{ |
||||
$xt[] = (object) [ |
||||
'version' => (float) $infoHash->version, |
||||
'value' => (string) $infoHash->value, |
||||
]; |
||||
} |
||||
} |
||||
|
||||
// Keyword Topic |
||||
$kt = []; |
||||
|
||||
foreach ($db->findKeywordTopicByMagnetId($magnet->magnetId) as $result) |
||||
{ |
||||
$kt[] = $db->getKeywordTopic($result->keywordTopicId)->value; |
||||
} |
||||
|
||||
// Address Tracker |
||||
$tr = []; |
||||
foreach ($db->findAddressTrackerByMagnetId($magnet->magnetId) as $result) |
||||
{ |
||||
$addressTracker = $db->getAddressTracker($result->addressTrackerId); |
||||
|
||||
$scheme = $db->getScheme($addressTracker->schemeId); |
||||
$host = $db->getHost($addressTracker->hostId); |
||||
$port = $db->getPort($addressTracker->portId); |
||||
$uri = $db->getUri($addressTracker->uriId); |
||||
|
||||
// Yggdrasil host only |
||||
if (!Valid::host($host->value)) |
||||
{ |
||||
continue; |
||||
} |
||||
|
||||
$tr[] = $port->value ? sprintf('%s://%s:%s%s', $scheme->value, |
||||
$host->value, |
||||
$port->value, |
||||
$uri->value) : sprintf('%s://%s%s', $scheme->value, |
||||
$host->value, |
||||
$uri->value); |
||||
} |
||||
|
||||
// Acceptable Source |
||||
$as = []; |
||||
|
||||
foreach ($db->findAcceptableSourceByMagnetId($magnet->magnetId) as $result) |
||||
{ |
||||
$acceptableSource = $db->getAcceptableSource($result->acceptableSourceId); |
||||
|
||||
$scheme = $db->getScheme($acceptableSource->schemeId); |
||||
$host = $db->getHost($acceptableSource->hostId); |
||||
$port = $db->getPort($acceptableSource->portId); |
||||
$uri = $db->getUri($acceptableSource->uriId); |
||||
|
||||
// Yggdrasil host only |
||||
if (!Valid::host($host->value)) |
||||
{ |
||||
continue; |
||||
} |
||||
|
||||
$as[] = $port->value ? sprintf('%s://%s:%s%s', $scheme->value, |
||||
$host->value, |
||||
$port->value, |
||||
$uri->value) : sprintf('%s://%s%s', $scheme->value, |
||||
$host->value, |
||||
$uri->value); |
||||
} |
||||
|
||||
// Exact Source |
||||
$xs = []; |
||||
|
||||
foreach ($db->findExactSourceByMagnetId($magnet->magnetId) as $result) |
||||
{ |
||||
$eXactSource = $db->getExactSource($result->eXactSourceId); |
||||
|
||||
$scheme = $db->getScheme($eXactSource->schemeId); |
||||
$host = $db->getHost($eXactSource->hostId); |
||||
$port = $db->getPort($eXactSource->portId); |
||||
$uri = $db->getUri($eXactSource->uriId); |
||||
|
||||
// Yggdrasil host only |
||||
if (!Valid::host($host->value)) |
||||
{ |
||||
continue; |
||||
} |
||||
|
||||
$xs[] = $port->value ? sprintf('%s://%s:%s%s', $scheme->value, |
||||
$host->value, |
||||
$port->value, |
||||
$uri->value) : sprintf('%s://%s%s', $scheme->value, |
||||
$host->value, |
||||
$uri->value); |
||||
} |
||||
|
||||
$request['magnet'] = (object) |
||||
[ |
||||
'magnetId' => (int) $magnet->magnetId, |
||||
'userId' => (int) $magnet->userId, |
||||
'title' => (string) $magnet->title, |
||||
'preview' => (string) $magnet->preview, |
||||
'description' => (string) $magnet->description, |
||||
'comments' => (bool) $magnet->comments, |
||||
'sensitive' => (bool) $magnet->sensitive, |
||||
'approved' => (bool) $magnet->approved, |
||||
'timeAdded' => (int) $magnet->timeAdded, |
||||
'timeUpdated' => (int) $magnet->timeUpdated, |
||||
'dn' => (string) $magnet->dn, |
||||
'xl' => (float) $magnet->xl, |
||||
'xt' => (object) $xt, |
||||
'kt' => (object) $kt, |
||||
'tr' => (object) $tr, |
||||
'as' => (object) $as, |
||||
'xs' => (object) $xs, |
||||
]; |
||||
} |
||||
|
||||
// Cache public status |
||||
if (!empty($public['user'][$magnet->userId])) |
||||
{ |
||||
$public['magnet'][$magnet->magnetId] = (bool) $magnet->public; |
||||
} else { |
||||
$public['magnet'][$magnet->magnetId] = false; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Magnet download request |
||||
if (!empty($push->magnetDownloadId) && API_EXPORT_MAGNET_DOWNLOADS_ENABLED) |
||||
{ |
||||
// Get magnet download info |
||||
if ($magnetDownload = $db->getMagnetDownload($push->magnetDownloadId)) |
||||
{ |
||||
// Dump public data only |
||||
if (!empty($public['magnet'][$magnetDownload->magnetId]) && |
||||
!empty($public['user'][$magnetDownload->userId])) |
||||
{ |
||||
$request['magnetDownload'] = (object) |
||||
[ |
||||
'magnetDownloadId' => (int) $magnetDownload->magnetDownloadId, |
||||
'userId' => (int) $magnetDownload->userId, |
||||
'magnetId' => (int) $magnetDownload->magnetId, |
||||
'timeAdded' => (int) $magnetDownload->timeAdded, |
||||
]; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Magnet comment request |
||||
if (!empty($push->magnetCommentId) && API_EXPORT_MAGNET_COMMENTS_ENABLED) |
||||
{ |
||||
// Get magnet comment info |
||||
if ($magnetComment = $db->getMagnetComment($push->magnetCommentId)) |
||||
{ |
||||
// Dump public data only |
||||
if (!empty($public['magnet'][$magnetComment->magnetId]) && |
||||
!empty($public['user'][$magnetComment->userId])) |
||||
{ |
||||
$request['magnetComment'] = (object) |
||||
[ |
||||
'magnetCommentId' => (int) $magnetComment->magnetCommentId, |
||||
'magnetCommentIdParent' => (int) $magnetComment->magnetCommentIdParent, |
||||
'userId' => (int) $magnetComment->userId, |
||||
'magnetId' => (int) $magnetComment->magnetId, |
||||
'timeAdded' => (int) $magnetComment->timeAdded, |
||||
'approved' => (bool) $magnetComment->approved, |
||||
'value' => (string) $magnetComment->value |
||||
]; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Magnet star request |
||||
if (!empty($push->magnetStarId) && API_EXPORT_MAGNET_STARS_ENABLED) |
||||
{ |
||||
// Get magnet star info |
||||
if ($magnetStar = $db->getMagnetStar($push->magnetStarId)) |
||||
{ |
||||
// Dump public data only |
||||
if (!empty($public['magnet'][$magnetStar->magnetId]) && |
||||
!empty($public['user'][$magnetStar->userId])) |
||||
{ |
||||
$request['magnetStar'] = (object) |
||||
[ |
||||
'magnetStarId' => (int) $magnetStar->magnetStarId, |
||||
'userId' => (int) $magnetStar->userId, |
||||
'magnetId' => (int) $magnetStar->magnetId, |
||||
'value' => (bool) $magnetStar->value, |
||||
'timeAdded' => (int) $magnetStar->timeAdded, |
||||
]; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Magnet view request |
||||
if (!empty($push->magnetViewId) && API_EXPORT_MAGNET_VIEWS_ENABLED) |
||||
{ |
||||
// Get magnet view info |
||||
if ($magnetView = $db->getMagnetView($push->magnetViewId)) |
||||
{ |
||||
// Dump public data only |
||||
if (!empty($public['magnet'][$magnetView->magnetId]) && |
||||
!empty($public['user'][$magnetView->userId])) |
||||
{ |
||||
$request['magnetView'] = (object) |
||||
[ |
||||
'magnetViewId' => (int) $magnetView->magnetViewId, |
||||
'userId' => (int) $magnetView->userId, |
||||
'magnetId' => (int) $magnetView->magnetId, |
||||
'timeAdded' => (int) $magnetView->timeAdded, |
||||
]; |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Check request |
||||
if (empty($request)) |
||||
{ |
||||
// Amy request data match conditions, skip |
||||
|
||||
continue; |
||||
} |
||||
|
||||
// Send push data |
||||
foreach (json_decode( |
||||
file_get_contents(__DIR__ . '/../../config/nodes.json') |
||||
) as $node) |
||||
{ |
||||
// Manifest exists |
||||
if (empty($node->manifest)) |
||||
{ |
||||
$debug['dump']['warning'][] = sprintf( |
||||
_('Manifest URL not provided: %s'), |
||||
$node |
||||
); |
||||
|
||||
continue; |
||||
} |
||||
|
||||
// Skip non-condition addresses |
||||
$error = []; |
||||
|
||||
if (!Valid::url($node->manifest, $error)) |
||||
{ |
||||
$debug['dump'][$node->manifest]['warning'][] = sprintf( |
||||
_('Manifest URL invalid: %s'), |
||||
print_r( |
||||
$error, |
||||
true |
||||
) |
||||
); |
||||
|
||||
continue; |
||||
} |
||||
|
||||
// Skip current host |
||||
$thisUrl = Yggverse\Parser\Url::parse(WEBSITE_URL); |
||||
$manifestUrl = Yggverse\Parser\Url::parse($node->manifest); |
||||
|
||||
if (empty($thisUrl->host->name) || |
||||
empty($manifestUrl->host->name) || |
||||
$manifestUrl->host->name == $thisUrl->host->name) // @TODO some mirrors could be available, improve condition |
||||
{ |
||||
// No debug warnings in this case, continue next item |
||||
|
||||
continue; |
||||
} |
||||
|
||||
// Get node manifest |
||||
// @TODO cache to prevent extra-queries as usually this script running every minute |
||||
$curl = new Curl($node->manifest, API_USER_AGENT); |
||||
|
||||
$debug['http']['total']++; |
||||
|
||||
if (200 != $code = $curl->getCode()) |
||||
{ |
||||
$debug['dump'][$node->manifest]['warning'][] = sprintf( |
||||
_('Manifest unreachable with code: "%s"'), |
||||
$code |
||||
); |
||||
|
||||
continue; |
||||
} |
||||
|
||||
if (!$manifest = $curl->getResponse()) |
||||
{ |
||||
$debug['dump'][$node->manifest]['warning'][] = sprintf( |
||||
_('Manifest URL "%s" has broken response'), |
||||
$node->manifest |
||||
); |
||||
|
||||
continue; |
||||
} |
||||
|
||||
// API channel not exists |
||||
if (empty($manifest->import)) |
||||
{ |
||||
$debug['dump'][$node->manifest]['warning'][] = sprintf( |
||||
_('Manifest import URL not provided: %s'), |
||||
$node |
||||
); |
||||
|
||||
continue; |
||||
} |
||||
|
||||
// Push API channel not exists |
||||
if (empty($manifest->import->push)) |
||||
{ |
||||
$debug['dump'][$manifest->import->push]['warning'][] = sprintf( |
||||
_('Manifest import push URL not provided: %s'), |
||||
$node |
||||
); |
||||
|
||||
continue; |
||||
} |
||||
|
||||
// Skip sending to non-condition addresses |
||||
$error = []; |
||||
|
||||
if (!Valid::url($manifest->import->push, $error)) |
||||
{ |
||||
$debug['dump'][$manifest->import->push]['warning'][] = sprintf( |
||||
_('Manifest import push URL invalid: %s'), |
||||
print_r( |
||||
$error, |
||||
true |
||||
) |
||||
); |
||||
|
||||
continue; |
||||
} |
||||
|
||||
// Skip current host |
||||
$thisUrl = Yggverse\Parser\Url::parse(WEBSITE_URL); |
||||
$pushUrl = Yggverse\Parser\Url::parse($manifest->import->push); |
||||
|
||||
if (empty($thisUrl->host->name) || |
||||
empty($pushUrl->host->name) || |
||||
$pushUrl->host->name == $thisUrl->host->name) // @TODO some mirrors could be available, improve condition |
||||
{ |
||||
// No debug warnings in this case, continue next item |
||||
|
||||
continue; |
||||
} |
||||
|
||||
// @TODO add recipient manifest conditions check to not disturb it API without needs |
||||
|
||||
// Send push request |
||||
$debug['dump'][$manifest->import->push]['request'][] = $request; |
||||
|
||||
$curl = new Curl( |
||||
$manifest->import->push, |
||||
API_USER_AGENT, |
||||
[ |
||||
'data' => json_encode($request) |
||||
] |
||||
); |
||||
|
||||
$debug['http']['total']++; |
||||
|
||||
if (200 != $code = $curl->getCode()) |
||||
{ |
||||
$debug['dump'][$manifest->import->push]['warning'][] = sprintf( |
||||
_('Server returned code "%s"'), |
||||
$code |
||||
); |
||||
|
||||
continue; |
||||
} |
||||
|
||||
if (!$response = $curl->getResponse()) |
||||
{ |
||||
$debug['dump'][$manifest->import->push]['warning'][] = _('Could not receive server response'); |
||||
|
||||
continue; |
||||
} |
||||
|
||||
$debug['dump'][$manifest->import->push]['response'][] = $response; |
||||
} |
||||
|
||||
// Drop processed item from queue |
||||
unset($memoryApiExportPush[$id]); |
||||
} |
||||
|
||||
// Update memory pool |
||||
$memory->set('api.export.push', $memoryApiExportPush, 3600); |
||||
} |
||||
|
||||
// Export push disabled, free api.export.push pool |
||||
else |
||||
{ |
||||
$memory->delete('api.export.push'); |
||||
} |
||||
|
||||
// Debug output |
||||
$debug['time']['total'] = microtime(true) - $debug['time']['total']; |
||||
|
||||
$debug['memory']['total'] = memory_get_usage() - $debug['memory']['start']; |
||||
$debug['memory']['peaks'] = memory_get_peak_usage(); |
||||
|
||||
$debug['db']['total']['select'] = $db->getDebug()->query->select->total; |
||||
$debug['db']['total']['insert'] = $db->getDebug()->query->insert->total; |
||||
$debug['db']['total']['update'] = $db->getDebug()->query->update->total; |
||||
$debug['db']['total']['delete'] = $db->getDebug()->query->delete->total; |
||||
|
||||
print_r($debug); |
||||
|
||||
// Debug log |
||||
if (LOG_CRONTAB_EXPORT_PUSH_ENABLED) |
||||
{ |
||||
@mkdir(LOG_DIRECTORY, 0770, true); |
||||
|
||||
if ($handle = fopen(LOG_DIRECTORY . '/' . LOG_CRONTAB_EXPORT_PUSH_FILENAME, 'a+')) |
||||
{ |
||||
fwrite($handle, print_r($debug, true)); |
||||
fclose($handle); |
||||
|
||||
chmod(LOG_DIRECTORY . '/' . LOG_CRONTAB_EXPORT_PUSH_FILENAME, 0770); |
||||
} |
||||
} |
@ -1,158 +0,0 @@
@@ -1,158 +0,0 @@
|
||||
<?php |
||||
|
||||
// Lock multi-thread execution |
||||
$semaphore = sem_get(crc32('yggtracker.crontab.scrape'), 1); |
||||
|
||||
if (false === sem_acquire($semaphore, true)) { |
||||
|
||||
exit (PHP_EOL . 'yggtracker.crontab.scrape process locked by another thread.' . PHP_EOL); |
||||
} |
||||
|
||||
// Bootstrap |
||||
require_once __DIR__ . '/../config/bootstrap.php'; |
||||
|
||||
// Init Debug |
||||
$debug = [ |
||||
'time' => [ |
||||
'ISO8601' => date('c'), |
||||
'total' => microtime(true), |
||||
], |
||||
'http' => |
||||
[ |
||||
'total' => 0, |
||||
], |
||||
'memory' => |
||||
[ |
||||
'start' => memory_get_usage(), |
||||
'total' => 0, |
||||
'peaks' => 0 |
||||
], |
||||
]; |
||||
|
||||
// Init Scraper |
||||
try { |
||||
|
||||
$scraper = new Scrapeer\Scraper(); |
||||
|
||||
} catch(Exception $e) { |
||||
|
||||
var_dump($e); |
||||
|
||||
exit; |
||||
} |
||||
|
||||
// Begin |
||||
try { |
||||
|
||||
$db->beginTransaction(); |
||||
|
||||
// Reset time offline by timeout |
||||
$db->resetMagnetToAddressTrackerTimeOfflineByTimeout( |
||||
CRAWLER_SCRAPE_TIME_OFFLINE_TIMEOUT |
||||
); |
||||
|
||||
foreach ($db->getMagnetToAddressTrackerScrapeQueue(CRAWLER_SCRAPE_QUEUE_LIMIT) as $queue) |
||||
{ |
||||
$hashes = []; |
||||
foreach ($db->findMagnetToInfoHashByMagnetId($queue->magnetId) as $result) |
||||
{ |
||||
$hashes[] = $db->getInfoHash($result->infoHashId)->value; |
||||
} |
||||
|
||||
if ($addressTracker = $db->getAddressTracker($queue->addressTrackerId)) |
||||
{ |
||||
// Build url |
||||
$scheme = $db->getScheme($addressTracker->schemeId); |
||||
$host = $db->getHost($addressTracker->hostId); |
||||
$port = $db->getPort($addressTracker->portId); |
||||
$uri = $db->getUri($addressTracker->uriId); |
||||
|
||||
$url = $port->value ? sprintf('%s://%s:%s%s', $scheme->value, |
||||
$host->value, |
||||
$port->value, |
||||
$uri->value) : sprintf('%s://%s%s', $scheme->value, |
||||
$host->value, |
||||
$uri->value); |
||||
|
||||
foreach ($hashes as $hash) |
||||
{ |
||||
if ($scrape = $scraper->scrape([$hash], [$url], null, 1)) |
||||
{ |
||||
$db->updateMagnetToAddressTrackerTimeOffline( |
||||
$queue->magnetToAddressTrackerId, |
||||
null |
||||
); |
||||
|
||||
if (isset($scrape[$hash]['seeders'])) |
||||
{ |
||||
$db->updateMagnetToAddressTrackerSeeders( |
||||
$queue->magnetToAddressTrackerId, |
||||
(int) $scrape[$hash]['seeders'], |
||||
time() |
||||
); |
||||
} |
||||
|
||||
if (isset($scrape[$hash]['completed'])) |
||||
{ |
||||
$db->updateMagnetToAddressTrackerCompleted( |
||||
$queue->magnetToAddressTrackerId, |
||||
(int) $scrape[$hash]['completed'], |
||||
time() |
||||
); |
||||
} |
||||
|
||||
if (isset($scrape[$hash]['leechers'])) |
||||
{ |
||||
$db->updateMagnetToAddressTrackerLeechers( |
||||
$queue->magnetToAddressTrackerId, |
||||
(int) $scrape[$hash]['leechers'], |
||||
time() |
||||
); |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
$db->updateMagnetToAddressTrackerTimeOffline( |
||||
$queue->magnetToAddressTrackerId, |
||||
time() |
||||
); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
$db->commit(); |
||||
|
||||
} catch (EXception $e) { |
||||
|
||||
$db->rollback(); |
||||
|
||||
var_dump($e); |
||||
} |
||||
|
||||
// Debug output |
||||
$debug['time']['total'] = microtime(true) - $debug['time']['total']; |
||||
|
||||
$debug['memory']['total'] = memory_get_usage() - $debug['memory']['start']; |
||||
$debug['memory']['peaks'] = memory_get_peak_usage(); |
||||
|
||||
$debug['db']['total']['select'] = $db->getDebug()->query->select->total; |
||||
$debug['db']['total']['insert'] = $db->getDebug()->query->insert->total; |
||||
$debug['db']['total']['update'] = $db->getDebug()->query->update->total; |
||||
$debug['db']['total']['delete'] = $db->getDebug()->query->delete->total; |
||||
|
||||
print_r($debug); |
||||
|
||||
// Debug log |
||||
if (LOG_CRONTAB_SCRAPE_ENABLED) |
||||
{ |
||||
@mkdir(LOG_DIRECTORY, 0774, true); |
||||
|
||||
if ($handle = fopen(LOG_DIRECTORY . '/' . LOG_CRONTAB_SCRAPE_FILENAME, 'a+')) |
||||
{ |
||||
fwrite($handle, print_r($debug, true)); |
||||
fclose($handle); |
||||
|
||||
chmod(LOG_DIRECTORY . '/' . LOG_CRONTAB_SCRAPE_FILENAME, 0774); |
||||
} |
||||
} |
@ -1,86 +0,0 @@
@@ -1,86 +0,0 @@
|
||||
<?php |
||||
|
||||
// Lock multi-thread execution |
||||
$semaphore = sem_get(crc32('yggtracker.crontab.sitemap'), 1); |
||||
|
||||
if (false === sem_acquire($semaphore, true)) { |
||||
|
||||
exit (PHP_EOL . 'yggtracker.crontab.sitemap process locked by another thread.' . PHP_EOL); |
||||
} |
||||
|
||||
// Bootstrap |
||||
require_once __DIR__ . '/../config/bootstrap.php'; |
||||
|
||||
// Init Debug |
||||
$debug = [ |
||||
'time' => [ |
||||
'ISO8601' => date('c'), |
||||
'total' => microtime(true), |
||||
], |
||||
'http' => |
||||
[ |
||||
'total' => 0, |
||||
], |
||||
'memory' => |
||||
[ |
||||
'start' => memory_get_usage(), |
||||
'total' => 0, |
||||
'peaks' => 0 |
||||
], |
||||
]; |
||||
|
||||
// Begin |
||||
try { |
||||
|
||||
// Delete cache |
||||
@unlink(__DIR__ . '/../public/sitemap.xml'); |
||||
|
||||
if ($handle = fopen(__DIR__ . '/../public/sitemap.xml', 'w+')) |
||||
{ |
||||
|
||||
fwrite($handle, '<?xml version="1.0" encoding="UTF-8"?>'); |
||||
fwrite($handle, '<urlset>'); |
||||
|
||||
foreach ($db->getMagnets() as $magnet) |
||||
{ |
||||
if ($magnet->public && $magnet->approved) |
||||
{ |
||||
fwrite($handle, sprintf('<url><loc>%s/magnet.php?magnetId=%s</loc></url>', WEBSITE_URL, $magnet->magnetId)); |
||||
} |
||||
} |
||||
|
||||
fwrite($handle, '</urlset>'); |
||||
fclose($handle); |
||||
} |
||||
|
||||
} catch (EXception $e) { |
||||
|
||||
var_dump($e); |
||||
} |
||||
|
||||
// Debug output |
||||
$debug['time']['total'] = microtime(true) - $debug['time']['total']; |
||||
|
||||
$debug['memory']['total'] = memory_get_usage() - $debug['memory']['start']; |
||||
$debug['memory']['peaks'] = memory_get_peak_usage(); |
||||
|
||||
$debug['db']['total']['select'] = $db->getDebug()->query->select->total; |
||||
$debug['db']['total']['insert'] = $db->getDebug()->query->insert->total; |
||||
$debug['db']['total']['update'] = $db->getDebug()->query->update->total; |
||||
$debug['db']['total']['delete'] = $db->getDebug()->query->delete->total; |
||||
|
||||
print_r($debug); |
||||
|
||||
// Debug log |
||||
if (LOG_CRONTAB_SITEMAP_ENABLED) |
||||
{ |
||||
@mkdir(LOG_DIRECTORY, 0774, true); |
||||
|
||||
if ($handle = fopen(LOG_DIRECTORY . '/' . LOG_CRONTAB_SITEMAP_FILENAME, 'a+')) |
||||
{ |
||||
fwrite($handle, print_r($debug, true)); |
||||
fclose($handle); |
||||
|
||||
chmod(LOG_DIRECTORY . '/' . LOG_CRONTAB_SITEMAP_FILENAME, 0774); |
||||
} |
||||
} |
@ -1,97 +0,0 @@
@@ -1,97 +0,0 @@
|
||||
<?php |
||||
|
||||
class Curl |
||||
{ |
||||
private $_connection; |
||||
private $_response; |
||||
|
||||
public function __construct(string $url, |
||||
string $userAgent = 'YGGtracker', |
||||
array $post = [], |
||||
int $connectTimeout = 10, |
||||
bool $header = false, |
||||
bool $followLocation = false, |
||||
int $maxRedirects = 10, |
||||
bool $sslVerifyHost = false, |
||||
bool $sslVerifyPeer = false) |
||||
{ |
||||
$this->_connection = curl_init($url); |
||||
|
||||
if ($userAgent) |
||||
{ |
||||
curl_setopt($this->_connection, CURLOPT_USERAGENT, $userAgent); |
||||
} |
||||
|
||||
if (!empty($post)) |
||||
{ |
||||
curl_setopt($this->_connection, CURLOPT_POST, true); |
||||
curl_setopt($this->_connection, CURLOPT_POSTFIELDS, http_build_query($post)); |
||||
} |
||||
|
||||
if ($header) { |
||||
curl_setopt($this->_connection, CURLOPT_HEADER, true); |
||||
} |
||||
|
||||
if ($followLocation) { |
||||
curl_setopt($this->_connection, CURLOPT_FOLLOWLOCATION, true); |
||||
curl_setopt($this->_connection, CURLOPT_MAXREDIRS, $maxRedirects); |
||||
} |
||||
|
||||
curl_setopt($this->_connection, CURLOPT_FRESH_CONNECT, true); |
||||
curl_setopt($this->_connection, CURLOPT_RETURNTRANSFER, true); |
||||
curl_setopt($this->_connection, CURLOPT_CONNECTTIMEOUT, $connectTimeout); |
||||
curl_setopt($this->_connection, CURLOPT_TIMEOUT, $connectTimeout); |
||||
curl_setopt($this->_connection, CURLOPT_SSL_VERIFYHOST, $sslVerifyHost); |
||||
curl_setopt($this->_connection, CURLOPT_SSL_VERIFYPEER, $sslVerifyPeer); |
||||
|
||||
$this->_response = curl_exec($this->_connection); |
||||
} |
||||
|
||||
public function __destruct() |
||||
{ |
||||
curl_close($this->_connection); |
||||
} |
||||
|
||||
public function getError() |
||||
{ |
||||
if (curl_errno($this->_connection)) |
||||
{ |
||||
return curl_errno($this->_connection); |
||||
} |
||||
|
||||
else |
||||
{ |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
public function getCode() |
||||
{ |
||||
return curl_getinfo($this->_connection, CURLINFO_HTTP_CODE); |
||||
} |
||||
|
||||
public function getContentType() |
||||
{ |
||||
return curl_getinfo($this->_connection, CURLINFO_CONTENT_TYPE); |
||||
} |
||||
|
||||
public function getSizeDownload() |
||||
{ |
||||
return curl_getinfo($this->_connection, CURLINFO_SIZE_DOWNLOAD); |
||||
} |
||||
|
||||
public function getSizeRequest() |
||||
{ |
||||
return curl_getinfo($this->_connection, CURLINFO_REQUEST_SIZE); |
||||
} |
||||
|
||||
public function getTotalTime() |
||||
{ |
||||
return curl_getinfo($this->_connection, CURLINFO_TOTAL_TIME_T); |
||||
} |
||||
|
||||
public function getResponse(bool $json = true) |
||||
{ |
||||
return $json ? json_decode($this->_response) : $this->_response; |
||||
} |
||||
} |
@ -1,27 +0,0 @@
@@ -1,27 +0,0 @@
|
||||
<?php |
||||
|
||||
class Environment |
||||
{ |
||||
public static function config(string $name) : object |
||||
{ |
||||
$config = __DIR__ . '/../config/' . $name . '.json'; |
||||
|
||||
if (file_exists(__DIR__ . '/../config/.env')) |
||||
{ |
||||
$environment = file_get_contents(__DIR__ . '/../config/.env'); |
||||
|
||||
$filename = __DIR__ . '/../config/' . $environment . '/' . $name . '.json'; |
||||
|
||||
if (file_exists($filename)) |
||||
{ |
||||
$config = $filename; |
||||
} |
||||
} |
||||
|
||||
return (object) json_decode( |
||||
file_get_contents( |
||||
$config |
||||
) |
||||
); |
||||
} |
||||
} |
@ -1,48 +0,0 @@
@@ -1,48 +0,0 @@
|
||||
<?php |
||||
|
||||
class Filter |
||||
{ |
||||
public static function magnetTitle(mixed $value) : string |
||||
{ |
||||
$value = trim( |
||||
strip_tags( |
||||
html_entity_decode($value) |
||||
) |
||||
); |
||||
|
||||
return (string) $value; |
||||
} |
||||
|
||||
public static function magnetPreview(mixed $value) : string |
||||
{ |
||||
$value = trim( |
||||
strip_tags( |
||||
html_entity_decode($value) |
||||
) |
||||
); |
||||
|
||||
return (string) $value; |
||||
} |
||||
|
||||
public static function magnetDescription(mixed $value) : string |
||||
{ |
||||
$value = trim( |
||||
strip_tags( |
||||
html_entity_decode($value) |
||||
) |
||||
); |
||||
|
||||
return (string) $value; |
||||
} |
||||
|
||||
public static function magnetDn(mixed $value) : string |
||||
{ |
||||
$value = trim( |
||||
strip_tags( |
||||
html_entity_decode($value) |
||||
) |
||||
); |
||||
|
||||
return (string) $value; |
||||
} |
||||
} |
@ -1,692 +0,0 @@
@@ -1,692 +0,0 @@
|
||||
<?php |
||||
/** |
||||
* Scrapeer, a tiny PHP library that lets you scrape |
||||
* HTTP(S) and UDP trackers for torrent information. |
||||
* |
||||
* This file is extensively based on Johannes Zinnau's |
||||
* work, which can be found at https://goo.gl/7hyjde |
||||
* |
||||
* Licensed under a Creative Commons |
||||
* Attribution-ShareAlike 3.0 Unported License |
||||
* http://creativecommons.org/licenses/by-sa/3.0 |
||||
* |
||||
* @package Scrapeer |
||||
*/ |
||||
|
||||
namespace Scrapeer; |
||||
|
||||
/** |
||||
* The one and only class you'll ever need. |
||||
*/ |
||||
class Scraper { |
||||
|
||||
/** |
||||
* Current version of Scrapeer |
||||
* |
||||
* @var string |
||||
*/ |
||||
const VERSION = '0.5.4'; |
||||
|
||||
/** |
||||
* Array of errors |
||||
* |
||||
* @var array |
||||
*/ |
||||
private $errors = array(); |
||||
|
||||
/** |
||||
* Array of infohashes to scrape |
||||
* |
||||
* @var array |
||||
*/ |
||||
private $infohashes = array(); |
||||
|
||||
/** |
||||
* Timeout for a single tracker |
||||
* |
||||
* @var int |
||||
*/ |
||||
private $timeout; |
||||
|
||||
/** |
||||
* Initiates the scraper |
||||
* |
||||
* @throws \RangeException In case of invalid amount of info-hashes. |
||||
* |
||||
* @param array|string $hashes List (>1) or string of infohash(es). |
||||
* @param array|string $trackers List (>1) or string of tracker(s). |
||||
* @param int|null $max_trackers Optional. Maximum number of trackers to be scraped, Default all. |
||||
* @param int $timeout Optional. Maximum time for each tracker scrape in seconds, Default 2. |
||||
* @param bool $announce Optional. Use announce instead of scrape, Default false. |
||||
* @return array List of results. |
||||
*/ |
||||
public function scrape( $hashes, $trackers, $max_trackers = null, $timeout = 2, $announce = false ) { |
||||
$final_result = array(); |
||||
|
||||
if ( empty( $trackers ) ) { |
||||
$this->errors[] = 'No tracker specified, aborting.'; |
||||
return $final_result; |
||||
} else if ( ! is_array( $trackers ) ) { |
||||
$trackers = array( $trackers ); |
||||
} |
||||
|
||||
if ( is_int( $timeout ) ) { |
||||
$this->timeout = $timeout; |
||||
} else { |
||||
$this->timeout = 2; |
||||
$this->errors[] = 'Timeout must be an integer. Using default value.'; |
||||
} |
||||
|
||||
try { |
||||
$this->infohashes = $this->normalize_infohashes( $hashes ); |
||||
} catch ( \RangeException $e ) { |
||||
$this->errors[] = $e->getMessage(); |
||||
return $final_result; |
||||
} |
||||
|
||||
$max_iterations = is_int( $max_trackers ) ? $max_trackers : count( $trackers ); |
||||
foreach ( $trackers as $index => $tracker ) { |
||||
if ( ! empty( $this->infohashes ) && $index < $max_iterations ) { |
||||
$info = parse_url( $tracker ); |
||||
$protocol = $info['scheme']; |
||||
$host = $info['host']; |
||||
if ( empty( $protocol ) || empty( $host ) ) { |
||||
$this->errors[] = 'Skipping invalid tracker (' . $tracker . ').'; |
||||
continue; |
||||
} |
||||
|
||||
$port = isset( $info['port'] ) ? $info['port'] : null; |
||||
$path = isset( $info['path'] ) ? $info['path'] : null; |
||||
$passkey = $this->get_passkey( $path ); |
||||
$result = $this->try_scrape( $protocol, $host, $port, $passkey, $announce ); |
||||
$final_result = array_merge( $final_result, $result ); |
||||
continue; |
||||
} |
||||
break; |
||||
} |
||||
return $final_result; |
||||
} |
||||
|
||||
/** |
||||
* Normalizes the given hashes |
||||
* |
||||
* @throws \RangeException If amount of valid infohashes > 64 or < 1. |
||||
* |
||||
* @param array $infohashes List of infohash(es). |
||||
* @return array Normalized infohash(es). |
||||
*/ |
||||
private function normalize_infohashes( $infohashes ) { |
||||
if ( ! is_array( $infohashes ) ) { |
||||
$infohashes = array( $infohashes ); |
||||
} |
||||
|
||||
foreach ( $infohashes as $index => $infohash ) { |
||||
if ( ! preg_match( '/^[a-f0-9]{40}$/i', $infohash ) ) { |
||||
$this->errors[] = 'Invalid infohash skipped (' . $infohash . ').'; |
||||
unset( $infohashes[ $index ] ); |
||||
} |
||||
} |
||||
|
||||
$total_infohashes = count( $infohashes ); |
||||
if ( $total_infohashes > 64 || $total_infohashes < 1 ) { |
||||
throw new \RangeException( 'Invalid amount of valid infohashes (' . $total_infohashes . ').' ); |
||||
} |
||||
|
||||
$infohashes = array_values( $infohashes ); |
||||
|
||||
return $infohashes; |
||||
} |
||||
|
||||
/** |
||||
* Returns the passkey found in the scrape request. |
||||
* |
||||
* @param string $path Path from the scrape request. |
||||
* @return string Passkey or empty string. |
||||
*/ |
||||
private function get_passkey( $path ) { |
||||
if ( ! is_null( $path ) && preg_match( '/[a-z0-9]{32}/i', $path, $matches ) ) { |
||||
return '/' . $matches[0]; |
||||
} |
||||
|
||||
return ''; |
||||
} |
||||
|
||||
/** |
||||
* Tries to scrape with a single tracker. |
||||
* |
||||
* @throws \Exception In case of unsupported protocol. |
||||
* |
||||
* @param string $protocol Protocol of the tracker. |
||||
* @param string $host Domain or address of the tracker. |
||||
* @param int $port Optional. Port number of the tracker. |
||||
* @param string $passkey Optional. Passkey provided in the scrape request. |
||||
* @param bool $announce Optional. Use announce instead of scrape, Default false. |
||||
* @return array List of results. |
||||
*/ |
||||
private function try_scrape( $protocol, $host, $port, $passkey, $announce ) { |
||||
$infohashes = $this->infohashes; |
||||
$this->infohashes = array(); |
||||
$results = array(); |
||||
try { |
||||
switch ( $protocol ) { |
||||
case 'udp': |
||||
$port = isset( $port ) ? $port : 80; |
||||
$results = $this->scrape_udp( $infohashes, $host, $port, $announce ); |
||||
break; |
||||
case 'http': |
||||
$port = isset( $port ) ? $port : 80; |
||||
$results = $this->scrape_http( $infohashes, $protocol, $host, $port, $passkey, $announce ); |
||||
break; |
||||
case 'https': |
||||
$port = isset( $port ) ? $port : 443; |
||||
$results = $this->scrape_http( $infohashes, $protocol, $host, $port, $passkey, $announce ); |
||||
break; |
||||
default: |
||||
throw new \Exception( 'Unsupported protocol (' . $protocol . '://' . $host . ').' ); |
||||
} |
||||
} catch ( \Exception $e ) { |
||||
$this->infohashes = $infohashes; |
||||
$this->errors[] = $e->getMessage(); |
||||
} |
||||
return $results; |
||||
} |
||||
|
||||
/** |
||||
* Initiates the HTTP(S) scraping |
||||
* |
||||
* @param array|string $infohashes List (>1) or string of infohash(es). |
||||
* @param string $protocol Protocol to use for the scraping. |
||||
* @param string $host Domain or IP address of the tracker. |
||||
* @param int $port Optional. Port number of the tracker, Default 80 (HTTP) or 443 (HTTPS). |
||||
* @param string $passkey Optional. Passkey provided in the scrape request. |
||||
* @param bool $announce Optional. Use announce instead of scrape, Default false. |
||||
* @return array List of results. |
||||
*/ |
||||
private function scrape_http( $infohashes, $protocol, $host, $port, $passkey, $announce ) { |
||||
if ( true === $announce ) { |
||||
$response = $this->http_announce( $infohashes, $protocol, $host, $port, $passkey ); |
||||
} else { |
||||
$query = $this->http_query( $infohashes, $protocol, $host, $port, $passkey ); |
||||
$response = $this->http_request( $query, $host, $port ); |
||||
} |
||||
$results = $this->http_data( $response, $infohashes, $host ); |
||||
|
||||
return $results; |
||||
} |
||||
|
||||
/** |
||||
* Builds the HTTP(S) query |
||||
* |
||||
* @param array|string $infohashes List (>1) or string of infohash(es). |
||||
* @param string $protocol Protocol to use for the scraping. |
||||
* @param string $host Domain or IP address of the tracker. |
||||
* @param int $port Port number of the tracker, Default 80 (HTTP) or 443 (HTTPS). |
||||
* @param string $passkey Optional. Passkey provided in the scrape request. |
||||
* @return string Request query. |
||||
*/ |
||||
private function http_query( $infohashes, $protocol, $host, $port, $passkey ) { |
||||
$tracker_url = $protocol . '://' . $host . ':' . $port . $passkey; |
||||
$scrape_query = ''; |
||||
|
||||
foreach ( $infohashes as $index => $infohash ) { |
||||
if ( $index > 0 ) { |
||||
$scrape_query .= '&info_hash=' . urlencode( pack( 'H*', $infohash ) ); |
||||
} else { |
||||
$scrape_query .= '/scrape?info_hash=' . urlencode( pack( 'H*', $infohash ) ); |
||||
} |
||||
} |
||||
$request_query = $tracker_url . $scrape_query; |
||||
|
||||
return $request_query; |
||||
} |
||||
|
||||
/** |
||||
* Executes the query and returns the result |
||||
* |
||||
* @throws \Exception If the connection can't be established. |
||||
* @throws \Exception If the response isn't valid. |
||||
* |
||||
* @param string $query The query that will be executed. |
||||
* @param string $host Domain or IP address of the tracker. |
||||
* @param int $port Port number of the tracker, Default 80 (HTTP) or 443 (HTTPS). |
||||
* @return string Request response. |
||||
*/ |
||||
private function http_request( $query, $host, $port ) { |
||||
$context = stream_context_create( array( |
||||
'http' => array( |
||||
'timeout' => $this->timeout, |
||||
), |
||||
)); |
||||
|
||||
if ( false === ( $response = @file_get_contents( $query, false, $context ) ) ) { |
||||
throw new \Exception( 'Invalid scrape connection (' . $host . ':' . $port . ').' ); |
||||
} |
||||
|
||||
if ( substr( $response, 0, 12 ) !== 'd5:filesd20:' ) { |
||||
throw new \Exception( 'Invalid scrape response (' . $host . ':' . $port . ').' ); |
||||
} |
||||
|
||||
return $response; |
||||
} |
||||
|
||||
/** |
||||
* Builds the query, sends the announce request and returns the data |
||||
* |
||||
* @throws \Exception If the connection can't be established. |
||||
* |
||||
* @param array|string $infohashes List (>1) or string of infohash(es). |
||||
* @param string $protocol Protocol to use for the scraping. |
||||
* @param string $host Domain or IP address of the tracker. |
||||
* @param int $port Port number of the tracker, Default 80 (HTTP) or 443 (HTTPS). |
||||
* @param string $passkey Optional. Passkey provided in the scrape request. |
||||
* @return string Request response. |
||||
*/ |
||||
private function http_announce( $infohashes, $protocol, $host, $port, $passkey ) { |
||||
$tracker_url = $protocol . '://' . $host . ':' . $port . $passkey; |
||||
$context = stream_context_create( array( |
||||
'http' => array( |
||||
'timeout' => $this->timeout, |
||||
), |
||||
)); |
||||
|
||||
$response_data = ''; |
||||
foreach ( $infohashes as $infohash ) { |
||||
$query = $tracker_url . '/announce?info_hash=' . urlencode( pack( 'H*', $infohash ) ); |
||||
if ( false === ( $response = @file_get_contents( $query, false, $context ) ) ) { |
||||
throw new \Exception( 'Invalid announce connection (' . $host . ':' . $port . ').' ); |
||||
} |
||||
|
||||
if ( substr( $response, 0, 12 ) !== 'd8:completei' || |
||||
substr( $response, 0, 46 ) === 'd8:completei0e10:downloadedi0e10:incompletei1e' ) { |
||||
continue; |
||||
} |
||||
|
||||
$ben_hash = '20:' . pack( 'H*', $infohash ) . 'd'; |
||||
$response_data .= $ben_hash . $response; |
||||
} |
||||
|
||||
return $response_data; |
||||
} |
||||
|
||||
/** |
||||
* Parses the response and returns the data |
||||
* |
||||
* @param string $response The response that will be parsed. |
||||
* @param array $infohashes List of infohash(es). |
||||
* @param string $host Domain or IP address of the tracker. |
||||
* @return array Parsed data. |
||||
*/ |
||||
private function http_data( $response, $infohashes, $host ) { |
||||
$torrents_data = array(); |
||||
|
||||
foreach ( $infohashes as $infohash ) { |
||||
$ben_hash = '20:' . pack( 'H*', $infohash ) . 'd'; |
||||
$start_pos = strpos( $response, $ben_hash ); |
||||
if ( false !== $start_pos ) { |
||||
$start = $start_pos + 24; |
||||
$head = substr( $response, $start ); |
||||
$end = strpos( $head, 'ee' ) + 1; |
||||
$data = substr( $response, $start, $end ); |
||||
|
||||
$seeders = '8:completei'; |
||||
$torrent_info['seeders'] = $this->get_information( $data, $seeders, 'e' ); |
||||
|
||||
$completed = '10:downloadedi'; |
||||
$torrent_info['completed'] = $this->get_information( $data, $completed, 'e' ); |
||||
|
||||
$leechers = '10:incompletei'; |
||||
$torrent_info['leechers'] = $this->get_information( $data, $leechers, 'e' ); |
||||
|
||||
$torrents_data[ $infohash ] = $torrent_info; |
||||
} else { |
||||
$this->collect_infohash( $infohash ); |
||||
$this->errors[] = 'Invalid infohash (' . $infohash . ') for tracker: ' . $host . '.'; |
||||
} |
||||
} |
||||
|
||||
return $torrents_data; |
||||
} |
||||
|
||||
/** |
||||
* Parses a string and returns the data between $start and $end. |
||||
* |
||||
* @param string $data The data that will be parsed. |
||||
* @param string $start Beginning part of the data. |
||||
* @param string $end Ending part of the data. |
||||
* @return int Parsed information or 0. |
||||
*/ |
||||
private function get_information( $data, $start, $end ) { |
||||
$start_pos = strpos( $data, $start ); |
||||
if ( false !== $start_pos ) { |
||||
$start = $start_pos + strlen( $start ); |
||||
$head = substr( $data, $start ); |
||||
$end = strpos( $head, $end ); |
||||
$information = substr( $data, $start, $end ); |
||||
|
||||
return (int) $information; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
/** |
||||
* Initiates the UDP scraping |
||||
* |
||||
* @param array|string $infohashes List (>1) or string of infohash(es). |
||||
* @param string $host Domain or IP address of the tracker. |
||||
* @param int $port Optional. Port number of the tracker, Default 80. |
||||
* @param bool $announce Optional. Use announce instead of scrape, Default false. |
||||
* @return array List of results. |
||||
*/ |
||||
private function scrape_udp( $infohashes, $host, $port, $announce ) { |
||||
list( $socket, $transaction_id, $connection_id ) = $this->prepare_udp( $host, $port ); |
||||
|
||||
if ( true === $announce ) { |
||||
$response = $this->udp_announce( $socket, $infohashes, $connection_id ); |
||||
$keys = 'Nleechers/Nseeders'; |
||||
$start = 12; |
||||
$end = 16; |
||||
$offset = 20; |
||||
} else { |
||||
$response = $this->udp_scrape( $socket, $infohashes, $connection_id, $transaction_id, $host, $port ); |
||||
$keys = 'Nseeders/Ncompleted/Nleechers'; |
||||
$start = 8; |
||||
$end = $offset = 12; |
||||
} |
||||
$results = $this->udp_scrape_data( $response, $infohashes, $host, $keys, $start, $end, $offset ); |
||||
|
||||
return $results; |
||||
} |
||||
|
||||
/** |
||||
* Prepares the UDP connection |
||||
* |
||||
* @param string $host Domain or IP address of the tracker. |
||||
* @param int $port Optional. Port number of the tracker, Default 80. |
||||
* @return array Created socket, transaction ID and connection ID. |
||||
*/ |
||||
private function prepare_udp( $host, $port ) { |
||||
$socket = $this->udp_create_connection( $host, $port ); |
||||
$transaction_id = $this->udp_connection_request( $socket ); |
||||
$connection_id = $this->udp_connection_response( $socket, $transaction_id, $host, $port ); |
||||
|
||||
return array( $socket, $transaction_id, $connection_id ); |
||||
} |
||||
|
||||
/** |
||||
* Creates the UDP socket and establishes the connection |
||||
* |
||||
* @throws \Exception If the socket couldn't be created or connected to. |
||||
* |
||||
* @param string $host Domain or IP address of the tracker. |
||||
* @param int $port Port number of the tracker, Default 80. |
||||
* @return resource $socket Created and connected socket. |
||||
*/ |
||||
private function udp_create_connection( $host, $port ) { |
||||
if ( false === ( $socket = @socket_create( AF_INET, SOCK_DGRAM, SOL_UDP ) ) ) { |
||||
throw new \Exception( "Couldn't create socket." ); |
||||
} |
||||
|
||||
$timeout = $this->timeout; |
||||
socket_set_option( $socket, SOL_SOCKET, SO_RCVTIMEO, array( 'sec' => $timeout, 'usec' => 0 ) ); |
||||
socket_set_option( $socket, SOL_SOCKET, SO_SNDTIMEO, array( 'sec' => $timeout, 'usec' => 0 ) ); |
||||
if ( false === @socket_connect( $socket, $host, $port ) ) { |
||||
throw new \Exception( "Couldn't connect to socket." ); |
||||
} |
||||
|
||||
return $socket; |
||||
} |
||||
|
||||
/** |
||||
* Writes to the connected socket and returns the transaction ID |
||||
* |
||||
* @throws \Exception If the socket couldn't be written to. |
||||
* |
||||
* @param resource $socket The socket resource. |
||||
* @return int The transaction ID. |
||||
*/ |
||||
private function udp_connection_request( $socket ) { |
||||
$connection_id = "\x00\x00\x04\x17\x27\x10\x19\x80"; |
||||
$action = pack( 'N', 0 ); |
||||
$transaction_id = mt_rand( 0, 2147483647 ); |
||||
$buffer = $connection_id . $action . pack( 'N', $transaction_id ); |
||||
if ( false === @socket_write( $socket, $buffer, strlen( $buffer ) ) ) { |
||||
socket_close( $socket ); |
||||
throw new \Exception( "Couldn't write to socket." ); |
||||
} |
||||
|
||||
return $transaction_id; |
||||
} |
||||
|
||||
/** |
||||
* Reads the connection response and returns the connection ID |
||||
* |
||||
* @throws \Exception If anything fails with the scraping. |
||||
* |
||||
* @param resource $socket The socket resource. |
||||
* @param int $transaction_id The transaction ID. |
||||
* @param string $host Domain or IP address of the tracker. |
||||
* @param int $port Port number of the tracker, Default 80. |
||||
* @return string The connection ID. |
||||
*/ |
||||
private function udp_connection_response( $socket, $transaction_id, $host, $port ) { |
||||
if ( false === ( $response = @socket_read( $socket, 16 ) ) ) { |
||||
socket_close( $socket ); |
||||
throw new \Exception( 'Invalid scrape connection! (' . $host . ':' . $port . ').' ); |
||||
} |
||||
|
||||
if ( strlen( $response ) < 16 ) { |
||||
socket_close( $socket ); |
||||
throw new \Exception( 'Invalid scrape response (' . $host . ':' . $port . ').' ); |
||||
} |
||||
|
||||
$result = unpack( 'Naction/Ntransaction_id', $response ); |
||||
if ( 0 !== $result['action'] || $result['transaction_id'] !== $transaction_id ) { |
||||
socket_close( $socket ); |
||||
throw new \Exception( 'Invalid scrape result (' . $host . ':' . $port . ').' ); |
||||
} |
||||
|
||||
$connection_id = substr( $response, 8, 8 ); |
||||
|
||||
return $connection_id; |
||||
} |
||||
|
||||
/** |
||||
* Reads the socket response and returns the torrent data |
||||
* |
||||
* @throws \Exception If anything fails while reading the response. |
||||
* |
||||
* @param resource $socket The socket resource. |
||||
* @param array $hashes List (>1) or string of infohash(es). |
||||
* @param string $connection_id The connection ID. |
||||
* @param int $transaction_id The transaction ID. |
||||
* @param string $host Domain or IP address of the tracker. |
||||
* @param int $port Port number of the tracker, Default 80. |
||||
* @return string Response data. |
||||
*/ |
||||
private function udp_scrape( $socket, $hashes, $connection_id, $transaction_id, $host, $port ) { |
||||
$this->udp_scrape_request( $socket, $hashes, $connection_id, $transaction_id ); |
||||
|
||||
$read_length = 8 + ( 12 * count( $hashes ) ); |
||||
if ( false === ( $response = @socket_read( $socket, $read_length ) ) ) { |
||||
socket_close( $socket ); |
||||
throw new \Exception( 'Invalid scrape connection (' . $host . ':' . $port . ').' ); |
||||
} |
||||
socket_close( $socket ); |
||||
|
||||
if ( strlen( $response ) < $read_length ) { |
||||
throw new \Exception( 'Invalid scrape response (' . $host . ':' . $port . ').' ); |
||||
} |
||||
|
||||
$result = unpack( 'Naction/Ntransaction_id', $response ); |
||||
if ( 2 !== $result['action'] || $result['transaction_id'] !== $transaction_id ) { |
||||
throw new \Exception( 'Invalid scrape result (' . $host . ':' . $port . ').' ); |
||||
} |
||||
|
||||
return $response; |
||||
} |
||||
|
||||
/** |
||||
* Writes to the connected socket |
||||
* |
||||
* @throws \Exception If the socket couldn't be written to. |
||||
* |
||||
* @param resource $socket The socket resource. |
||||
* @param array $hashes List (>1) or string of infohash(es). |
||||
* @param string $connection_id The connection ID. |
||||
* @param int $transaction_id The transaction ID. |
||||
*/ |
||||
private function udp_scrape_request( $socket, $hashes, $connection_id, $transaction_id ) { |
||||
$action = pack( 'N', 2 ); |
||||
|
||||
$infohashes = ''; |
||||
foreach ( $hashes as $infohash ) { |
||||
$infohashes .= pack( 'H*', $infohash ); |
||||
} |
||||
|
||||
$buffer = $connection_id . $action . pack( 'N', $transaction_id ) . $infohashes; |
||||
if ( false === @socket_write( $socket, $buffer, strlen( $buffer ) ) ) { |
||||
socket_close( $socket ); |
||||
throw new \Exception( "Couldn't write to socket." ); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Writes the announce to the connected socket |
||||
* |
||||
* @throws \Exception If the socket couldn't be written to. |
||||
* |
||||
* @param resource $socket The socket resource. |
||||
* @param array $hashes List (>1) or string of infohash(es). |
||||
* @param string $connection_id The connection ID. |
||||
* @return string Torrent(s) data. |
||||
*/ |
||||
private function udp_announce( $socket, $hashes, $connection_id ) { |
||||
$action = pack( 'N', 1 ); |
||||
$downloaded = $left = $uploaded = "\x30\x30\x30\x30\x30\x30\x30\x30"; |
||||
$peer_id = $this->random_peer_id(); |
||||
$event = pack( 'N', 3 ); |
||||
$ip_addr = pack( 'N', 0 ); |
||||
$key = pack( 'N', mt_rand( 0, 2147483647 ) ); |
||||
$num_want = -1; |
||||
$ann_port = pack( 'N', mt_rand( 0, 255 ) ); |
||||
|
||||
$response_data = ''; |
||||
foreach ( $hashes as $infohash ) { |
||||
$transaction_id = mt_rand( 0, 2147483647 ); |
||||
$buffer = $connection_id . $action . pack( 'N', $transaction_id ) . pack( 'H*', $infohash ) . |
||||
$peer_id . $downloaded . $left . $uploaded . $event . $ip_addr . $key . $num_want . $ann_port; |
||||
|
||||
if ( false === @socket_write( $socket, $buffer, strlen( $buffer ) ) ) { |
||||
socket_close( $socket ); |
||||
throw new \Exception( "Couldn't write announce to socket." ); |
||||
} |
||||
|
||||
$response = $this->udp_verify_announce( $socket, $transaction_id ); |
||||
if ( false === $response ) { |
||||
continue; |
||||
} |
||||
|
||||
$response_data .= $response; |
||||
} |
||||
socket_close( $socket ); |
||||
|
||||
return $response_data; |
||||
} |
||||
|
||||
/** |
||||
* Generates a random peer ID |
||||
* |
||||
* @return string Generated peer ID. |
||||
*/ |
||||
private function random_peer_id() { |
||||
$identifier = '-SP0054-'; |
||||
$chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; |
||||
$peer_id = $identifier . substr( str_shuffle( $chars ), 0, 12 ); |
||||
|
||||
return $peer_id; |
||||
} |
||||
|
||||
/** |
||||
* Verifies the correctness of the announce response |
||||
* |
||||
* @param resource $socket The socket resource. |
||||
* @param int $transaction_id The transaction ID. |
||||
* @return string Response data. |
||||
*/ |
||||
private function udp_verify_announce( $socket, $transaction_id ) { |
||||
if ( false === ( $response = @socket_read( $socket, 20 ) ) ) { |
||||
return false; |
||||
} |
||||
|
||||
if ( strlen( $response ) < 20 ) { |
||||
return false; |
||||
} |
||||
|
||||
$result = unpack( 'Naction/Ntransaction_id', $response ); |
||||
if ( 1 !== $result['action'] || $result['transaction_id'] !== $transaction_id ) { |
||||
return false; |
||||
} |
||||
|
||||
return $response; |
||||
} |
||||
|
||||
/** |
||||
* Reads the socket response and returns the torrent data |
||||
* |
||||
* @param string $response Data from the request response. |
||||
* @param array $hashes List (>1) or string of infohash(es). |
||||
* @param string $host Domain or IP address of the tracker. |
||||
* @param string $keys Keys for the unpacked information. |
||||
* @param int $start Start of the content we want to unpack. |
||||
* @param int $end End of the content we want to unpack. |
||||
* @param int $offset Offset to the next content part. |
||||
* @return array Scraped torrent data. |
||||
*/ |
||||
private function udp_scrape_data( $response, $hashes, $host, $keys, $start, $end, $offset ) { |
||||
$torrents_data = array(); |
||||
|
||||
foreach ( $hashes as $infohash ) { |
||||
$byte_string = substr( $response, $start, $end ); |
||||
$data = unpack( 'N', $byte_string ); |
||||
$content = $data[1]; |
||||
if ( ! empty( $content ) ) { |
||||
$results = unpack( $keys, $byte_string ); |
||||
$torrents_data[ $infohash ] = $results; |
||||
} else { |
||||
$this->collect_infohash( $infohash ); |
||||
$this->errors[] = 'Invalid infohash (' . $infohash . ') for tracker: ' . $host . '.'; |
||||
} |
||||
$start += $offset; |
||||
} |
||||
|
||||
return $torrents_data; |
||||
} |
||||
|
||||
/** |
||||
* Collects info-hashes that couldn't be scraped. |
||||
* |
||||
* @param string $infohash Infohash that wasn't scraped. |
||||
*/ |
||||
private function collect_infohash( $infohash ) { |
||||
$this->infohashes[] = $infohash; |
||||
} |
||||
|
||||
/** |
||||
* Checks if there are any errors |
||||
* |
||||
* @return bool True or false, depending if errors are present or not. |
||||
*/ |
||||
public function has_errors() { |
||||
return ! empty( $this->errors ); |
||||
} |
||||
|
||||
/** |
||||
* Returns all the errors that were logged |
||||
* |
||||
* @return array All the logged errors. |
||||
*/ |
||||
public function get_errors() { |
||||
return $this->errors; |
||||
} |
||||
} |
@ -1,45 +0,0 @@
@@ -1,45 +0,0 @@
|
||||
<?php |
||||
|
||||
class Time |
||||
{ |
||||
public static function ago(int $time) |
||||
{ |
||||
$diff = time() - $time; |
||||
|
||||
if ($diff < 1) |
||||
{ |
||||
return _('now'); |
||||
} |
||||
|
||||
$values = |
||||
[ |
||||
365 * 24 * 60 * 60 => _('year'), |
||||
30 * 24 * 60 * 60 => _('month'), |
||||
24 * 60 * 60 => _('day'), |
||||
60 * 60 => _('hour'), |
||||
60 => _('minute'), |
||||
1 => _('second') |
||||
]; |
||||
|
||||
$plural = [ |
||||
_('year') => _('years'), |
||||
_('month') => _('months'), |
||||
_('day') => _('days'), |
||||
_('hour') => _('hours'), |
||||
_('minute') => _('minutes'), |
||||
_('second') => _('seconds') |
||||
]; |
||||
|
||||
foreach ($values as $key => $value) |
||||
{ |
||||
$result = $diff / $key; |
||||
|
||||
if ($result >= 1) |
||||
{ |
||||
$round = round($result); |
||||
|
||||
return sprintf('%s %s ago', $round, $round > 1 ? $plural[$value] : $value); |
||||
} |
||||
} |
||||
} |
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue