mirror of
https://github.com/YGGverse/YGGtracker.git
synced 2026-03-31 17:15:38 +00:00
Compare commits
434 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d72bda1e71 | ||
|
|
085e6174d9 | ||
|
|
7bcc52fcae | ||
|
|
5a1ada42e7 | ||
|
|
ee9b7be6ac | ||
|
|
09811cd801 | ||
|
|
1281acea22 | ||
|
|
95addf0c48 | ||
|
|
e780c5b4b5 | ||
|
|
6c775f822c | ||
|
|
bff1962071 | ||
|
|
10181a04f1 | ||
|
|
7eb02d06cc | ||
|
|
2f4dbff90d | ||
|
|
3c6b1d6ab7 | ||
|
|
9d596de610 | ||
|
|
6603790aba | ||
|
|
fe608cff8f | ||
|
|
9814a56135 | ||
|
|
27e598fded | ||
|
|
f5d4c19eb9 | ||
|
|
9081acebb3 | ||
|
|
3b832c94bc | ||
|
|
110976c619 | ||
|
|
2afaf2f618 | ||
|
|
8aff756e30 | ||
|
|
7177cdb4fe | ||
|
|
3ce3dfe77b | ||
|
|
5523034307 | ||
|
|
e3503bc4bd | ||
|
|
002a41da87 | ||
|
|
deb35d5013 | ||
|
|
1ae5d324c2 | ||
|
|
5a0342a998 | ||
|
|
7a1fa12271 | ||
|
|
77616c3c8a | ||
|
|
b96ed08694 | ||
|
|
ff565ac33b | ||
|
|
d1e9e72401 | ||
|
|
178ab23031 | ||
|
|
e1e3bfb2ce | ||
|
|
31aad63399 | ||
|
|
2e9b119733 | ||
|
|
0247e0e064 | ||
|
|
701b448cd6 | ||
|
|
35babed517 | ||
|
|
f8e0890966 | ||
|
|
261031dc50 | ||
|
|
6dcbd6de40 | ||
|
|
ad5b075878 | ||
|
|
7796aba342 | ||
|
|
2e4129927d | ||
|
|
258d206f2e | ||
|
|
4f2879fdef | ||
|
|
e788744a0f | ||
|
|
986f6678f8 | ||
|
|
514b1ebc5d | ||
|
|
3589d2eef4 | ||
|
|
69309b9a98 | ||
|
|
5c76a17df5 | ||
|
|
cfeeabee72 | ||
|
|
cef76daa49 | ||
|
|
446da9eb0b | ||
|
|
99ea797699 | ||
|
|
8742c91f9f | ||
|
|
ed3803df95 | ||
|
|
1b8deb439b | ||
|
|
706ea40eec | ||
|
|
bd5191e894 | ||
|
|
8ae1b3f0b7 | ||
|
|
997666ab8e | ||
|
|
c7c5d7340c | ||
|
|
afcacebe26 | ||
|
|
989f2f3311 | ||
|
|
3cbc6ea90f | ||
|
|
dbc37da63d | ||
|
|
be248963e9 | ||
|
|
246fdda4fb | ||
|
|
5c4e949269 | ||
|
|
0b77a5d171 | ||
|
|
79886c0d77 | ||
|
|
a47893ff9b | ||
|
|
83ea09adad | ||
|
|
82f69c64a6 | ||
|
|
4969d33f57 | ||
|
|
1d6a7b03b2 | ||
|
|
1c9de9b275 | ||
|
|
6bc2c360a3 | ||
|
|
341c3a70a5 | ||
|
|
fa1f1f18c6 | ||
|
|
b4c6a98d49 | ||
|
|
0d96cd3e3a | ||
|
|
5ecf8461b4 | ||
|
|
a72413706d | ||
|
|
dd04922cc0 | ||
|
|
24c58d4301 | ||
|
|
5f4a14ebe2 | ||
|
|
78a7134ced | ||
|
|
09bd7ecf34 | ||
|
|
7373f622e4 | ||
|
|
35bf1a8814 | ||
|
|
d40436db00 | ||
|
|
6bfd230915 | ||
|
|
8190fc1914 | ||
|
|
c70205e204 | ||
|
|
a128cb7cb3 | ||
|
|
2d2c6be016 | ||
|
|
8affbe8644 | ||
|
|
e6fac3d298 | ||
|
|
371dda9d31 | ||
|
|
5d8988719a | ||
|
|
0b316734b8 | ||
|
|
314320c554 | ||
|
|
51ee02201a | ||
|
|
31bed20b4b | ||
|
|
4c519a56ba | ||
|
|
d794e48a54 | ||
|
|
3a14f29b38 | ||
|
|
b9111213b2 | ||
|
|
891868eccd | ||
|
|
d3cdbc831c | ||
|
|
33e950bb42 | ||
|
|
4a801fa809 | ||
|
|
2524a30476 | ||
|
|
306ccb6078 | ||
|
|
1c869fd78e | ||
|
|
29553c75c8 | ||
|
|
70ce180765 | ||
|
|
2acfc06ca6 | ||
|
|
4cafc51b67 | ||
|
|
99eb0ddcb2 | ||
|
|
8e069c7997 | ||
|
|
8c48e3b40e | ||
|
|
fd17185a9e | ||
|
|
5161becd60 | ||
|
|
9c0b10b283 | ||
|
|
b7238eaf9f | ||
|
|
453d70b7cb | ||
|
|
3005b16c94 | ||
|
|
1f51c56c0d | ||
|
|
8e204312e0 | ||
|
|
1cd252b610 | ||
|
|
bc67ae2198 | ||
|
|
7dfb133328 | ||
|
|
111fe6a7b5 | ||
|
|
7587b38831 | ||
|
|
69a463f0b0 | ||
|
|
dfcd5c438e | ||
|
|
9925d4f57c | ||
|
|
0923eb2234 | ||
|
|
6bda9c733c | ||
|
|
b0f7202c82 | ||
|
|
4c521aef99 | ||
|
|
8cf9905846 | ||
|
|
6ab4dc87c2 | ||
|
|
8308ab28f9 | ||
|
|
2682666b94 | ||
|
|
bd7fe4cf04 | ||
|
|
07262f4486 | ||
|
|
f5d8759dfe | ||
|
|
5168280519 | ||
|
|
d0096cbfe2 | ||
|
|
a285aa3158 | ||
|
|
06dd75eb1e | ||
|
|
e5855d06b5 | ||
|
|
6a3c810da7 | ||
|
|
be34befe96 | ||
|
|
20dc122ab2 | ||
|
|
68fbafaefa | ||
|
|
e97c4ec27f | ||
|
|
8b42df4041 | ||
|
|
60503becdf | ||
|
|
f45bda4e58 | ||
|
|
ef25487854 | ||
|
|
d9ecec44c0 | ||
|
|
851efe0392 | ||
|
|
924137f09a | ||
|
|
60e8a57dce | ||
|
|
e0410390aa | ||
|
|
f8de41ffab | ||
|
|
e21561c1d0 | ||
|
|
5fde6c2d1c | ||
|
|
a930ca4df7 | ||
|
|
4799b0dcd4 | ||
|
|
616ee54ad7 | ||
|
|
401c38ff6a | ||
|
|
94a86e5acc | ||
|
|
91c9415976 | ||
|
|
92d715d303 | ||
|
|
643d643f46 | ||
|
|
7c28eaadd2 | ||
|
|
3ef090257a | ||
|
|
35b84546ff | ||
|
|
60a5593446 | ||
|
|
130add0904 | ||
|
|
b1db78554c | ||
|
|
daf8c63c51 | ||
|
|
f8e7bd8c44 | ||
|
|
df6896f3e5 | ||
|
|
ed6c4ea415 | ||
|
|
92c2a56bbf | ||
|
|
ffaae984d6 | ||
|
|
4d231a799f | ||
|
|
ea9f7f1589 | ||
|
|
da1e869be5 | ||
|
|
e713c17333 | ||
|
|
d1f8c126b0 | ||
|
|
5e25b15de3 | ||
|
|
038abfbb52 | ||
|
|
bdb563dace | ||
|
|
c3414e132b | ||
|
|
63dfeb9d4c | ||
|
|
311ecd0481 | ||
|
|
ab217b240a | ||
|
|
3aa7e5ff3c | ||
|
|
d2cb66f51d | ||
|
|
cf9b8de29f | ||
|
|
f56f07ac29 | ||
|
|
995d4bde54 | ||
|
|
cc6c68957c | ||
|
|
9c9dc5b5a4 | ||
|
|
7aa2c03abc | ||
|
|
d4fbb4b592 | ||
|
|
c1c5c7fa59 | ||
|
|
28f21d09c6 | ||
|
|
0339ee9f23 | ||
|
|
6ea0024b94 | ||
|
|
855929f592 | ||
|
|
964dae97aa | ||
|
|
46d5e25a5f | ||
|
|
b1445ce541 | ||
|
|
a3dd5a81a9 | ||
|
|
ef84fefca3 | ||
|
|
c47c8ad83b | ||
|
|
cf204d09d0 | ||
|
|
87f6661423 | ||
|
|
ac49397a1a | ||
|
|
1f7d8d0810 | ||
|
|
bd772f87d1 | ||
|
|
c747166a30 | ||
|
|
649838d4ee | ||
|
|
4dcdd177ee | ||
|
|
247003a366 | ||
|
|
22df1dbbd0 | ||
|
|
a5d5f95dce | ||
|
|
f8969ba66e | ||
|
|
8ab4c0b9cf | ||
|
|
e9375f9127 | ||
|
|
1495378a4b | ||
|
|
f88f1c63f2 | ||
|
|
4b2b239b76 | ||
|
|
c0f593ebec | ||
|
|
b25cc173c5 | ||
|
|
e591fe36a1 | ||
|
|
44927e2d07 | ||
|
|
4f24ec22d2 | ||
|
|
285a5104e2 | ||
|
|
8d258c677b | ||
|
|
42cef39589 | ||
|
|
c3ba8930f4 | ||
|
|
b1679f3f65 | ||
|
|
6effb4cad2 | ||
|
|
5a940541ee | ||
|
|
0ab282e1c4 | ||
|
|
7b3ab7de7b | ||
|
|
0a218cfd3a | ||
|
|
d97a678952 | ||
|
|
f260bda7ee | ||
|
|
997d9db562 | ||
|
|
3a7a87a89e | ||
|
|
68fdc3c28c | ||
|
|
1be80ca34e | ||
|
|
1b722812e8 | ||
|
|
d2f7fff24e | ||
|
|
2f06c2a7e5 | ||
|
|
6752b7b93e | ||
|
|
ffa59d6f82 | ||
|
|
4a6dd85c60 | ||
|
|
dec2383a04 | ||
|
|
69214e8058 | ||
|
|
0609d5775f | ||
|
|
62ad522286 | ||
|
|
5b0a7bcb69 | ||
|
|
3d74818303 | ||
|
|
a8b08bed06 | ||
|
|
41b46b6604 | ||
|
|
b81d973331 | ||
|
|
cbda078c38 | ||
|
|
d34ff8ecc6 | ||
|
|
fafca13b32 | ||
|
|
8bdb115a26 | ||
|
|
9335b01ad6 | ||
|
|
f919a7ed85 | ||
|
|
5aa20e6a22 | ||
|
|
6fbce1678d | ||
|
|
9af7117206 | ||
|
|
57085f8167 | ||
|
|
23b74a800a | ||
|
|
f0a38e1bf5 | ||
|
|
16696bc1d2 | ||
|
|
bf08fa6191 | ||
|
|
6031ce59a2 | ||
|
|
a0b59500fc | ||
|
|
df253ba1b0 | ||
|
|
e772955eb2 | ||
|
|
86e1455c6b | ||
|
|
387acb59b6 | ||
|
|
21ffd8aa01 | ||
|
|
ab2c310ec5 | ||
|
|
1c1e5b02c1 | ||
|
|
6655fd907f | ||
|
|
3f083cb1b7 | ||
|
|
36b1bf25f4 | ||
|
|
c0cc029350 | ||
|
|
8df29ef605 | ||
|
|
b1fe274ae8 | ||
|
|
edc78531d3 | ||
|
|
eade8260d7 | ||
|
|
12e56bf6d1 | ||
|
|
f6e6a0f114 | ||
|
|
fe7e33f7ef | ||
|
|
731624d886 | ||
|
|
ffa568275f | ||
|
|
82a4860e0c | ||
|
|
1fffab732c | ||
|
|
2d6fa05081 | ||
|
|
5f5da0ded4 | ||
|
|
b9a2804132 | ||
|
|
d73086231f | ||
|
|
8ee25d3a30 | ||
|
|
02e56e4d08 | ||
|
|
8680718714 | ||
|
|
fbeb793f6d | ||
|
|
36329b0b81 | ||
|
|
8eb5a05f29 | ||
|
|
0aa38c16dd | ||
|
|
af72fa14c4 | ||
|
|
4b1b80b875 | ||
|
|
90f28cfd8b | ||
|
|
4852882243 | ||
|
|
b1e5d524ac | ||
|
|
0224fbf23a | ||
|
|
7750a214e0 | ||
|
|
b863d145a0 | ||
|
|
9fcc5a451b | ||
|
|
f85414fd2b | ||
|
|
6b2e67f04b | ||
|
|
ae8ec4823a | ||
|
|
737b79b608 | ||
|
|
8d1c35360b | ||
|
|
4720b34e9c | ||
|
|
89ac72b77d | ||
|
|
06e0694739 | ||
|
|
380377b27c | ||
|
|
3c233fcfad | ||
|
|
7d7629488a | ||
|
|
ca50f85626 | ||
|
|
cfc9c721ff | ||
|
|
7f892b0772 | ||
|
|
3a4e498c34 | ||
|
|
1f6c439c92 | ||
|
|
e6293f90b2 | ||
|
|
2eb0a30ed2 | ||
|
|
f51ad8d5de | ||
|
|
a4dae88575 | ||
|
|
1dba3a4126 | ||
|
|
1c79b7c0b6 | ||
|
|
aa4d2821b1 | ||
|
|
d3e978dbde | ||
|
|
d25634299e | ||
|
|
44e37cfbe6 | ||
|
|
00c39f49c1 | ||
|
|
451df36fc2 | ||
|
|
f39d3d8ea4 | ||
|
|
0227f0c9ec | ||
|
|
2279e0d44e | ||
|
|
4a1f06fd82 | ||
|
|
f1086bfbc9 | ||
|
|
c05abad02f | ||
|
|
86587db9e9 | ||
|
|
623375484e | ||
|
|
246944e74e | ||
|
|
35ced66093 | ||
|
|
e5a0ac92cc | ||
|
|
e560c38c1f | ||
|
|
3dd619baa7 | ||
|
|
fe48055f82 | ||
|
|
d955446b99 | ||
|
|
0672f0349e | ||
|
|
0065ef476c | ||
|
|
5e31e0e972 | ||
|
|
b269e24561 | ||
|
|
f1fedfbbcb | ||
|
|
982f532841 | ||
|
|
aa0be166d1 | ||
|
|
da89328ce5 | ||
|
|
1233527e49 | ||
|
|
c05eae8c9c | ||
|
|
bbd54b3a14 | ||
|
|
0c26c0ac9b | ||
|
|
f9eb917149 | ||
|
|
8b8eb74835 | ||
|
|
0d840a5ab5 | ||
|
|
d77ad74d32 | ||
|
|
5a3ac70fd2 | ||
|
|
d949474737 | ||
|
|
9aaa5d5989 | ||
|
|
3a858648de | ||
|
|
556a135b59 | ||
|
|
e4d1215a53 | ||
|
|
32c1bbe4a2 | ||
|
|
e1054cd69e | ||
|
|
6b112d441c | ||
|
|
947e359976 | ||
|
|
eebfefbb3d | ||
|
|
1512bfe3c8 | ||
|
|
f3b32a713f | ||
|
|
d8f6b6d27e | ||
|
|
762d72e913 | ||
|
|
31b8a0a5fb | ||
|
|
4697519663 | ||
|
|
9b0bfcb76c | ||
|
|
919bc0a66c | ||
|
|
8740e7a63f | ||
|
|
a600a08a28 | ||
|
|
c4f5409ffa | ||
|
|
cbf0902677 | ||
|
|
41a557cbb6 | ||
|
|
a9f67243ba | ||
|
|
67921a0d3e | ||
|
|
fe2a7a575f | ||
|
|
3507abc35f | ||
|
|
34ae82532f | ||
|
|
2add05557d |
216 changed files with 42977 additions and 13170 deletions
126
.env
Normal file
126
.env
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
# 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
|
||||
APP_KEY=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
|
||||
|
||||
# Application version, used for API and media cache
|
||||
APP_VERSION=2.6.2
|
||||
|
||||
# Application name
|
||||
APP_NAME=YGGtracker
|
||||
|
||||
# Default locale
|
||||
APP_LOCALE=en
|
||||
|
||||
# Supported locales for interface and content filters
|
||||
APP_LOCALES=en|cs|nl|eo|fr|ja|ka|de|he|it|lv|pl|pt|ru|es|uk
|
||||
|
||||
# Content categories, lowercase, enabled by default for new users
|
||||
# src/Twig/AppExtension.php:transCategory
|
||||
APP_CATEGORIES=movie|series|tv|animation|music|game|audiobook|podcast|book|archive|picture|software|other
|
||||
|
||||
# Items per page on pagination
|
||||
APP_PAGINATION=10
|
||||
|
||||
# Default application theme
|
||||
APP_THEME=default
|
||||
|
||||
# Additional themes, stored in /src/templates, /public/asset
|
||||
APP_THEMES=default
|
||||
|
||||
# Default sensitive status for new users
|
||||
APP_SENSITIVE=1
|
||||
|
||||
# Default approved status for new users
|
||||
APP_APPROVED=1
|
||||
|
||||
# Default Yggdrasil filters status for new users
|
||||
APP_YGGDRASIL=1
|
||||
|
||||
# Default posters status for new users
|
||||
APP_POSTERS=1
|
||||
|
||||
# Build-in trackers append to downloads
|
||||
APP_TRACKERS=http://[201:23b4:991a:634d:8359:4521:5576:15b7]:2023/announce|http://[200:1e2f:e608:eb3a:2bf:1e62:87ba:e2f7]/announce|http://[316:c51a:62a3:8b9::5]/announce
|
||||
|
||||
# List of crawlers where ignored in actions and activity features
|
||||
APP_CRAWLERS=201:23b4:991a:634d:8359:4521:5576:15b7|30a:5fad::e|202:f2bc:f800:7cc4:c109:7857:5cae:6630|200:1554:e730:4030:605b:47be:6fb6:7b11
|
||||
|
||||
# Max torrent filesize for uploads (check upload_max_filesize in the php.ini)
|
||||
APP_TORRENT_FILE_SIZE_MAX=1024000
|
||||
|
||||
# Max torrent poster filesize for uploads (check upload_max_filesize in the php.ini)
|
||||
APP_TORRENT_POSTER_FILE_SIZE_MAX=10240000
|
||||
|
||||
# Store wanted torrent files in /app/var/ftp by /app/crontab/torrent/scrape/{key}
|
||||
APP_TORRENT_WANTED_FTP_ENABLED=1
|
||||
APP_TORRENT_WANTED_FTP_FOLDER=/yggtracker
|
||||
APP_TORRENT_WANTED_FTP_APPROVED_ONLY=1
|
||||
|
||||
# Enable search index for torrent name
|
||||
APP_INDEX_TORRENT_NAME_ENABLED=1
|
||||
|
||||
# Enable search index for torrent info hash v1
|
||||
APP_INDEX_TORRENT_HASH_V1_ENABLED=1
|
||||
|
||||
# Enable search index for torrent info hash v2
|
||||
APP_INDEX_TORRENT_HASH_V2_ENABLED=1
|
||||
|
||||
# Enable search index for torrent filenames
|
||||
APP_INDEX_TORRENT_FILENAMES_ENABLED=1
|
||||
|
||||
# Enable search index for torrent source
|
||||
APP_INDEX_TORRENT_SOURCE_ENABLED=1
|
||||
|
||||
# Enable search index for torrent comment
|
||||
APP_INDEX_TORRENT_COMMENT_ENABLED=1
|
||||
|
||||
# Enable search index for words length greater than N chars
|
||||
APP_INDEX_WORD_LENGTH_MIN=3
|
||||
|
||||
# Enable search index for words length not greater than N chars
|
||||
APP_INDEX_WORD_LENGTH_MAX=255
|
||||
6
.env.test
Normal file
6
.env.test
Normal file
|
|
@ -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
|
||||
33
.gitignore
vendored
33
.gitignore
vendored
|
|
@ -1,21 +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/yggtracker.mwb.bak
|
||||
###> 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.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
|
||||
220
README.md
220
README.md
|
|
@ -1,55 +1,76 @@
|
|||
# YGGtracker
|
||||
|
||||
Distributed BitTorrent Registry for Yggdrasil
|
||||
> [!NOTE]
|
||||
> Take a look at [βtracker](https://github.com/yggverse/btracker) - the modern aggregation alternative written in Rust!
|
||||
|
||||
YGGtracker uses [Yggdrasil](https://github.com/yggdrasil-network/yggdrasil-go) IPv6 addresses to identify users without registration.
|
||||
A social-oriented BitTorrent catalog for the [Yggdrasil](https://github.com/yggdrasil-network) network, written in the Symfony framework.
|
||||
|
||||
#### Nodes online
|
||||
YGGtracker is a manually operated catalog and social network that allows users to share their torrents in the local network. Engine uses IPv6 `0200::/7` addresses to identify users without registration.
|
||||
|
||||
YGGtracker is distributed index engine, default nodes list defined in [nodes.json](https://github.com/YGGverse/YGGtracker/blob/main/src/config/nodes.json)
|
||||
#### [Showcase](https://github.com/YGGverse/YGGtracker/wiki/Showcase)
|
||||
|
||||
If you have launched new one, feel free to participate by PR.
|
||||

|
||||
|
||||
#### Trackers
|
||||
|
||||
Open trackers defined in [trackers.json](https://github.com/YGGverse/YGGtracker/blob/main/src/config/trackers.json)
|
||||
|
||||
* Application appends initial trackers to all download links and magnet forms
|
||||
* Trackers not in list will be cropped by the application filter
|
||||
* Feel free to PR new yggdrasil tracker!
|
||||
|
||||
#### Requirements
|
||||
|
||||
```
|
||||
php8^
|
||||
php-pdo
|
||||
php-mysql
|
||||
php-curl
|
||||
php-memcached
|
||||
sphinxsearch
|
||||
memcached
|
||||
```
|
||||
#### Installation
|
||||
|
||||
```
|
||||
symfony check:requirements
|
||||
```
|
||||
|
||||
##### Production
|
||||
|
||||
* `composer create-project yggverse/yggtracker`
|
||||
Install stable release
|
||||
|
||||
```
|
||||
composer create-project yggverse/yggtracker
|
||||
```
|
||||
|
||||
##### Development
|
||||
|
||||
* `git clone https://github.com/YGGverse/YGGtracker.git`
|
||||
* `cd YGGtracker`
|
||||
* `composer update`
|
||||
Latest codebase available in repository
|
||||
|
||||
#### Setup
|
||||
* Server configuration `/example/environment`
|
||||
* The web root dir is `/src/public`
|
||||
* Deploy the database using [MySQL Workbench](https://www.mysql.com/products/workbench) project presented in the `/database` folder
|
||||
* Install [Sphinx Search Server](https://sphinxsearch.com)
|
||||
* Configuration examples presented at `/example/environment` folder. On first app launch, configuration file will be auto-generated in `/src/config`
|
||||
* Make sure `/src/api` folder writable
|
||||
```
|
||||
git clone https://github.com/YGGverse/YGGtracker.git
|
||||
cd YGGtracker
|
||||
composer update
|
||||
symfony server:start
|
||||
```
|
||||
|
||||
#### Contribute
|
||||
##### Database
|
||||
|
||||
New installation
|
||||
|
||||
```
|
||||
php bin/console doctrine:schema:update --force
|
||||
```
|
||||
|
||||
Existing DB upgrade
|
||||
|
||||
```
|
||||
php bin/console doctrine:migrations:migrate
|
||||
```
|
||||
|
||||
##### Crontab
|
||||
|
||||
* `* * * * * /crontab/torrent/scrape/{%app.key%}` - update seeding stats
|
||||
|
||||
##### FTP
|
||||
|
||||
Setup anonymous read-only access to `/var/ftp` catalog ([read more](https://github.com/YGGverse/YGGtracker/wiki/Features#the-wanted))
|
||||
|
||||
##### App settings
|
||||
|
||||
Custom settings could be provided in the `/.env.local` file by overwriting default `/.env` values
|
||||
|
||||
#### Localization
|
||||
|
||||
[](https://crowdin.com/project/yggtracker)
|
||||
|
||||
#### API
|
||||
|
||||
[Wiki reference](https://github.com/YGGverse/YGGtracker/wiki/API)
|
||||
|
||||
#### Contribution
|
||||
|
||||
Please make new branch for each PR
|
||||
|
||||
|
|
@ -58,129 +79,40 @@ git checkout main
|
|||
git checkout -b my-pr-branch-name
|
||||
```
|
||||
|
||||
#### Roadmap
|
||||
|
||||
* [ ] BitTorrent protocol
|
||||
+ [ ] Protocol
|
||||
+ [ ] announce
|
||||
+ [ ] announce-list
|
||||
+ [ ] comment
|
||||
+ [ ] created by
|
||||
+ [ ] creation date
|
||||
+ [ ] info
|
||||
+ [ ] file-duration
|
||||
+ [ ] file-media
|
||||
+ [ ] files
|
||||
+ [ ] name
|
||||
+ [ ] piece length
|
||||
+ [ ] pieces
|
||||
+ [ ] private
|
||||
+ [ ] profiles
|
||||
|
||||
* [ ] Magnet protocol
|
||||
+ [x] Exact Topic / xt
|
||||
+ [x] Display Name / dn
|
||||
+ [x] eXact Length / xl
|
||||
+ [x] Address Tracker / rt
|
||||
+ [x] Web Seed / ws
|
||||
+ [x] Acceptable Source / as
|
||||
+ [x] eXact Source / xs
|
||||
+ [x] Keyword Topic / kt
|
||||
+ [ ] Manifest Topic / mt
|
||||
+ [ ] Select Only / so
|
||||
+ [ ] PEer / x.pe
|
||||
|
||||
* [ ] Catalog
|
||||
+ [x] Public levels
|
||||
+ [x] Sensitive filter
|
||||
+ [x] Comments
|
||||
+ [x] Scrape trackers
|
||||
+ [x] Peers
|
||||
+ [x] Completed
|
||||
+ [x] Leechers
|
||||
+ [x] Stars
|
||||
+ [x] Views
|
||||
+ [x] Downloads
|
||||
+ [x] Wanted
|
||||
+ [x] Threading comments
|
||||
+ [ ] Forks
|
||||
|
||||
* [ ] Profile
|
||||
+ [ ] Listing
|
||||
+ [ ] Uploads
|
||||
+ [ ] Downloads
|
||||
+ [ ] Stars
|
||||
+ [ ] Following
|
||||
+ [ ] Followers
|
||||
+ [ ] Comments
|
||||
+ [ ] Settings
|
||||
+ [ ] Public name
|
||||
+ [ ] Downloads customization
|
||||
+ [ ] Address Tracker
|
||||
+ [ ] Web Seed
|
||||
+ [ ] Acceptable Source
|
||||
+ [ ] eXact Source
|
||||
+ [ ] Content filters
|
||||
|
||||
* [x] API
|
||||
+ [x] Active (push)
|
||||
+ [x] Magnet
|
||||
+ [x] Edit
|
||||
+ [x] Download
|
||||
+ [x] Comment
|
||||
+ [x] Star
|
||||
+ [x] View
|
||||
+ [x] Passive (feed)
|
||||
+ [x] Manifest
|
||||
+ [x] Users
|
||||
+ [x] Magnets
|
||||
+ [x] Downloads
|
||||
+ [x] Comments
|
||||
+ [x] Stars
|
||||
+ [x] Views
|
||||
|
||||
* [x] Export
|
||||
+ [x] Sitemap
|
||||
+ [x] RSS
|
||||
+ [x] Magnets
|
||||
+ [x] Comments
|
||||
|
||||
* [x] Other
|
||||
+ [x] Moderation
|
||||
+ [x] UI
|
||||
+ [ ] CLI
|
||||
+ [ ] Installation tools
|
||||
|
||||
|
||||
#### Donate to contributors
|
||||
|
||||
* @d47081:
|
||||
|
||||
+ 
|
||||
+ [BTC](https://www.blockchain.com/explorer/addresses/btc/bc1qngdf2kwty6djjqpk0ynkpq9wmlrmtm7e0c534y) | [LTC](https://live.blockcypher.com/ltc/address/LUSiqzKsfB1vBLvpu515DZktG9ioKqLyj7) | [XMR](835gSR1Uvka19gnWPkU2pyRozZugRZSPHDuFL6YajaAqjEtMwSPr4jafM8idRuBWo7AWD3pwFQSYRMRW9XezqrK4BEXBgXE) | [ZEPH](ZEPHsADHXqnhfWhXrRcXnyBQMucE3NM7Ng5ZVB99XwA38PTnbjLKpCwcQVgoie8EJuWozKgBiTmDFW4iY7fNEgSEWyAy4dotqtX)
|
||||
+ Support our server by order [Linux VPS](https://www.yourserver.se/portal/aff.php?aff=610)
|
||||
+ Inspiration by [SomaFM Deep Space One](https://somafm.com/deepspaceone/)
|
||||
|
||||
#### License
|
||||
|
||||
* Engine sources [MIT License](https://github.com/YGGverse/YGGtracker/blob/main/LICENSE)
|
||||
|
||||
#### Versioning
|
||||
|
||||
[Semantic Versioning 2.0.0](https://semver.org/#semantic-versioning-200)
|
||||
|
||||
#### Components
|
||||
|
||||
* [Symfony Framework](https://symfony.com)
|
||||
* [SVG icons](https://icons.getbootstrap.com)
|
||||
* [PHP Scrapper](https://github.com/medariox/scrapeer)
|
||||
* [Scrapper](https://github.com/medariox/scrapeer) / [Composer Edition](https://github.com/YGGverse/scrapeer)
|
||||
* [Bencode](https://github.com/Rhilip/Bencode)
|
||||
* [Transliteration](https://github.com/ashtokalo/php-translit)
|
||||
* [Identicons](https://github.com/dmester/jdenticon-php)
|
||||
|
||||
#### Feedback
|
||||
#### Support
|
||||
|
||||
[https://github.com/YGGverse/YGGtracker/issues](https://github.com/YGGverse/YGGtracker/issues)
|
||||
* [Issues](https://github.com/YGGverse/YGGtracker/issues)
|
||||
* [Documentation](https://github.com/YGGverse/YGGtracker/wiki)
|
||||
* [HowTo Yggdrasil](https://ygg.work.gd/yggdrasil:bittorrent:yggtracker)
|
||||
|
||||
#### Community
|
||||
#### Blog
|
||||
|
||||
* [Mastodon](https://mastodon.social/@YGGverse)
|
||||
|
||||
#### Integrations
|
||||
|
||||
* [YGGtracker Search Plugin for qBittorrent](https://github.com/YGGverse/qbittorrent-yggtracker-search-plugin)
|
||||
* [Crontab script that allows to receive wanted torrents from multiple YGGtracker nodes](https://github.com/YGGverse/yggtracker-wanted-torrents-receiver)
|
||||
|
||||
#### See also
|
||||
|
||||
* [YGGo - YGGo! Distributed Web Search Engine ](https://github.com/YGGverse/YGGo)
|
||||
* [YGGwave ~ The Radio Catalog](https://github.com/YGGverse/YGGwave)
|
||||
* [YGGstate - Yggdrasil Network Explorer](https://github.com/YGGverse/YGGstate)
|
||||
* [YGGstate - Yggdrasil Network Explorer](https://github.com/YGGverse/YGGstate)
|
||||
|
|
|
|||
17
bin/console
Executable file
17
bin/console
Executable file
|
|
@ -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);
|
||||
};
|
||||
19
bin/phpunit
Executable file
19
bin/phpunit
Executable file
|
|
@ -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';
|
||||
}
|
||||
118
composer.json
118
composer.json
|
|
@ -1,24 +1,114 @@
|
|||
{
|
||||
"name": "yggverse/yggtracker",
|
||||
"description": "Public BitTorrent tracker for Yggdrasil network",
|
||||
"description": "BitTorrent tracker for Yggdrasil network",
|
||||
"type": "project",
|
||||
"require": {
|
||||
"php": "^8.1",
|
||||
"yggverse/cache": ">=0.3.0",
|
||||
"yggverse/parser": ">=0.4.0",
|
||||
"jdenticon/jdenticon": "^1.0",
|
||||
"christeredvartsen/php-bittorrent": "^2.0"
|
||||
},
|
||||
"license": "MIT",
|
||||
"minimum-stability": "stable",
|
||||
"prefer-stable": true,
|
||||
"require": {
|
||||
"php": ">=8.1",
|
||||
"ext-ctype": "*",
|
||||
"ext-iconv": "*",
|
||||
"ashtokalo/php-translit": "^0.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",
|
||||
"league/commonmark": "^2.4",
|
||||
"phpdocumentor/reflection-docblock": "^5.3",
|
||||
"phpstan/phpdoc-parser": "^1.24",
|
||||
"rhilip/bencode": "^2.3",
|
||||
"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": "^3.7",
|
||||
"twig/intl-extra": "^3.7",
|
||||
"twig/markdown-extra": "^3.7",
|
||||
"twig/string-extra": "^3.7",
|
||||
"twig/twig": "^2.12|^3.0",
|
||||
"yggverse/scrapeer": "^0.5.4"
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"php-http/discovery": true,
|
||||
"symfony/flex": true,
|
||||
"symfony/runtime": true
|
||||
},
|
||||
"sort-packages": true
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Yggverse\\Yggtracker\\": "src/"
|
||||
"App\\": "src/"
|
||||
}
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "YGGverse"
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"App\\Tests\\": "tests/"
|
||||
}
|
||||
],
|
||||
"minimum-stability": "alpha"
|
||||
},
|
||||
"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"
|
||||
],
|
||||
"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.*"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10456
composer.lock
generated
Normal file
10456
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
14
config/bundles.php
Normal file
14
config/bundles.php
Normal file
|
|
@ -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],
|
||||
];
|
||||
19
config/packages/cache.yaml
Normal file
19
config/packages/cache.yaml
Normal file
|
|
@ -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
|
||||
5
config/packages/debug.yaml
Normal file
5
config/packages/debug.yaml
Normal file
|
|
@ -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)%"
|
||||
48
config/packages/doctrine.yaml
Normal file
48
config/packages/doctrine.yaml
Normal file
|
|
@ -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
|
||||
6
config/packages/doctrine_migrations.yaml
Normal file
6
config/packages/doctrine_migrations.yaml
Normal file
|
|
@ -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
|
||||
25
config/packages/framework.yaml
Normal file
25
config/packages/framework.yaml
Normal file
|
|
@ -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
|
||||
3
config/packages/mailer.yaml
Normal file
3
config/packages/mailer.yaml
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
framework:
|
||||
mailer:
|
||||
dsn: '%env(MAILER_DSN)%'
|
||||
24
config/packages/messenger.yaml
Normal file
24
config/packages/messenger.yaml
Normal file
|
|
@ -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
|
||||
62
config/packages/monolog.yaml
Normal file
62
config/packages/monolog.yaml
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
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
|
||||
path: "%kernel.logs_dir%/%kernel.environment%.log"
|
||||
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
|
||||
13
config/packages/notifier.yaml
Normal file
13
config/packages/notifier.yaml
Normal file
|
|
@ -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 }
|
||||
12
config/packages/routing.yaml
Normal file
12
config/packages/routing.yaml
Normal file
|
|
@ -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
|
||||
39
config/packages/security.yaml
Normal file
39
config/packages/security.yaml
Normal file
|
|
@ -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
|
||||
17
config/packages/translation.yaml
Normal file
17
config/packages/translation.yaml
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
framework:
|
||||
default_locale: en
|
||||
translator:
|
||||
default_path: '%kernel.project_dir%/translations'
|
||||
fallbacks:
|
||||
- en
|
||||
providers:
|
||||
crowdin:
|
||||
dsn: '%env(CROWDIN_DSN)%'
|
||||
domains: ['messages']
|
||||
locales: ['en','cs','nl','eo','fr', 'ja', 'ka','de','he','it','lv','pl','pt','ru','es','uk']
|
||||
# loco:
|
||||
# dsn: '%env(LOCO_DSN)%'
|
||||
# lokalise:
|
||||
# dsn: '%env(LOKALISE_DSN)%'
|
||||
# phrase:
|
||||
# dsn: '%env(PHRASE_DSN)%'
|
||||
10
config/packages/twig.yaml
Normal file
10
config/packages/twig.yaml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
twig:
|
||||
default_path: '%kernel.project_dir%/templates'
|
||||
globals:
|
||||
version: '%app.version%'
|
||||
name: '%app.name%'
|
||||
theme: '%app.theme%'
|
||||
|
||||
when@test:
|
||||
twig:
|
||||
strict_variables: true
|
||||
13
config/packages/validator.yaml
Normal file
13
config/packages/validator.yaml
Normal file
|
|
@ -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
|
||||
17
config/packages/web_profiler.yaml
Normal file
17
config/packages/web_profiler.yaml
Normal file
|
|
@ -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 }
|
||||
5
config/preload.php
Normal file
5
config/preload.php
Normal file
|
|
@ -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';
|
||||
}
|
||||
5
config/routes.yaml
Normal file
5
config/routes.yaml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
controllers:
|
||||
resource:
|
||||
path: ../src/Controller/
|
||||
namespace: App\Controller
|
||||
type: attribute
|
||||
4
config/routes/framework.yaml
Normal file
4
config/routes/framework.yaml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
when@dev:
|
||||
_errors:
|
||||
resource: '@FrameworkBundle/Resources/config/routing/errors.xml'
|
||||
prefix: /_error
|
||||
8
config/routes/web_profiler.yaml
Normal file
8
config/routes/web_profiler.yaml
Normal file
|
|
@ -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
|
||||
58
config/services.yaml
Normal file
58
config/services.yaml
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
# 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)%'
|
||||
app.key: '%env(APP_KEY)%'
|
||||
app.pagination: '%env(APP_PAGINATION)%'
|
||||
app.trackers: '%env(APP_TRACKERS)%'
|
||||
app.crawlers: '%env(APP_CRAWLERS)%'
|
||||
app.locales: '%env(APP_LOCALES)%'
|
||||
app.categories: '%env(APP_CATEGORIES)%'
|
||||
app.themes: '%env(APP_THEMES)%'
|
||||
app.locale: '%env(APP_LOCALE)%'
|
||||
app.theme: '%env(APP_THEME)%'
|
||||
app.sensitive: '%env(APP_SENSITIVE)%'
|
||||
app.approved: '%env(APP_APPROVED)%'
|
||||
app.yggdrasil: '%env(APP_YGGDRASIL)%'
|
||||
app.posters: '%env(APP_POSTERS)%'
|
||||
app.torrent.size.max: '%env(APP_TORRENT_FILE_SIZE_MAX)%'
|
||||
app.torrent.poster.size.max: '%env(APP_TORRENT_POSTER_FILE_SIZE_MAX)%'
|
||||
app.torrent.wanted.ftp.enabled: '%env(APP_TORRENT_WANTED_FTP_ENABLED)%'
|
||||
app.torrent.wanted.ftp.folder: '%env(APP_TORRENT_WANTED_FTP_FOLDER)%'
|
||||
app.torrent.wanted.ftp.approved: '%env(APP_TORRENT_WANTED_FTP_APPROVED_ONLY)%'
|
||||
app.index.torrent.name.enabled: '%env(APP_INDEX_TORRENT_NAME_ENABLED)%'
|
||||
app.index.torrent.filenames.enabled: '%env(APP_INDEX_TORRENT_FILENAMES_ENABLED)%'
|
||||
app.index.torrent.hash.v1.enabled: '%env(APP_INDEX_TORRENT_HASH_V1_ENABLED)%'
|
||||
app.index.torrent.hash.v2.enabled: '%env(APP_INDEX_TORRENT_HASH_V2_ENABLED)%'
|
||||
app.index.torrent.source.enabled: '%env(APP_INDEX_TORRENT_SOURCE_ENABLED)%'
|
||||
app.index.torrent.comment.enabled: '%env(APP_INDEX_TORRENT_COMMENT_ENABLED)%'
|
||||
app.index.word.length.min: '%env(APP_INDEX_WORD_LENGTH_MIN)%'
|
||||
app.index.word.length.max: '%env(APP_INDEX_WORD_LENGTH_MAX)%'
|
||||
|
||||
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
|
||||
|
||||
App\Twig\AppExtension:
|
||||
arguments:
|
||||
- '@service_container'
|
||||
tags:
|
||||
- { name: twig.extension}
|
||||
Binary file not shown.
14
docker-compose.override.yml
Normal file
14
docker-compose.override.yml
Normal file
|
|
@ -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 ###
|
||||
21
docker-compose.yml
Normal file
21
docker-compose.yml
Normal file
|
|
@ -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 @@
|
|||
@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,218 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 YGGverse
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* Project home page
|
||||
* https://github.com/YGGverse/YGGtracker
|
||||
*
|
||||
* Get support
|
||||
* https://github.com/YGGverse/YGGtracker/issues
|
||||
*/
|
||||
|
||||
// Debug
|
||||
ini_set('display_errors', '1');
|
||||
ini_set('display_startup_errors', '1');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
// Database
|
||||
define('DB_PORT', 3306);
|
||||
define('DB_HOST', 'localhost');
|
||||
define('DB_NAME', '');
|
||||
define('DB_USERNAME', '');
|
||||
define('DB_PASSWORD', '');
|
||||
|
||||
// Sphinx
|
||||
define('SPHINX_HOST', '127.0.0.1');
|
||||
define('SPHINX_PORT', 9306);
|
||||
|
||||
// Memcached
|
||||
define('MEMCACHED_PORT', 11211);
|
||||
define('MEMCACHED_HOST', 'localhost');
|
||||
define('MEMCACHED_NAMESPACE', 'yggtracker');
|
||||
define('MEMCACHED_TIMEOUT', 60 * 5);
|
||||
|
||||
// Webapp
|
||||
define('WEBSITE_URL', '');
|
||||
define('WEBSITE_NAME', 'YGGtracker');
|
||||
define('WEBSITE_CSS_VERSION', 1);
|
||||
|
||||
define('WEBSITE_PAGINATION_LIMIT', 20);
|
||||
|
||||
// Moderation
|
||||
define('MODERATOR_IP_LIST', (array)
|
||||
[
|
||||
'127.0.0.1',
|
||||
// ...
|
||||
]
|
||||
);
|
||||
|
||||
// User
|
||||
define('USER_DEFAULT_APPROVED', false);
|
||||
|
||||
define('USER_AUTO_APPROVE_ON_MAGNET_APPROVE', true);
|
||||
define('USER_AUTO_APPROVE_ON_COMMENT_APPROVE', true);
|
||||
define('USER_AUTO_APPROVE_ON_IMPORT_APPROVED', false);
|
||||
|
||||
define('USER_DEFAULT_IDENTICON', 'jidenticon'); // jidenticon|false
|
||||
define('USER_IDENTICON_FIELD', 'address'); // address|userId|...
|
||||
|
||||
// Magnet
|
||||
define('MAGNET_DEFAULT_APPROVED', USER_DEFAULT_APPROVED);
|
||||
define('MAGNET_DEFAULT_PUBLIC', false);
|
||||
define('MAGNET_DEFAULT_COMMENTS', true);
|
||||
define('MAGNET_DEFAULT_SENSITIVE', false);
|
||||
|
||||
define('MAGNET_AUTO_APPROVE_ON_IMPORT_APPROVED', true);
|
||||
|
||||
define('MAGNET_EDITOR_LOCK_TIMEOUT', 60*60);
|
||||
|
||||
define('MAGNET_TITLE_MIN_LENGTH', 10);
|
||||
define('MAGNET_TITLE_MAX_LENGTH', 140);
|
||||
define('MAGNET_TITLE_REGEX', '/.*/ui');
|
||||
|
||||
define('MAGNET_PREVIEW_MIN_LENGTH', 0);
|
||||
define('MAGNET_PREVIEW_MAX_LENGTH', 255);
|
||||
define('MAGNET_PREVIEW_REGEX', '/.*/ui');
|
||||
|
||||
define('MAGNET_DESCRIPTION_MIN_LENGTH', 0);
|
||||
define('MAGNET_DESCRIPTION_MAX_LENGTH', 10000);
|
||||
define('MAGNET_DESCRIPTION_REGEX', '/.*/ui');
|
||||
|
||||
define('MAGNET_DN_MIN_LENGTH', 2);
|
||||
define('MAGNET_DN_MAX_LENGTH', 255);
|
||||
define('MAGNET_DN_REGEX', '/.*/ui');
|
||||
|
||||
define('MAGNET_KT_MIN_LENGTH', 2);
|
||||
define('MAGNET_KT_MAX_LENGTH', 140);
|
||||
define('MAGNET_KT_REGEX', '/[\w]+/ui');
|
||||
define('MAGNET_KT_MIN_QUANTITY', 0);
|
||||
define('MAGNET_KT_MAX_QUANTITY', 20);
|
||||
|
||||
define('MAGNET_TR_MIN_QUANTITY', 1);
|
||||
define('MAGNET_TR_MAX_QUANTITY', 50);
|
||||
|
||||
define('MAGNET_AS_MIN_QUANTITY', 0);
|
||||
define('MAGNET_AS_MAX_QUANTITY', 50);
|
||||
|
||||
define('MAGNET_WS_MIN_QUANTITY', 0);
|
||||
define('MAGNET_WS_MAX_QUANTITY', 50);
|
||||
|
||||
define('MAGNET_STOP_WORDS_SIMILAR',
|
||||
[
|
||||
'series',
|
||||
'season',
|
||||
'discography',
|
||||
// ...
|
||||
]
|
||||
);
|
||||
|
||||
// Magnet comment
|
||||
define('MAGNET_COMMENT_DEFAULT_APPROVED', false);
|
||||
define('MAGNET_COMMENT_DEFAULT_PUBLIC', false);
|
||||
define('MAGNET_COMMENT_MIN_LENGTH', 1);
|
||||
define('MAGNET_COMMENT_MAX_LENGTH', 1000);
|
||||
|
||||
// Torrent
|
||||
define('TORRENT_ANNOUNCE_MIN_QUANTITY', 1);
|
||||
define('TORRENT_ANNOUNCE_MAX_QUANTITY', 50);
|
||||
|
||||
define('TORRENT_COMMENT_MIN_LENGTH', 0);
|
||||
define('TORRENT_COMMENT_MAX_LENGTH', 255);
|
||||
define('TORRENT_COMMENT_REGEX', '/.*/ui');
|
||||
|
||||
define('TORRENT_INFO_NAME_MIN_LENGTH', 0);
|
||||
define('TORRENT_INFO_NAME_MAX_LENGTH', 255);
|
||||
define('TORRENT_INFO_NAME_REGEX', '/.*/ui');
|
||||
|
||||
define('TORRENT_INFO_SOURCE_MIN_LENGTH', 0);
|
||||
define('TORRENT_INFO_SOURCE_MAX_LENGTH', 255);
|
||||
define('TORRENT_INFO_SOURCE_REGEX', '/.*/ui');
|
||||
|
||||
define('TORRENT_CREATED_BY_MIN_LENGTH', 0);
|
||||
define('TORRENT_CREATED_BY_MAX_LENGTH', 255);
|
||||
define('TORRENT_CREATED_BY_REGEX', '/.*/ui');
|
||||
|
||||
// Yggdrasil
|
||||
define('YGGDRASIL_HOST_REGEX', '/^0{0,1}[2-3][a-f0-9]{0,2}:/'); // thanks to @ygguser (https://github.com/YGGverse/YGGo/issues/1#issuecomment-1498182228 )
|
||||
|
||||
// Crawler
|
||||
define('CRAWLER_SCRAPE_QUEUE_LIMIT', 1);
|
||||
define('CRAWLER_SCRAPE_TIME_OFFLINE_TIMEOUT', 60*60*24);
|
||||
|
||||
// Node
|
||||
define('NODE_RULE_SUBJECT', 'Common');
|
||||
define('NODE_RULE_LANGUAGES', 'All');
|
||||
|
||||
// API
|
||||
define('API_VERSION', '1.0.0');
|
||||
|
||||
define('API_USER_AGENT', WEBSITE_NAME);
|
||||
|
||||
/// Export
|
||||
define('API_EXPORT_ENABLED', true);
|
||||
|
||||
define('API_EXPORT_PUSH_ENABLED', true); // depends of API_EXPORT_ENABLED
|
||||
|
||||
define('API_EXPORT_USERS_ENABLED', true); // depends of API_EXPORT_ENABLED
|
||||
define('API_EXPORT_MAGNETS_ENABLED', true); // depends of API_EXPORT_ENABLED, API_EXPORT_USERS_ENABLED
|
||||
define('API_EXPORT_MAGNET_DOWNLOADS_ENABLED', true); // depends of API_EXPORT_ENABLED, API_EXPORT_USERS_ENABLED, API_EXPORT_MAGNETS_ENABLED
|
||||
define('API_EXPORT_MAGNET_COMMENTS_ENABLED', true); // depends of API_EXPORT_ENABLED, API_EXPORT_USERS_ENABLED, API_EXPORT_MAGNETS_ENABLED
|
||||
define('API_EXPORT_MAGNET_STARS_ENABLED', true); // depends of API_EXPORT_ENABLED, API_EXPORT_USERS_ENABLED, API_EXPORT_MAGNETS_ENABLED
|
||||
define('API_EXPORT_MAGNET_VIEWS_ENABLED', true); // depends of API_EXPORT_ENABLED, API_EXPORT_USERS_ENABLED, API_EXPORT_MAGNETS_ENABLED
|
||||
|
||||
/// Import
|
||||
define('API_IMPORT_ENABLED', true);
|
||||
|
||||
define('API_IMPORT_PUSH_ENABLED', true); // depends of API_IMPORT_ENABLED
|
||||
|
||||
define('API_IMPORT_USERS_ENABLED', true); // depends of API_IMPORT_ENABLED
|
||||
define('API_IMPORT_USERS_APPROVED_ONLY', false); // depends of API_IMPORT_ENABLED, API_IMPORT_USERS_ENABLED
|
||||
define('API_IMPORT_MAGNETS_ENABLED', true); // depends of API_IMPORT_ENABLED, API_IMPORT_USERS_ENABLED
|
||||
define('API_IMPORT_MAGNETS_APPROVED_ONLY', false); // depends of API_IMPORT_ENABLED, API_IMPORT_USERS_ENABLED, API_IMPORT_MAGNETS_ENABLED
|
||||
define('API_IMPORT_MAGNET_DOWNLOADS_ENABLED', true); // depends of API_IMPORT_ENABLED, API_IMPORT_USERS_ENABLED, API_IMPORT_MAGNETS_ENABLED
|
||||
define('API_IMPORT_MAGNET_COMMENTS_ENABLED', true); // depends of API_IMPORT_ENABLED, API_IMPORT_USERS_ENABLED, API_IMPORT_MAGNETS_ENABLED
|
||||
define('API_IMPORT_MAGNET_COMMENTS_APPROVED_ONLY', false); // depends of API_IMPORT_ENABLED, API_IMPORT_USERS_ENABLED, API_IMPORT_MAGNETS_ENABLED, API_IMPORT_MAGNET_COMMENTS_ENABLED
|
||||
define('API_IMPORT_MAGNET_STARS_ENABLED', true); // depends of API_IMPORT_ENABLED, API_IMPORT_USERS_ENABLED, API_IMPORT_MAGNETS_ENABLED
|
||||
define('API_IMPORT_MAGNET_VIEWS_ENABLED', true); // depends of API_IMPORT_ENABLED, API_IMPORT_USERS_ENABLED, API_IMPORT_MAGNETS_ENABLED
|
||||
|
||||
// Logs
|
||||
define('LOG_DIRECTORY', __DIR__ . '/../storage/log');
|
||||
|
||||
define('LOG_CRONTAB_SCRAPE_ENABLED', true);
|
||||
define('LOG_CRONTAB_SCRAPE_FILENAME', sprintf('crontab_scrape_%s.log', date('Y-m-d')));
|
||||
|
||||
define('LOG_CRONTAB_SITEMAP_ENABLED', true);
|
||||
define('LOG_CRONTAB_SITEMAP_FILENAME', sprintf('crontab_sitemap_%s.log', date('Y-m-d')));
|
||||
|
||||
define('LOG_CRONTAB_EXPORT_FEED_ENABLED', true);
|
||||
define('LOG_CRONTAB_EXPORT_FEED_FILENAME', sprintf('crontab_export_feed_%s.log', date('Y-m-d')));
|
||||
|
||||
define('LOG_CRONTAB_EXPORT_PUSH_ENABLED', true);
|
||||
define('LOG_CRONTAB_EXPORT_PUSH_FILENAME', sprintf('crontab_export_push_%s.log', date('Y-m-d')));
|
||||
|
||||
define('LOG_CRONTAB_IMPORT_FEED_ENABLED', true);
|
||||
define('LOG_CRONTAB_IMPORT_FEED_FILENAME', sprintf('crontab_import_feed_%s.log', date('Y-m-d')));
|
||||
|
||||
define('LOG_API_PUSH_ENABLED', true);
|
||||
define('LOG_API_PUSH_FILENAME', sprintf('api_push_%s.log', date('Y-m-d')));
|
||||
|
|
@ -1,25 +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;
|
||||
}
|
||||
|
||||
location ~ \.php$ {
|
||||
include snippets/fastcgi-php.conf;
|
||||
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
|
||||
}
|
||||
|
||||
location ~ /\. {
|
||||
deny all;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
37
migrations/Version20231026163131.php
Normal file
37
migrations/Version20231026163131.php
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20231026163131 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('ALTER TABLE torrent ADD COLUMN status BOOLEAN DEFAULT 1');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__torrent AS SELECT id, user_id, added, scraped, locales, sensitive, approved, md5file, keywords, seeders, peers, leechers FROM torrent');
|
||||
$this->addSql('DROP TABLE torrent');
|
||||
$this->addSql('CREATE TABLE torrent (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, user_id INTEGER NOT NULL, added INTEGER NOT NULL, scraped INTEGER DEFAULT NULL, locales CLOB NOT NULL --(DC2Type:simple_array)
|
||||
, sensitive BOOLEAN NOT NULL, approved BOOLEAN NOT NULL, md5file VARCHAR(32) NOT NULL, keywords CLOB DEFAULT NULL --(DC2Type:simple_array)
|
||||
, seeders INTEGER DEFAULT NULL, peers INTEGER DEFAULT NULL, leechers INTEGER DEFAULT NULL)');
|
||||
$this->addSql('INSERT INTO torrent (id, user_id, added, scraped, locales, sensitive, approved, md5file, keywords, seeders, peers, leechers) SELECT id, user_id, added, scraped, locales, sensitive, approved, md5file, keywords, seeders, peers, leechers FROM __temp__torrent');
|
||||
$this->addSql('DROP TABLE __temp__torrent');
|
||||
}
|
||||
}
|
||||
35
migrations/Version20231029184600.php
Normal file
35
migrations/Version20231029184600.php
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20231029184600 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE TABLE torrent_poster (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, torrent_id INTEGER NOT NULL, user_id INTEGER NOT NULL, added INTEGER NOT NULL, approved BOOLEAN NOT NULL, md5file VARCHAR(32) NOT NULL)');
|
||||
$this->addSql('ALTER TABLE user ADD COLUMN posters BOOLEAN NOT NULL DEFAULT 1');
|
||||
$this->addSql('ALTER TABLE torrent ADD COLUMN torrent_poster_id BOOLEAN NULL');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('DROP TABLE torrent_poster');
|
||||
$this->addSql('ALTER TABLE user DROP COLUMN posters');
|
||||
$this->addSql('ALTER TABLE torrent DROP COLUMN torrent_poster_id');
|
||||
}
|
||||
}
|
||||
31
migrations/Version20231030225418.php
Normal file
31
migrations/Version20231030225418.php
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20231030225418 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('ALTER TABLE torrent_poster ADD COLUMN position BOOLEAN NOT NULL DEFAULT "center"');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('ALTER TABLE torrent_poster DROP COLUMN position');
|
||||
}
|
||||
}
|
||||
37
migrations/Version20231103235504.php
Normal file
37
migrations/Version20231103235504.php
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20231103235504 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE TABLE torrent_categories (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, torrent_id INTEGER NOT NULL, user_id INTEGER NOT NULL, added INTEGER NOT NULL, value CLOB NOT NULL --(DC2Type:simple_array)
|
||||
, approved BOOLEAN NOT NULL)');
|
||||
$this->addSql('ALTER TABLE user ADD COLUMN categories CLOB DEFAULT "other"');
|
||||
$this->addSql('ALTER TABLE torrent ADD COLUMN categories CLOB DEFAULT "other"');
|
||||
$this->addSql('UPDATE user SET categories = "movie,series,tv,animation,music,game,audiobook,podcast,book,archive,picture,software,other"');
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('DROP TABLE torrent_categories');
|
||||
$this->addSql('ALTER TABLE user DROP COLUMN categories');
|
||||
$this->addSql('ALTER TABLE torrent DROP COLUMN categories');
|
||||
}
|
||||
}
|
||||
38
phpunit.xml.dist
Normal file
38
phpunit.xml.dist
Normal file
|
|
@ -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>
|
||||
128
public/asset/default/css/common.css
Normal file
128
public/asset/default/css/common.css
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
* {
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
:focus,
|
||||
:focus-within,
|
||||
:focus-visible,
|
||||
:active,
|
||||
:target,
|
||||
:hover {
|
||||
opacity: 1;
|
||||
transition: opacity .2s ease-in-out;
|
||||
|
||||
}
|
||||
|
||||
body {
|
||||
background: #282b3c;
|
||||
color: #ccc;
|
||||
font-family: Sans-serif;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
a,
|
||||
a:visited,
|
||||
a:active {
|
||||
color: #96d9a1;
|
||||
text-decoration: none;
|
||||
opacity: .9;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5 {
|
||||
display: inline-block;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #ccc;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
input,
|
||||
button,
|
||||
select,
|
||||
textarea {
|
||||
accent-color: #65916d;
|
||||
background: #5d627d;
|
||||
border: #5d627d 1px solid;
|
||||
color: #ccc;
|
||||
border-radius: 3px;
|
||||
padding: 6px 8px;
|
||||
opacity: .96;
|
||||
}
|
||||
|
||||
input[type="file"] {
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
/*
|
||||
main input,
|
||||
main button,
|
||||
main select,
|
||||
main textarea {
|
||||
padding: 8px;
|
||||
}
|
||||
*/
|
||||
textarea:focus,
|
||||
input:focus {
|
||||
border: #65916d 1px solid;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
select[multiple="multiple"] > option {
|
||||
border-top: 1px #5d627d solid;
|
||||
border-bottom: 1px #5d627d solid;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
select[multiple="multiple"] > option:active,
|
||||
select[multiple="multiple"] > option:focus,
|
||||
select[multiple="multiple"] > option:focus-within,
|
||||
select[multiple="multiple"] > option:checked {
|
||||
border-top: 1px #65916d solid;
|
||||
border-bottom: 1px #65916d solid;
|
||||
background: linear-gradient(#65916d, #65916d);
|
||||
}
|
||||
|
||||
button,
|
||||
input[type="submit"] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
textarea,
|
||||
select[multiple="multiple"] {
|
||||
min-height: 180px;
|
||||
}
|
||||
|
||||
textarea::placeholder,
|
||||
input::placeholder {
|
||||
color: #9698a5;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
input[type="text"]:hover,
|
||||
textarea:hover {
|
||||
background: #636884;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 2px 0;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
header a.logo {
|
||||
color: #ccc;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
header a.logo > span {
|
||||
color: #96d9a1;
|
||||
}
|
||||
553
public/asset/default/css/framework.css
Normal file
553
public/asset/default/css/framework.css
Normal file
|
|
@ -0,0 +1,553 @@
|
|||
.container {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
max-width: 748px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.row {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.column {
|
||||
position: relative;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.word-break {
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.overflow-auto {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.float-left {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.float-right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.text-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.text-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.text-color-green,
|
||||
a.text-color-green,
|
||||
a.text-color-green:active,
|
||||
a.text-color-green:visited,
|
||||
a.text-color-green:hover {
|
||||
color: #96d9a1;
|
||||
}
|
||||
|
||||
.text-color-red,
|
||||
a.text-color-red,
|
||||
a.text-color-red:active,
|
||||
a.text-color-red:visited,
|
||||
a.text-color-red:hover {
|
||||
color: #d77575;
|
||||
}
|
||||
|
||||
.text-color-pink,
|
||||
a.text-color-pink,
|
||||
a.text-color-pink:active,
|
||||
a.text-color-pink:visited,
|
||||
a.text-color-pink:hover {
|
||||
color: #b55cab;
|
||||
}
|
||||
|
||||
.text-color-default,
|
||||
a.text-color-default,
|
||||
a.text-color-default:active,
|
||||
a.text-color-default:visited,
|
||||
a.text-color-default:hover {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.text-color-white,
|
||||
a.text-color-white,
|
||||
a.text-color-white:active,
|
||||
a.text-color-white:visited,
|
||||
a.text-color-white:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/*
|
||||
.text-color-pink {
|
||||
color: #a44399;
|
||||
}
|
||||
*/
|
||||
|
||||
.text-color-blue {
|
||||
color: #5785b7;
|
||||
}
|
||||
|
||||
.text-color-night,
|
||||
a.text-color-night,
|
||||
a.text-color-night:active,
|
||||
a.text-color-night:visited,
|
||||
a.text-color-night:hover {
|
||||
color: #838695;
|
||||
}
|
||||
|
||||
.label {
|
||||
padding: 4px 8px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.label-green,
|
||||
a.label-green,
|
||||
a.label-green:active,
|
||||
a.label-green:visited,
|
||||
a.label-green:hover {
|
||||
color: #fff;
|
||||
background-color: #65916d;
|
||||
}
|
||||
|
||||
.button,
|
||||
a.button,
|
||||
a.button:active,
|
||||
a.button:visited,
|
||||
a.button:hover {
|
||||
background: #5d627d;
|
||||
border: #5d627d 1px solid;
|
||||
color: #ccc;
|
||||
padding: 6px 8px;
|
||||
border-radius: 3px;
|
||||
opacity: .96;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.button-green,
|
||||
a.button-green,
|
||||
a.button-green:active,
|
||||
a.button-green:visited,
|
||||
a.button-green:hover {
|
||||
color: #fff;
|
||||
background-color: #65916d;
|
||||
border: #65916d 1px solid;
|
||||
}
|
||||
|
||||
.button-green:hover {
|
||||
color: #fff;
|
||||
background-color: #709e79;
|
||||
}
|
||||
|
||||
.position-relative {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.position-fixed {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.position-absolute {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.vertical-align-middle {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.top-2-px {
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
.line-height-20-px {
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.line-height-26-px {
|
||||
line-height: 26px;
|
||||
}
|
||||
|
||||
.border-radius-3-px {
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.border-radius-50 {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.border-color-pink-light {
|
||||
border: 1px #9b6895 solid;
|
||||
}
|
||||
|
||||
.border-color-pink {
|
||||
border: 1px #a44399 solid;
|
||||
}
|
||||
|
||||
.border-color-green {
|
||||
border: 1px #65916d solid;
|
||||
}
|
||||
|
||||
.border-color-pink {
|
||||
border-bottom: 1px #a44399 solid;
|
||||
}
|
||||
|
||||
.border-color-default {
|
||||
border: 1px rgba(93, 98, 125, .6) solid;
|
||||
}
|
||||
|
||||
.border-bottom-default {
|
||||
border-bottom: 1px rgba(93, 98, 125, .6) solid;
|
||||
}
|
||||
|
||||
.border-top-default {
|
||||
border-top: 1px rgba(93, 98, 125, .6) solid;
|
||||
}
|
||||
|
||||
.border-bottom-dashed {
|
||||
border-bottom: 1px rgba(93, 98, 125, .6) dashed;
|
||||
}
|
||||
|
||||
.border-top-dashed {
|
||||
border-top: 1px rgba(93, 98, 125, .6) dashed;
|
||||
}
|
||||
|
||||
.border-width-2-px {
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
.background-poster {
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-blend-mode: soft-light;
|
||||
}
|
||||
|
||||
.background-color-night {
|
||||
background-color: #34384f;
|
||||
}
|
||||
|
||||
.background-color-night-light {
|
||||
background-color: #3d4159;
|
||||
}
|
||||
|
||||
.background-color-green {
|
||||
background-color: #65916d;
|
||||
}
|
||||
|
||||
.background-color-hover-night-light:hover,
|
||||
a.background-color-hover-night-light:hover,
|
||||
a:active.background-color-hover-night-light:hover,
|
||||
a:visited.background-color-hover-night-light:hover {
|
||||
/*color: #fff;*/
|
||||
background-color: #3d4159;
|
||||
}
|
||||
|
||||
.background-color-red {
|
||||
background-color: #9b4a4a;
|
||||
}
|
||||
|
||||
.cursor-default {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.cursor-help {
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.font-weight-normal {
|
||||
font-weight: normal
|
||||
}
|
||||
|
||||
.font-weight-200 {
|
||||
font-weight: 200
|
||||
}
|
||||
|
||||
.font-size-10-px {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.font-size-12-px {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.font-size-22-px {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.padding-0 {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.padding-x-0 {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.padding-4-px {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.padding-l-4-px {
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.padding-t-4-px {
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.padding-y-4-px {
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.padding-x-4-px {
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.padding-x-8-px {
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.padding-y-6-px {
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
.padding-8-px {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.padding-l-8-px {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.padding-t-8-px {
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.padding-b-8-px {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.padding-y-8-px {
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.padding-y-12-px {
|
||||
padding-top: 12px;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
.padding-b-16-px {
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.padding-t-16-px {
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
.padding-y-16-px {
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.padding-x-16-px {
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
.padding-16-px {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.padding-24-px {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.padding-x-24-px {
|
||||
padding-left: 24px;
|
||||
padding-right: 24px;
|
||||
}
|
||||
|
||||
.padding-y-24-px {
|
||||
padding-top: 24px;
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
|
||||
.margin-t-4-px {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.margin-l-4-px {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.margin-8-px {
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
.margin-x-8-px {
|
||||
margin-left: 8px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.margin-l-8-px {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.margin-16-px {
|
||||
margin: 16px;
|
||||
}
|
||||
|
||||
.margin-l-16-px {
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
.margin-x-4-px {
|
||||
margin-left: 4px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.margin-b-4-px {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.margin-r-4-px {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.margin-r-8-px {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.margin-l-12-px {
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.margin-l-96-px {
|
||||
margin-left: 96px;
|
||||
}
|
||||
|
||||
.margin-l--48-px {
|
||||
margin-left: -48px;
|
||||
}
|
||||
|
||||
.margin-y-8-px {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.margin-t-8-px {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.margin-b-8-px {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.margin-y-16-px {
|
||||
margin-top: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.margin-t-16-px {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.margin-b-16-px {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.margin-b-24-px {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.display-block {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.display-inline-block {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.display-flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.opacity-0 {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.opacity-06 {
|
||||
opacity: .6;
|
||||
}
|
||||
|
||||
.opacity-hover-1:hover {
|
||||
opacity: 1;
|
||||
transition: opacity .2s;
|
||||
}
|
||||
|
||||
*:hover > .parent-hover-opacity-09 {
|
||||
opacity: .9;
|
||||
transition: opacity .2s;
|
||||
}
|
||||
|
||||
.blur-2 {
|
||||
filter: blur(2px);
|
||||
}
|
||||
|
||||
.blur-hover-0:hover {
|
||||
filter: blur(0);
|
||||
}
|
||||
|
||||
/* responsive rules */
|
||||
|
||||
.width-100 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.width-50 {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.width-20 {
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.width-80 {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.width-80-px {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.min-width-120-px {
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.min-width-200-px {
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
@media (max-width: 1220px) {
|
||||
|
||||
.width-tablet-100 {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 512px) {
|
||||
|
||||
.width-mobile-100 {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
9
public/index.php
Normal file
9
public/index.php
Normal file
|
|
@ -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
src/Controller/.gitignore
vendored
Normal file
0
src/Controller/.gitignore
vendored
Normal file
1905
src/Controller/ActivityController.php
Normal file
1905
src/Controller/ActivityController.php
Normal file
File diff suppressed because it is too large
Load diff
164
src/Controller/SearchController.php
Normal file
164
src/Controller/SearchController.php
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
<?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\UserService;
|
||||
use App\Service\TorrentService;
|
||||
use App\Service\ActivityService;
|
||||
|
||||
class SearchController extends AbstractController
|
||||
{
|
||||
public function module(
|
||||
Request $request,
|
||||
UserService $userService,
|
||||
TorrentService $torrentService,
|
||||
ActivityService $activityService
|
||||
): Response
|
||||
{
|
||||
// Defaults
|
||||
$locales = [];
|
||||
$categories = [];
|
||||
$sensitive = [];
|
||||
|
||||
// Request
|
||||
$query = $request->get('query') ? urldecode($request->get('query')) : '';
|
||||
$filter = $request->get('filter') ? true : false;
|
||||
|
||||
// Extended search
|
||||
if ($filter)
|
||||
{
|
||||
// Init user
|
||||
$user = $this->initUser(
|
||||
$request,
|
||||
$userService,
|
||||
$activityService
|
||||
);
|
||||
|
||||
// Keywords
|
||||
$keywords = explode(' ', $query);
|
||||
|
||||
// Locales
|
||||
foreach (explode('|', $this->getParameter('app.locales')) as $locale)
|
||||
{
|
||||
|
||||
if ($request->get('locales'))
|
||||
{
|
||||
$checked = in_array($locale, (array) $request->get('locales'));
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
$checked = in_array($locale, $user->getLocales());
|
||||
}
|
||||
|
||||
$locales[] =
|
||||
[
|
||||
'value' => $locale,
|
||||
'checked' => $checked,
|
||||
'total' => $torrentService->findTorrentsTotal(
|
||||
0,
|
||||
$keywords,
|
||||
[$locale],
|
||||
$request->get('categories') ? $request->get('categories') : $user->getCategories(),
|
||||
$request->get('sensitive') ? null : false,
|
||||
!$user->isModerator() ? true : null,
|
||||
!$user->isModerator() ? true : null,
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
// Categories
|
||||
foreach (explode('|', $this->getParameter('app.categories')) as $category)
|
||||
{
|
||||
if ($request->get('categories'))
|
||||
{
|
||||
$checked = in_array($category, (array) $request->get('categories'));
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
$checked = in_array($category, $user->getCategories());
|
||||
}
|
||||
|
||||
$categories[] =
|
||||
[
|
||||
'value' => $category,
|
||||
'checked' => $checked,
|
||||
'total' => $torrentService->findTorrentsTotal(
|
||||
0,
|
||||
$keywords,
|
||||
$request->get('locales') ? $request->get('locales') : $user->getLocales(),
|
||||
[$category],
|
||||
$request->get('sensitive') ? null : false,
|
||||
!$user->isModerator() ? true : null,
|
||||
!$user->isModerator() ? true : null,
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
// Sensitive
|
||||
$sensitive =
|
||||
[
|
||||
'checked' => $request->get('sensitive'),
|
||||
'total' => $torrentService->findTorrentsTotal(
|
||||
0,
|
||||
$keywords,
|
||||
$request->get('locales') ? $request->get('locales') : $user->getLocales(),
|
||||
$request->get('categories') ? $request->get('categories') : $user->getCategories(),
|
||||
true,
|
||||
!$user->isModerator() ? true : null,
|
||||
!$user->isModerator() ? true : null,
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
return $this->render(
|
||||
'default/search/module.html.twig',
|
||||
[
|
||||
'query' => $query,
|
||||
'filter' => $filter,
|
||||
'sensitive' => $sensitive,
|
||||
'locales' => $locales,
|
||||
'categories' => $categories,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
private function initUser(
|
||||
Request $request,
|
||||
UserService $userService,
|
||||
ActivityService $activityService
|
||||
): ?\App\Entity\User
|
||||
{
|
||||
// Init user
|
||||
if (!$user = $userService->findUserByAddress($request->getClientIp()))
|
||||
{
|
||||
$user = $userService->addUser(
|
||||
$request->getClientIp(),
|
||||
time(),
|
||||
$this->getParameter('app.locale'),
|
||||
explode('|', $this->getParameter('app.locales')),
|
||||
$activityService->getEventCodes(),
|
||||
$this->getParameter('app.theme'),
|
||||
$this->getParameter('app.sensitive'),
|
||||
$this->getParameter('app.yggdrasil'),
|
||||
$this->getParameter('app.posters'),
|
||||
$this->getParameter('app.approved')
|
||||
);
|
||||
|
||||
// Add user join event
|
||||
$activityService->addEventUserAdd(
|
||||
$user->getId(),
|
||||
time()
|
||||
);
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
}
|
||||
3486
src/Controller/TorrentController.php
Normal file
3486
src/Controller/TorrentController.php
Normal file
File diff suppressed because it is too large
Load diff
672
src/Controller/UserController.php
Normal file
672
src/Controller/UserController.php
Normal file
|
|
@ -0,0 +1,672 @@
|
|||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
use App\Service\ActivityService;
|
||||
use App\Service\UserService;
|
||||
use App\Service\TorrentService;
|
||||
|
||||
class UserController extends AbstractController
|
||||
{
|
||||
#[Route('/')]
|
||||
public function root(
|
||||
Request $request,
|
||||
UserService $userService,
|
||||
ActivityService $activityService
|
||||
): Response
|
||||
{
|
||||
// Init user
|
||||
$user = $this->initUser(
|
||||
$request,
|
||||
$userService,
|
||||
$activityService
|
||||
);
|
||||
|
||||
return $this->redirectToRoute(
|
||||
'torrent_recent',
|
||||
[
|
||||
'_locale' => $user->getLocale()
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[Route(
|
||||
'/{_locale}/settings',
|
||||
name: 'user_settings',
|
||||
requirements: [
|
||||
'_locale' => '%app.locales%',
|
||||
],
|
||||
)]
|
||||
public function settings(
|
||||
Request $request,
|
||||
UserService $userService,
|
||||
ActivityService $activityService
|
||||
): Response
|
||||
{
|
||||
// Init user
|
||||
$user = $this->initUser(
|
||||
$request,
|
||||
$userService,
|
||||
$activityService
|
||||
);
|
||||
|
||||
// Process post request
|
||||
if ($request->isMethod('post'))
|
||||
{
|
||||
// Update locale
|
||||
if (in_array($request->get('locale'), explode('|', $this->getParameter('app.locales'))))
|
||||
{
|
||||
$user->setLocale(
|
||||
$request->get('locale')
|
||||
);
|
||||
}
|
||||
|
||||
// Update locales
|
||||
if ($request->get('locales'))
|
||||
{
|
||||
$locales = [];
|
||||
foreach ((array) $request->get('locales') as $locale)
|
||||
{
|
||||
if (in_array($locale, explode('|', $this->getParameter('app.locales'))))
|
||||
{
|
||||
$locales[] = $locale;
|
||||
}
|
||||
}
|
||||
|
||||
$user->setLocales(
|
||||
$locales
|
||||
);
|
||||
}
|
||||
|
||||
// Update categories
|
||||
if ($request->get('categories'))
|
||||
{
|
||||
$categories = [];
|
||||
foreach ((array) $request->get('categories') as $category)
|
||||
{
|
||||
if (in_array($category, explode('|', $this->getParameter('app.categories'))))
|
||||
{
|
||||
$categories[] = $category;
|
||||
}
|
||||
}
|
||||
|
||||
$user->setCategories(
|
||||
$categories
|
||||
);
|
||||
}
|
||||
|
||||
// Update theme
|
||||
if (in_array($request->get('theme'), explode('|', $this->getParameter('app.themes'))))
|
||||
{
|
||||
$user->setTheme(
|
||||
$request->get('theme')
|
||||
);
|
||||
}
|
||||
|
||||
// Update events
|
||||
$events = [];
|
||||
foreach ((array) $request->get('events') as $event)
|
||||
{
|
||||
if (in_array($event, $activityService->getEventCodes()))
|
||||
{
|
||||
$events[] = $event;
|
||||
}
|
||||
}
|
||||
|
||||
$user->setEvents(
|
||||
$events
|
||||
);
|
||||
|
||||
// Update sensitive
|
||||
$user->setSensitive(
|
||||
$request->get('sensitive') === 'true'
|
||||
);
|
||||
|
||||
// Update yggdrasil
|
||||
$user->setYggdrasil(
|
||||
$request->get('yggdrasil') === 'true'
|
||||
);
|
||||
|
||||
// Update posters
|
||||
$user->setPosters(
|
||||
$request->get('posters') === 'true'
|
||||
);
|
||||
|
||||
// Save changes to DB
|
||||
$userService->save($user);
|
||||
|
||||
// Redirect user to new locale
|
||||
return $this->redirectToRoute(
|
||||
'user_settings',
|
||||
[
|
||||
'_locale' => $user->getLocale()
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// Render template
|
||||
return $this->render(
|
||||
'default/user/settings.html.twig',
|
||||
[
|
||||
'user' => [
|
||||
'id' => $user->getId(),
|
||||
'sensitive' => $user->isSensitive(),
|
||||
'yggdrasil' => $user->isYggdrasil(),
|
||||
'posters' => $user->isPosters(),
|
||||
'locale' => $user->getLocale(),
|
||||
'locales' => $user->getLocales(),
|
||||
'categories' => $user->getCategories(),
|
||||
'events' => $user->getEvents(),
|
||||
'theme' => $user->getTheme(),
|
||||
'added' => $user->getAdded()
|
||||
],
|
||||
'locales' => explode('|', $this->getParameter('app.locales')),
|
||||
'categories' => explode('|', $this->getParameter('app.categories')),
|
||||
'themes' => explode('|', $this->getParameter('app.themes')),
|
||||
'events' => $activityService->getEventsTree()
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[Route(
|
||||
'/{_locale}/profile/{userId}',
|
||||
name: 'user_info',
|
||||
defaults: [
|
||||
'userId' => 0,
|
||||
],
|
||||
requirements: [
|
||||
'_locale' => '%app.locales%',
|
||||
'userId' => '\d+',
|
||||
],
|
||||
)]
|
||||
public function info(
|
||||
Request $request,
|
||||
TranslatorInterface $translator,
|
||||
UserService $userService,
|
||||
ActivityService $activityService
|
||||
): Response
|
||||
{
|
||||
// Init user
|
||||
$user = $this->initUser(
|
||||
$request,
|
||||
$userService,
|
||||
$activityService
|
||||
);
|
||||
|
||||
if (!$user->isStatus())
|
||||
{
|
||||
// @TODO
|
||||
throw new \Exception(
|
||||
$translator->trans('Access denied')
|
||||
);
|
||||
}
|
||||
|
||||
// Init target user
|
||||
if (!$userTarget = $userService->getUser(
|
||||
$request->get('userId') ? $request->get('userId') : $user->getId()
|
||||
))
|
||||
{
|
||||
throw $this->createNotFoundException();
|
||||
}
|
||||
|
||||
// Get total activities
|
||||
$total = $activityService->findActivitiesTotalByUserId(
|
||||
$userTarget->getId(),
|
||||
$user->getEvents()
|
||||
);
|
||||
|
||||
// Init page
|
||||
$page = $request->get('page') ? (int) $request->get('page') : 1;
|
||||
|
||||
// Render template
|
||||
return $this->render(
|
||||
'default/user/info.html.twig',
|
||||
[
|
||||
'session' =>
|
||||
[
|
||||
'user' => $user,
|
||||
'owner' => $user->getId() === $userTarget->getId(),
|
||||
'moderator' => $user->isModerator()
|
||||
],
|
||||
'user' => [
|
||||
'id' => $userTarget->getId(),
|
||||
'address' => $userTarget->getAddress(),
|
||||
'moderator' => $userTarget->isModerator(),
|
||||
'approved' => $userTarget->isApproved(),
|
||||
'status' => $userTarget->isStatus(),
|
||||
'posters' => $userTarget->isPosters(),
|
||||
'sensitive' => $userTarget->isSensitive(),
|
||||
'yggdrasil' => $userTarget->isYggdrasil(),
|
||||
'locale' => $userTarget->getLocale(),
|
||||
'locales' => $userTarget->getLocales(),
|
||||
'categories' => $user->getCategories(),
|
||||
'events' => $userTarget->getEvents(),
|
||||
'theme' => $userTarget->getTheme(),
|
||||
'added' => $userTarget->getAdded(),
|
||||
'identicon' => $userService->identicon(
|
||||
$userTarget->getAddress(),
|
||||
48
|
||||
),
|
||||
'owner' => $user->getId() === $userTarget->getId(),
|
||||
'star' =>
|
||||
[
|
||||
'exist' => (bool) $userService->findUserStar(
|
||||
$user->getId(),
|
||||
$userTarget->getId()
|
||||
),
|
||||
'total' => $userService->findUserStarsTotalByUserIdTarget(
|
||||
$userTarget->getId()
|
||||
)
|
||||
],
|
||||
'activities' => $activityService->findLastActivitiesByUserId(
|
||||
$userTarget->getId(),
|
||||
$userTarget->getEvents(),
|
||||
$this->getParameter('app.pagination'),
|
||||
($page - 1) * $this->getParameter('app.pagination')
|
||||
)
|
||||
],
|
||||
'events' => $activityService->getEventsTree(),
|
||||
'pagination' =>
|
||||
[
|
||||
'page' => $page,
|
||||
'pages' => ceil($total / $this->getParameter('app.pagination')),
|
||||
'total' => $total
|
||||
]
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[Route(
|
||||
'/{_locale}/user/star/toggle/{userId}',
|
||||
name: 'user_star_toggle',
|
||||
requirements:
|
||||
[
|
||||
'_locale' => '%app.locales%',
|
||||
'userId' => '\d+',
|
||||
],
|
||||
methods:
|
||||
[
|
||||
'GET'
|
||||
]
|
||||
)]
|
||||
public function toggleStar(
|
||||
Request $request,
|
||||
TranslatorInterface $translator,
|
||||
UserService $userService,
|
||||
ActivityService $activityService
|
||||
): Response
|
||||
{
|
||||
// Init user
|
||||
$user = $this->initUser(
|
||||
$request,
|
||||
$userService,
|
||||
$activityService
|
||||
);
|
||||
|
||||
if (!$user->isStatus())
|
||||
{
|
||||
// @TODO
|
||||
throw new \Exception(
|
||||
$translator->trans('Access denied')
|
||||
);
|
||||
}
|
||||
|
||||
// Block crawler requests
|
||||
if (in_array($request->getClientIp(), explode('|', $this->getParameter('app.crawlers'))))
|
||||
{
|
||||
throw $this->createNotFoundException();
|
||||
}
|
||||
|
||||
// Init target user
|
||||
if (!$userTarget = $userService->getUser($request->get('userId')))
|
||||
{
|
||||
throw $this->createNotFoundException();
|
||||
}
|
||||
|
||||
// Update
|
||||
$value = $userService->toggleUserStar(
|
||||
$user->getId(),
|
||||
$userTarget->getId(),
|
||||
time()
|
||||
);
|
||||
|
||||
// Add activity event
|
||||
if ($value)
|
||||
{
|
||||
$activityService->addEventUserStarAdd(
|
||||
$user->getId(),
|
||||
time(),
|
||||
$userTarget->getId()
|
||||
);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
$activityService->addEventUserStarDelete(
|
||||
$user->getId(),
|
||||
time(),
|
||||
$userTarget->getId()
|
||||
);
|
||||
}
|
||||
|
||||
// Redirect
|
||||
return $this->redirectToRoute(
|
||||
'user_info',
|
||||
[
|
||||
'_locale' => $request->get('_locale'),
|
||||
'userId' => $userTarget->getId()
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[Route(
|
||||
'/{_locale}/user/{userId}/moderator/toggle',
|
||||
name: 'user_moderator_toggle',
|
||||
requirements:
|
||||
[
|
||||
'_locale' => '%app.locales%',
|
||||
'userId' => '\d+',
|
||||
],
|
||||
methods:
|
||||
[
|
||||
'GET'
|
||||
]
|
||||
)]
|
||||
public function toggleModerator(
|
||||
Request $request,
|
||||
TranslatorInterface $translator,
|
||||
UserService $userService,
|
||||
ActivityService $activityService
|
||||
): Response
|
||||
{
|
||||
// Init user
|
||||
$user = $this->initUser(
|
||||
$request,
|
||||
$userService,
|
||||
$activityService
|
||||
);
|
||||
|
||||
if (!$user->isModerator())
|
||||
{
|
||||
// @TODO
|
||||
throw new \Exception(
|
||||
$translator->trans('Access denied')
|
||||
);
|
||||
}
|
||||
|
||||
// Init target user
|
||||
if (!$userTarget = $userService->getUser($request->get('userId')))
|
||||
{
|
||||
throw $this->createNotFoundException();
|
||||
}
|
||||
|
||||
// Update user moderator
|
||||
$value = $userService->toggleUserModerator(
|
||||
$userTarget->getId()
|
||||
)->isModerator();
|
||||
|
||||
// Add activity event
|
||||
if ($value)
|
||||
{
|
||||
$activityService->addEventUserModeratorAdd(
|
||||
$user->getId(),
|
||||
time(),
|
||||
$userTarget->getId()
|
||||
);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
$activityService->addEventUserModeratorDelete(
|
||||
$user->getId(),
|
||||
time(),
|
||||
$userTarget->getId()
|
||||
);
|
||||
}
|
||||
|
||||
// Redirect
|
||||
return $this->redirectToRoute(
|
||||
'user_info',
|
||||
[
|
||||
'_locale' => $request->get('_locale'),
|
||||
'userId' => $userTarget->getId()
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[Route(
|
||||
'/{_locale}/user/{userId}/status/toggle',
|
||||
name: 'user_status_toggle',
|
||||
requirements:
|
||||
[
|
||||
'_locale' => '%app.locales%',
|
||||
'userId' => '\d+',
|
||||
],
|
||||
methods:
|
||||
[
|
||||
'GET'
|
||||
]
|
||||
)]
|
||||
public function toggleStatus(
|
||||
Request $request,
|
||||
TranslatorInterface $translator,
|
||||
UserService $userService,
|
||||
ActivityService $activityService
|
||||
): Response
|
||||
{
|
||||
// Init user
|
||||
$user = $this->initUser(
|
||||
$request,
|
||||
$userService,
|
||||
$activityService
|
||||
);
|
||||
|
||||
if (!$user->isModerator())
|
||||
{
|
||||
// @TODO
|
||||
throw new \Exception(
|
||||
$translator->trans('Access denied')
|
||||
);
|
||||
}
|
||||
|
||||
// Init target user
|
||||
if (!$userTarget = $userService->getUser($request->get('userId')))
|
||||
{
|
||||
throw $this->createNotFoundException();
|
||||
}
|
||||
|
||||
// Update user status
|
||||
$value = $userService->toggleUserStatus(
|
||||
$userTarget->getId()
|
||||
)->isStatus();
|
||||
|
||||
// Add activity event
|
||||
if ($value)
|
||||
{
|
||||
$activityService->addEventUserStatusAdd(
|
||||
$user->getId(),
|
||||
time(),
|
||||
$userTarget->getId()
|
||||
);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
$activityService->addEventUserStatusDelete(
|
||||
$user->getId(),
|
||||
time(),
|
||||
$userTarget->getId()
|
||||
);
|
||||
}
|
||||
|
||||
// Redirect
|
||||
return $this->redirectToRoute(
|
||||
'user_info',
|
||||
[
|
||||
'_locale' => $request->get('_locale'),
|
||||
'userId' => $userTarget->getId()
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[Route(
|
||||
'/{_locale}/user/{userId}/approved/toggle',
|
||||
name: 'user_approved_toggle',
|
||||
requirements:
|
||||
[
|
||||
'_locale' => '%app.locales%',
|
||||
'userId' => '\d+',
|
||||
],
|
||||
methods:
|
||||
[
|
||||
'GET'
|
||||
]
|
||||
)]
|
||||
public function toggleApproved(
|
||||
Request $request,
|
||||
TranslatorInterface $translator,
|
||||
UserService $userService,
|
||||
TorrentService $torrentService,
|
||||
ActivityService $activityService
|
||||
): Response
|
||||
{
|
||||
// Init user
|
||||
$user = $this->initUser(
|
||||
$request,
|
||||
$userService,
|
||||
$activityService
|
||||
);
|
||||
|
||||
if (!$user->isModerator())
|
||||
{
|
||||
// @TODO
|
||||
throw new \Exception(
|
||||
$translator->trans('Access denied')
|
||||
);
|
||||
}
|
||||
|
||||
// Init target user
|
||||
if (!$userTarget = $userService->getUser($request->get('userId')))
|
||||
{
|
||||
throw $this->createNotFoundException();
|
||||
}
|
||||
|
||||
// Auto-approve all related content on user approve
|
||||
if (!$userTarget->isApproved())
|
||||
{
|
||||
$torrentService->setTorrentsApprovedByUserId(
|
||||
$userTarget->getId(),
|
||||
true
|
||||
);
|
||||
|
||||
$torrentService->setTorrentLocalesApprovedByUserId(
|
||||
$userTarget->getId(),
|
||||
true
|
||||
);
|
||||
|
||||
$torrentService->setTorrentCategoriesApprovedByUserId(
|
||||
$userTarget->getId(),
|
||||
true
|
||||
);
|
||||
|
||||
$torrentService->setTorrentSensitivesApprovedByUserId(
|
||||
$userTarget->getId(),
|
||||
true
|
||||
);
|
||||
|
||||
$torrentService->setTorrentPostersApprovedByUserId(
|
||||
$userTarget->getId(),
|
||||
true
|
||||
);
|
||||
|
||||
// @TODO make event for each item
|
||||
}
|
||||
|
||||
// Update user approved
|
||||
$value = $userService->toggleUserApproved(
|
||||
$userTarget->getId()
|
||||
)->isApproved();
|
||||
|
||||
// Add activity event
|
||||
if ($value)
|
||||
{
|
||||
$activityService->addEventUserApproveAdd(
|
||||
$user->getId(),
|
||||
time(),
|
||||
$userTarget->getId()
|
||||
);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
$activityService->addEventUserApproveDelete(
|
||||
$user->getId(),
|
||||
time(),
|
||||
$userTarget->getId()
|
||||
);
|
||||
}
|
||||
|
||||
// Redirect
|
||||
return $this->redirectToRoute(
|
||||
'user_info',
|
||||
[
|
||||
'_locale' => $request->get('_locale'),
|
||||
'userId' => $userTarget->getId()
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function module(?string $route): Response
|
||||
{
|
||||
return $this->render(
|
||||
'default/user/module.html.twig',
|
||||
[
|
||||
'route' => $route,
|
||||
'stars' => 0,
|
||||
'views' => 0,
|
||||
'comments' => 0,
|
||||
'downloads' => 0,
|
||||
'editions' => 0,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
private function initUser(
|
||||
Request $request,
|
||||
UserService $userService,
|
||||
ActivityService $activityService
|
||||
): ?\App\Entity\User
|
||||
{
|
||||
// Init user
|
||||
if (!$user = $userService->findUserByAddress($request->getClientIp()))
|
||||
{
|
||||
$user = $userService->addUser(
|
||||
$request->getClientIp(),
|
||||
time(),
|
||||
$this->getParameter('app.locale'),
|
||||
explode('|', $this->getParameter('app.locales')),
|
||||
$activityService->getEventCodes(),
|
||||
$this->getParameter('app.theme'),
|
||||
$this->getParameter('app.sensitive'),
|
||||
$this->getParameter('app.yggdrasil'),
|
||||
$this->getParameter('app.posters'),
|
||||
$this->getParameter('app.approved')
|
||||
);
|
||||
|
||||
// Add user join event
|
||||
$activityService->addEventUserAdd(
|
||||
$user->getId(),
|
||||
time()
|
||||
);
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
}
|
||||
0
src/Entity/.gitignore
vendored
Normal file
0
src/Entity/.gitignore
vendored
Normal file
160
src/Entity/Activity.php
Normal file
160
src/Entity/Activity.php
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\ActivityRepository;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: ActivityRepository::class)]
|
||||
class Activity
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $event = null;
|
||||
|
||||
// Event codes
|
||||
|
||||
/// User
|
||||
public const EVENT_USER_ADD = 1000;
|
||||
|
||||
public const EVENT_USER_APPROVE_ADD = 1200;
|
||||
public const EVENT_USER_APPROVE_DELETE = 1201;
|
||||
|
||||
public const EVENT_USER_MODERATOR_ADD = 1300;
|
||||
public const EVENT_USER_MODERATOR_DELETE = 1301;
|
||||
|
||||
public const EVENT_USER_STATUS_ADD = 1400;
|
||||
public const EVENT_USER_STATUS_DELETE = 1401;
|
||||
|
||||
public const EVENT_USER_STAR_ADD = 1500;
|
||||
public const EVENT_USER_STAR_DELETE = 1501;
|
||||
|
||||
/// Torrent
|
||||
public const EVENT_TORRENT_ADD = 2000;
|
||||
|
||||
public const EVENT_TORRENT_APPROVE_ADD = 1100;
|
||||
public const EVENT_TORRENT_APPROVE_DELETE = 1101;
|
||||
|
||||
public const EVENT_TORRENT_LOCALES_ADD = 2200;
|
||||
public const EVENT_TORRENT_LOCALES_DELETE = 2201;
|
||||
public const EVENT_TORRENT_LOCALES_APPROVE_ADD = 2210;
|
||||
public const EVENT_TORRENT_LOCALES_APPROVE_DELETE = 2211;
|
||||
|
||||
public const EVENT_TORRENT_SENSITIVE_ADD = 2300;
|
||||
public const EVENT_TORRENT_SENSITIVE_DELETE = 2301;
|
||||
public const EVENT_TORRENT_SENSITIVE_APPROVE_ADD = 2310;
|
||||
public const EVENT_TORRENT_SENSITIVE_APPROVE_DELETE = 2311;
|
||||
|
||||
public const EVENT_TORRENT_STAR_ADD = 2400;
|
||||
public const EVENT_TORRENT_STAR_DELETE = 2401;
|
||||
|
||||
public const EVENT_TORRENT_DOWNLOAD_FILE_ADD = 2500;
|
||||
|
||||
public const EVENT_TORRENT_DOWNLOAD_MAGNET_ADD = 2600;
|
||||
|
||||
public const EVENT_TORRENT_WANTED_ADD = 2700;
|
||||
|
||||
public const EVENT_TORRENT_STATUS_ADD = 1800;
|
||||
public const EVENT_TORRENT_STATUS_DELETE = 1801;
|
||||
|
||||
public const EVENT_TORRENT_POSTER_ADD = 2800;
|
||||
public const EVENT_TORRENT_POSTER_DELETE = 2801;
|
||||
public const EVENT_TORRENT_POSTER_APPROVE_ADD = 2810;
|
||||
public const EVENT_TORRENT_POSTER_APPROVE_DELETE = 2811;
|
||||
|
||||
public const EVENT_TORRENT_CATEGORIES_ADD = 2900;
|
||||
public const EVENT_TORRENT_CATEGORIES_DELETE = 2901;
|
||||
public const EVENT_TORRENT_CATEGORIES_APPROVE_ADD = 2910;
|
||||
public const EVENT_TORRENT_CATEGORIES_APPROVE_DELETE = 2911;
|
||||
|
||||
// ...
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $userId = null;
|
||||
|
||||
#[ORM\Column(nullable: true)]
|
||||
private ?int $torrentId = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $added = null;
|
||||
|
||||
#[ORM\Column(type: Types::ARRAY)]
|
||||
private array $data = [];
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setId(string $id): static
|
||||
{
|
||||
$this->id = $id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEvent(): ?int
|
||||
{
|
||||
return $this->event;
|
||||
}
|
||||
|
||||
public function setEvent(int $event): static
|
||||
{
|
||||
$this->event = $event;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUserId(): ?int
|
||||
{
|
||||
return $this->userId;
|
||||
}
|
||||
|
||||
public function setUserId(?int $userId): static
|
||||
{
|
||||
$this->userId = $userId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTorrentId(): ?int
|
||||
{
|
||||
return $this->torrentId;
|
||||
}
|
||||
|
||||
public function setTorrentId(?int $torrentId): static
|
||||
{
|
||||
$this->torrentId = $torrentId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAdded(): ?int
|
||||
{
|
||||
return $this->added;
|
||||
}
|
||||
|
||||
public function setAdded(int $added): static
|
||||
{
|
||||
$this->added = $added;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getData(): array
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function setData(array $data): static
|
||||
{
|
||||
$this->data = $data;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
239
src/Entity/Torrent.php
Normal file
239
src/Entity/Torrent.php
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\TorrentRepository;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: TorrentRepository::class)]
|
||||
|
||||
class Torrent
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $userId = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $added = null;
|
||||
|
||||
#[ORM\Column(nullable: true)]
|
||||
private ?int $scraped = null;
|
||||
|
||||
#[ORM\Column(type: Types::SIMPLE_ARRAY)]
|
||||
private array $locales = [];
|
||||
|
||||
#[ORM\Column]
|
||||
private ?bool $sensitive = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?bool $approved = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?bool $status = null;
|
||||
|
||||
#[ORM\Column(length: 32)]
|
||||
private ?string $md5file = null;
|
||||
|
||||
#[ORM\Column(type: Types::SIMPLE_ARRAY, nullable: true)]
|
||||
private ?array $keywords = null;
|
||||
|
||||
#[ORM\Column(nullable: true)]
|
||||
private ?int $seeders = null;
|
||||
|
||||
#[ORM\Column(nullable: true)]
|
||||
private ?int $peers = null;
|
||||
|
||||
#[ORM\Column(nullable: true)]
|
||||
private ?int $leechers = null;
|
||||
|
||||
#[ORM\Column(nullable: true)]
|
||||
private ?int $torrentPosterId = null;
|
||||
|
||||
#[ORM\Column(type: Types::SIMPLE_ARRAY)]
|
||||
private ?array $categories = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setId(string $id): static
|
||||
{
|
||||
$this->id = $id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUserId(): ?int
|
||||
{
|
||||
return $this->userId;
|
||||
}
|
||||
|
||||
public function setUserId(int $userId): static
|
||||
{
|
||||
$this->userId = $userId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAdded(): ?int
|
||||
{
|
||||
return $this->added;
|
||||
}
|
||||
|
||||
public function setAdded(int $added): static
|
||||
{
|
||||
$this->added = $added;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getScraped(): ?int
|
||||
{
|
||||
return $this->scraped;
|
||||
}
|
||||
|
||||
public function setScraped(int $scraped): static
|
||||
{
|
||||
$this->scraped = $scraped;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMd5file(): ?string
|
||||
{
|
||||
return $this->md5file;
|
||||
}
|
||||
|
||||
public function setMd5file(string $md5file): static
|
||||
{
|
||||
$this->md5file = $md5file;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getKeywords(): ?array
|
||||
{
|
||||
return $this->keywords;
|
||||
}
|
||||
|
||||
public function setKeywords(?array $keywords): static
|
||||
{
|
||||
$this->keywords = $keywords;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLocales(): array
|
||||
{
|
||||
return $this->locales;
|
||||
}
|
||||
|
||||
public function setLocales(array $locales): static
|
||||
{
|
||||
$this->locales = $locales;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isSensitive(): ?bool
|
||||
{
|
||||
return $this->sensitive;
|
||||
}
|
||||
|
||||
public function setSensitive(bool $sensitive): static
|
||||
{
|
||||
$this->sensitive = $sensitive;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public function getSeeders(): ?int
|
||||
{
|
||||
return $this->seeders;
|
||||
}
|
||||
|
||||
public function setSeeders(?int $seeders): static
|
||||
{
|
||||
$this->seeders = $seeders;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPeers(): ?int
|
||||
{
|
||||
return $this->peers;
|
||||
}
|
||||
|
||||
public function setPeers(?int $peers): static
|
||||
{
|
||||
$this->peers = $peers;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLeechers(): ?int
|
||||
{
|
||||
return $this->leechers;
|
||||
}
|
||||
|
||||
public function setLeechers(?int $leechers): static
|
||||
{
|
||||
$this->leechers = $leechers;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTorrentPosterId(): ?int
|
||||
{
|
||||
return $this->torrentPosterId;
|
||||
}
|
||||
|
||||
public function setTorrentPosterId(?int $torrentPosterId): static
|
||||
{
|
||||
$this->torrentPosterId = $torrentPosterId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCategories(): ?array
|
||||
{
|
||||
return $this->categories;
|
||||
}
|
||||
|
||||
public function setCategories(?array $categories): static
|
||||
{
|
||||
$this->categories = $categories;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
103
src/Entity/TorrentCategories.php
Normal file
103
src/Entity/TorrentCategories.php
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\TorrentCategoriesRepository;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: TorrentCategoriesRepository::class)]
|
||||
class TorrentCategories
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $torrentId = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $userId = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $added = null;
|
||||
|
||||
#[ORM\Column(type: Types::SIMPLE_ARRAY)]
|
||||
private array $value = [];
|
||||
|
||||
#[ORM\Column]
|
||||
private ?bool $approved = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setId(string $id): static
|
||||
{
|
||||
$this->id = $id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTorrentId(): ?int
|
||||
{
|
||||
return $this->torrentId;
|
||||
}
|
||||
|
||||
public function setTorrentId(int $torrentId): static
|
||||
{
|
||||
$this->torrentId = $torrentId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUserId(): ?int
|
||||
{
|
||||
return $this->userId;
|
||||
}
|
||||
|
||||
public function setUserId(int $userId): static
|
||||
{
|
||||
$this->userId = $userId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAdded(): ?int
|
||||
{
|
||||
return $this->added;
|
||||
}
|
||||
|
||||
public function setAdded(int $added): static
|
||||
{
|
||||
$this->added = $added;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getValue(): array
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function setValue(array $value): static
|
||||
{
|
||||
$this->value = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isApproved(): ?bool
|
||||
{
|
||||
return $this->approved;
|
||||
}
|
||||
|
||||
public function setApproved(bool $approved): static
|
||||
{
|
||||
$this->approved = $approved;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
72
src/Entity/TorrentDownloadFile.php
Normal file
72
src/Entity/TorrentDownloadFile.php
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\TorrentDownloadFileRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: TorrentDownloadFileRepository::class)]
|
||||
class TorrentDownloadFile
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $torrentId = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $userId = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $added = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setId(string $id): static
|
||||
{
|
||||
$this->id = $id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTorrentId(): ?int
|
||||
{
|
||||
return $this->torrentId;
|
||||
}
|
||||
|
||||
public function setTorrentId(int $torrentId): static
|
||||
{
|
||||
$this->torrentId = $torrentId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUserId(): ?int
|
||||
{
|
||||
return $this->userId;
|
||||
}
|
||||
|
||||
public function setUserId(int $userId): static
|
||||
{
|
||||
$this->userId = $userId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAdded(): ?int
|
||||
{
|
||||
return $this->added;
|
||||
}
|
||||
|
||||
public function setAdded(int $added): static
|
||||
{
|
||||
$this->added = $added;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
72
src/Entity/TorrentDownloadMagnet.php
Normal file
72
src/Entity/TorrentDownloadMagnet.php
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\TorrentDownloadMagnetRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: TorrentDownloadMagnetRepository::class)]
|
||||
class TorrentDownloadMagnet
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $torrentId = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $userId = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $added = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setId(string $id): static
|
||||
{
|
||||
$this->id = $id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTorrentId(): ?int
|
||||
{
|
||||
return $this->torrentId;
|
||||
}
|
||||
|
||||
public function setTorrentId(int $torrentId): static
|
||||
{
|
||||
$this->torrentId = $torrentId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUserId(): ?int
|
||||
{
|
||||
return $this->userId;
|
||||
}
|
||||
|
||||
public function setUserId(int $userId): static
|
||||
{
|
||||
$this->userId = $userId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAdded(): ?int
|
||||
{
|
||||
return $this->added;
|
||||
}
|
||||
|
||||
public function setAdded(int $added): static
|
||||
{
|
||||
$this->added = $added;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
103
src/Entity/TorrentLocales.php
Normal file
103
src/Entity/TorrentLocales.php
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\TorrentLocalesRepository;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: TorrentLocalesRepository::class)]
|
||||
class TorrentLocales
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $torrentId = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $userId = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $added = null;
|
||||
|
||||
#[ORM\Column(type: Types::SIMPLE_ARRAY)]
|
||||
private array $value = [];
|
||||
|
||||
#[ORM\Column]
|
||||
private ?bool $approved = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setId(string $id): static
|
||||
{
|
||||
$this->id = $id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTorrentId(): ?int
|
||||
{
|
||||
return $this->torrentId;
|
||||
}
|
||||
|
||||
public function setTorrentId(int $torrentId): static
|
||||
{
|
||||
$this->torrentId = $torrentId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUserId(): ?int
|
||||
{
|
||||
return $this->userId;
|
||||
}
|
||||
|
||||
public function setUserId(int $userId): static
|
||||
{
|
||||
$this->userId = $userId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAdded(): ?int
|
||||
{
|
||||
return $this->added;
|
||||
}
|
||||
|
||||
public function setAdded(int $added): static
|
||||
{
|
||||
$this->added = $added;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getValue(): array
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function setValue(array $value): static
|
||||
{
|
||||
$this->value = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isApproved(): ?bool
|
||||
{
|
||||
return $this->approved;
|
||||
}
|
||||
|
||||
public function setApproved(bool $approved): static
|
||||
{
|
||||
$this->approved = $approved;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
117
src/Entity/TorrentPoster.php
Normal file
117
src/Entity/TorrentPoster.php
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\TorrentPosterRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: TorrentPosterRepository::class)]
|
||||
class TorrentPoster
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $torrentId = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $userId = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $added = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?bool $approved = null;
|
||||
|
||||
#[ORM\Column(length: 32)]
|
||||
private ?string $md5file = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $position = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setId(string $id): static
|
||||
{
|
||||
$this->id = $id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTorrentId(): ?int
|
||||
{
|
||||
return $this->torrentId;
|
||||
}
|
||||
|
||||
public function setTorrentId(int $torrentId): static
|
||||
{
|
||||
$this->torrentId = $torrentId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUserId(): ?int
|
||||
{
|
||||
return $this->userId;
|
||||
}
|
||||
|
||||
public function setUserId(int $userId): static
|
||||
{
|
||||
$this->userId = $userId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAdded(): ?int
|
||||
{
|
||||
return $this->added;
|
||||
}
|
||||
|
||||
public function setAdded(int $added): static
|
||||
{
|
||||
$this->added = $added;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isApproved(): ?bool
|
||||
{
|
||||
return $this->approved;
|
||||
}
|
||||
|
||||
public function setApproved(bool $approved): static
|
||||
{
|
||||
$this->approved = $approved;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMd5file(): ?string
|
||||
{
|
||||
return $this->md5file;
|
||||
}
|
||||
|
||||
public function setMd5file(string $md5file): static
|
||||
{
|
||||
$this->md5file = $md5file;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPosition(): ?string
|
||||
{
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
public function setPosition(string $position): static
|
||||
{
|
||||
$this->position = $position;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
103
src/Entity/TorrentSensitive.php
Normal file
103
src/Entity/TorrentSensitive.php
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\TorrentSensitiveRepository;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: TorrentSensitiveRepository::class)]
|
||||
class TorrentSensitive
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $torrentId = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $userId = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $added = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?bool $value = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?bool $approved = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setId(string $id): static
|
||||
{
|
||||
$this->id = $id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTorrentId(): ?int
|
||||
{
|
||||
return $this->torrentId;
|
||||
}
|
||||
|
||||
public function setTorrentId(int $torrentId): static
|
||||
{
|
||||
$this->torrentId = $torrentId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUserId(): ?int
|
||||
{
|
||||
return $this->userId;
|
||||
}
|
||||
|
||||
public function setUserId(int $userId): static
|
||||
{
|
||||
$this->userId = $userId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAdded(): ?int
|
||||
{
|
||||
return $this->added;
|
||||
}
|
||||
|
||||
public function setAdded(int $added): static
|
||||
{
|
||||
$this->added = $added;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isValue(): ?bool
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function setValue(bool $value): static
|
||||
{
|
||||
$this->value = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isApproved(): ?bool
|
||||
{
|
||||
return $this->approved;
|
||||
}
|
||||
|
||||
public function setApproved(bool $approved): static
|
||||
{
|
||||
$this->approved = $approved;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
65
src/Entity/TorrentStar.php
Normal file
65
src/Entity/TorrentStar.php
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\TorrentStarRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: TorrentStarRepository::class)]
|
||||
class TorrentStar
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $torrentId = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $userId = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $added = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getTorrentId(): ?int
|
||||
{
|
||||
return $this->torrentId;
|
||||
}
|
||||
|
||||
public function setTorrentId(int $torrentId): static
|
||||
{
|
||||
$this->torrentId = $torrentId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUserId(): ?int
|
||||
{
|
||||
return $this->userId;
|
||||
}
|
||||
|
||||
public function setUserId(int $userId): static
|
||||
{
|
||||
$this->userId = $userId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAdded(): ?int
|
||||
{
|
||||
return $this->added;
|
||||
}
|
||||
|
||||
public function setAdded(int $added): static
|
||||
{
|
||||
$this->added = $added;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
223
src/Entity/User.php
Normal file
223
src/Entity/User.php
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\UserRepository;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
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 ?bool $moderator = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?bool $approved = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?bool $status = null;
|
||||
|
||||
#[ORM\Column(length: 2)]
|
||||
private ?string $locale = null;
|
||||
|
||||
#[ORM\Column(type: Types::SIMPLE_ARRAY)]
|
||||
private array $locales = [];
|
||||
|
||||
#[ORM\Column(type: Types::SIMPLE_ARRAY)]
|
||||
private array $events = [];
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $theme = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?bool $sensitive = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?bool $yggdrasil = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?bool $posters = null;
|
||||
|
||||
#[ORM\Column(type: Types::SIMPLE_ARRAY)]
|
||||
private ?array $categories = 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 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;
|
||||
}
|
||||
|
||||
public function getLocale(): ?string
|
||||
{
|
||||
return $this->locale;
|
||||
}
|
||||
|
||||
public function setLocale(string $locale): static
|
||||
{
|
||||
$this->locale = $locale;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLocales(): array
|
||||
{
|
||||
return $this->locales;
|
||||
}
|
||||
|
||||
public function setLocales(array $locales): static
|
||||
{
|
||||
$this->locales = $locales;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEvents(): array
|
||||
{
|
||||
return $this->events;
|
||||
}
|
||||
|
||||
public function setEvents(array $events): static
|
||||
{
|
||||
$this->events = $events;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTheme(): ?string
|
||||
{
|
||||
return $this->theme;
|
||||
}
|
||||
|
||||
public function setTheme(string $theme): static
|
||||
{
|
||||
$this->theme = $theme;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isSensitive(): ?bool
|
||||
{
|
||||
return $this->sensitive;
|
||||
}
|
||||
|
||||
public function setSensitive(bool $sensitive): static
|
||||
{
|
||||
$this->sensitive = $sensitive;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isYggdrasil(): ?bool
|
||||
{
|
||||
return $this->yggdrasil;
|
||||
}
|
||||
|
||||
public function setYggdrasil(bool $yggdrasil): static
|
||||
{
|
||||
$this->yggdrasil = $yggdrasil;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isPosters(): ?bool
|
||||
{
|
||||
return $this->posters;
|
||||
}
|
||||
|
||||
public function setPosters(bool $posters): static
|
||||
{
|
||||
$this->posters = $posters;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCategories(): ?array
|
||||
{
|
||||
return $this->categories;
|
||||
}
|
||||
|
||||
public function setCategories(?array $categories): static
|
||||
{
|
||||
$this->categories = $categories;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
65
src/Entity/UserStar.php
Normal file
65
src/Entity/UserStar.php
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\UserStarRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: UserStarRepository::class)]
|
||||
class UserStar
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $userId = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $userIdTarget = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $added = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getUserId(): ?int
|
||||
{
|
||||
return $this->userId;
|
||||
}
|
||||
|
||||
public function setUserId(int $userId): static
|
||||
{
|
||||
$this->userId = $userId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUserIdTarget(): ?int
|
||||
{
|
||||
return $this->userIdTarget;
|
||||
}
|
||||
|
||||
public function setUserIdTarget(int $userIdTarget): static
|
||||
{
|
||||
$this->userIdTarget = $userIdTarget;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAdded(): ?int
|
||||
{
|
||||
return $this->added;
|
||||
}
|
||||
|
||||
public function setAdded(int $added): static
|
||||
{
|
||||
$this->added = $added;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
11
src/Kernel.php
Normal file
11
src/Kernel.php
Normal file
|
|
@ -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
src/Repository/.gitignore
vendored
Normal file
0
src/Repository/.gitignore
vendored
Normal file
68
src/Repository/ActivityRepository.php
Normal file
68
src/Repository/ActivityRepository.php
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\Activity;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Activity>
|
||||
*
|
||||
* @method Activity|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method Activity|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method Activity[] findAll()
|
||||
* @method Activity[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class ActivityRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, Activity::class);
|
||||
}
|
||||
|
||||
public function findActivitiesTotal(
|
||||
array $whitelist
|
||||
): int
|
||||
{
|
||||
return $this->createQueryBuilder('a')
|
||||
->select('count(a.id)')
|
||||
->where('a.event IN (:event)')
|
||||
->setParameter(':event', $whitelist)
|
||||
->getQuery()
|
||||
->getSingleScalarResult()
|
||||
;
|
||||
}
|
||||
|
||||
public function findActivitiesTotalByUserId(
|
||||
int $userId,
|
||||
array $whitelist
|
||||
): int
|
||||
{
|
||||
return $this->createQueryBuilder('a')
|
||||
->select('count(a.id)')
|
||||
->where('a.userId = :userId')
|
||||
->andWhere('a.event IN (:event)')
|
||||
->setParameter(':userId', $userId)
|
||||
->setParameter(':event', $whitelist)
|
||||
->getQuery()
|
||||
->getSingleScalarResult()
|
||||
;
|
||||
}
|
||||
|
||||
public function findActivitiesTotalByTorrentId(
|
||||
int $torrentId,
|
||||
array $whitelist
|
||||
): int
|
||||
{
|
||||
return $this->createQueryBuilder('a')
|
||||
->select('count(a.id)')
|
||||
->where('a.torrentId = :torrentId')
|
||||
->andWhere('a.event IN (:event)')
|
||||
->setParameter(':torrentId', $torrentId)
|
||||
->setParameter(':event', $whitelist)
|
||||
->getQuery()
|
||||
->getSingleScalarResult()
|
||||
;
|
||||
}
|
||||
}
|
||||
23
src/Repository/TorrentCategoriesRepository.php
Normal file
23
src/Repository/TorrentCategoriesRepository.php
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\TorrentCategories;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<TorrentCategories>
|
||||
*
|
||||
* @method TorrentCategories|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method TorrentCategories|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method TorrentCategories[] findAll()
|
||||
* @method TorrentCategories[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class TorrentCategoriesRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, TorrentCategories::class);
|
||||
}
|
||||
}
|
||||
36
src/Repository/TorrentDownloadFileRepository.php
Normal file
36
src/Repository/TorrentDownloadFileRepository.php
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\TorrentDownloadFile;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<TorrentDownloadFile>
|
||||
*
|
||||
* @method TorrentDownloadFile|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method TorrentDownloadFile|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method TorrentDownloadFile[] findAll()
|
||||
* @method TorrentDownloadFile[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class TorrentDownloadFileRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, TorrentDownloadFile::class);
|
||||
}
|
||||
|
||||
public function findTorrentDownloadFilesTotalByTorrentId(
|
||||
int $torrentId
|
||||
): int
|
||||
{
|
||||
return $this->createQueryBuilder('tdf')
|
||||
->select('count(tdf.id)')
|
||||
->where('tdf.torrentId = :torrentId')
|
||||
->setParameter('torrentId', $torrentId)
|
||||
->getQuery()
|
||||
->getSingleScalarResult()
|
||||
;
|
||||
}
|
||||
}
|
||||
36
src/Repository/TorrentDownloadMagnetRepository.php
Normal file
36
src/Repository/TorrentDownloadMagnetRepository.php
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\TorrentDownloadMagnet;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<TorrentDownloadMagnet>
|
||||
*
|
||||
* @method TorrentDownloadMagnet|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method TorrentDownloadMagnet|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method TorrentDownloadMagnet[] findAll()
|
||||
* @method TorrentDownloadMagnet[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class TorrentDownloadMagnetRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, TorrentDownloadMagnet::class);
|
||||
}
|
||||
|
||||
public function findTorrentDownloadMagnetsTotalByTorrentId(
|
||||
int $torrentId
|
||||
): int
|
||||
{
|
||||
return $this->createQueryBuilder('tdm')
|
||||
->select('count(tdm.id)')
|
||||
->where('tdm.torrentId = :torrentId')
|
||||
->setParameter('torrentId', $torrentId)
|
||||
->getQuery()
|
||||
->getSingleScalarResult()
|
||||
;
|
||||
}
|
||||
}
|
||||
23
src/Repository/TorrentLocalesRepository.php
Normal file
23
src/Repository/TorrentLocalesRepository.php
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\TorrentLocales;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<TorrentLocales>
|
||||
*
|
||||
* @method TorrentLocales|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method TorrentLocales|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method TorrentLocales[] findAll()
|
||||
* @method TorrentLocales[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class TorrentLocalesRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, TorrentLocales::class);
|
||||
}
|
||||
}
|
||||
23
src/Repository/TorrentPosterRepository.php
Normal file
23
src/Repository/TorrentPosterRepository.php
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\TorrentPoster;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<TorrentPoster>
|
||||
*
|
||||
* @method TorrentPoster|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method TorrentPoster|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method TorrentPoster[] findAll()
|
||||
* @method TorrentPoster[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class TorrentPosterRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, TorrentPoster::class);
|
||||
}
|
||||
}
|
||||
241
src/Repository/TorrentRepository.php
Normal file
241
src/Repository/TorrentRepository.php
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\Torrent;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Torrent>
|
||||
*
|
||||
* @method Torrent|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method Torrent|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method Torrent[] findAll()
|
||||
* @method Torrent[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class TorrentRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, Torrent::class);
|
||||
}
|
||||
|
||||
public function findTorrentsTotal(
|
||||
int $userId,
|
||||
array $keywords,
|
||||
?array $locales,
|
||||
?array $categories,
|
||||
?bool $sensitive = null,
|
||||
?bool $approved = null,
|
||||
?bool $status = null,
|
||||
int $limit = 10,
|
||||
int $offset = 0
|
||||
): int
|
||||
{
|
||||
return $this->getTorrentsQueryByFilter(
|
||||
$userId,
|
||||
$keywords,
|
||||
$locales,
|
||||
$categories,
|
||||
$sensitive,
|
||||
$approved,
|
||||
$status,
|
||||
)->select('count(t.id)')
|
||||
->getQuery()
|
||||
->getSingleScalarResult();
|
||||
}
|
||||
|
||||
public function findTorrents(
|
||||
int $userId,
|
||||
array $keywords,
|
||||
?array $locales,
|
||||
?array $categories,
|
||||
?bool $sensitive = null,
|
||||
?bool $approved = null,
|
||||
?bool $status = null,
|
||||
int $limit = 10,
|
||||
int $offset = 0
|
||||
): array
|
||||
{
|
||||
return $this->getTorrentsQueryByFilter(
|
||||
$userId,
|
||||
$keywords,
|
||||
$locales,
|
||||
$categories,
|
||||
$sensitive,
|
||||
$approved,
|
||||
$status,
|
||||
)->setMaxResults($limit)
|
||||
->setFirstResult($offset)
|
||||
->orderBy('t.id', 'DESC') // same as t.added
|
||||
->getQuery()
|
||||
->getResult();
|
||||
}
|
||||
|
||||
private function getTorrentsQueryByFilter(
|
||||
int $userId,
|
||||
?array $keywords,
|
||||
?array $locales,
|
||||
?array $categories,
|
||||
?bool $sensitive = null,
|
||||
?bool $approved = null,
|
||||
?bool $status = null
|
||||
): \Doctrine\ORM\QueryBuilder
|
||||
{
|
||||
$query = $this->createQueryBuilder('t');
|
||||
|
||||
if (is_array($keywords))
|
||||
{
|
||||
foreach ($keywords as $i => $keyword)
|
||||
{
|
||||
// Make query to the index case insensitive
|
||||
$keyword = mb_strtolower($keyword);
|
||||
|
||||
// Init OR condition for each word form
|
||||
$orKeywords = $query->expr()->orX();
|
||||
|
||||
$orKeywords->add("t.keywords LIKE :keyword{$i}");
|
||||
$query->setParameter(":keyword{$i}", "%{$keyword}%");
|
||||
|
||||
// Generate word forms for each transliteration locale #33
|
||||
foreach ($this->generateWordForms($keyword) as $j => $wordForm)
|
||||
{
|
||||
$orKeywords->add("t.keywords LIKE :keyword{$i}{$j}");
|
||||
$query->setParameter(":keyword{$i}{$j}", "%{$wordForm}%");
|
||||
}
|
||||
|
||||
// Append AND condition
|
||||
$query->andWhere($orKeywords);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($locales))
|
||||
{
|
||||
$orLocales = $query->expr()->orX();
|
||||
|
||||
foreach ($locales as $i => $locale)
|
||||
{
|
||||
$orLocales->add("t.locales LIKE :locale{$i}");
|
||||
$orLocales->add("t.userId = :userId");
|
||||
|
||||
$query->setParameter(":locale{$i}", "%{$locale}%");
|
||||
$query->setParameter('userId', $userId);
|
||||
}
|
||||
|
||||
$query->andWhere($orLocales);
|
||||
}
|
||||
|
||||
if (is_array($categories))
|
||||
{
|
||||
$orCategories = $query->expr()->orX();
|
||||
|
||||
foreach ($categories as $i => $category)
|
||||
{
|
||||
$orCategories->add("t.categories LIKE :category{$i}");
|
||||
$orCategories->add("t.userId = :userId");
|
||||
|
||||
$query->setParameter(":category{$i}", "%{$category}%");
|
||||
$query->setParameter('userId', $userId);
|
||||
}
|
||||
|
||||
$query->andWhere($orCategories);
|
||||
}
|
||||
|
||||
if (is_bool($sensitive))
|
||||
{
|
||||
$orSensitive = $query->expr()->orX();
|
||||
|
||||
$orSensitive->add("t.sensitive = :sensitive");
|
||||
$orSensitive->add("t.userId = :userId");
|
||||
|
||||
$query->setParameter('sensitive', $sensitive);
|
||||
$query->setParameter('userId', $userId);
|
||||
|
||||
$query->andWhere($orSensitive);
|
||||
}
|
||||
|
||||
if (is_bool($approved))
|
||||
{
|
||||
$orApproved = $query->expr()->orX();
|
||||
|
||||
$orApproved->add("t.approved = :approved");
|
||||
$orApproved->add("t.userId = :userId");
|
||||
|
||||
$query->setParameter('approved', $approved);
|
||||
$query->setParameter('userId', $userId);
|
||||
|
||||
$query->andWhere($orApproved);
|
||||
}
|
||||
|
||||
if (is_bool($status))
|
||||
{
|
||||
$orStatus = $query->expr()->orX();
|
||||
|
||||
$orStatus->add("t.status = :status");
|
||||
$orStatus->add("t.userId = :userId");
|
||||
|
||||
$query->setParameter('status', $status);
|
||||
$query->setParameter('userId', $userId);
|
||||
|
||||
$query->andWhere($orStatus);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
// Word forms generator to improve search results
|
||||
// e.g. transliteration rules for latin filenames
|
||||
private function generateWordForms(
|
||||
string $keyword,
|
||||
// #33 supported locales:
|
||||
// https://github.com/ashtokalo/php-translit
|
||||
array $transliteration = [
|
||||
'be',
|
||||
'bg',
|
||||
'el',
|
||||
'hy',
|
||||
'kk',
|
||||
'mk',
|
||||
'ru',
|
||||
'ka',
|
||||
'uk'
|
||||
],
|
||||
// Additional char forms
|
||||
array $charForms =
|
||||
[
|
||||
'c' => 'k',
|
||||
'k' => 'c',
|
||||
]
|
||||
): array
|
||||
{
|
||||
$wordForms = [];
|
||||
|
||||
// Apply transliteration
|
||||
foreach ($transliteration as $locale)
|
||||
{
|
||||
$wordForms[] = \ashtokalo\translit\Translit::object()->convert(
|
||||
$keyword,
|
||||
$locale
|
||||
);
|
||||
}
|
||||
|
||||
// Apply char forms
|
||||
foreach ($wordForms as $wordForm)
|
||||
{
|
||||
foreach ($charForms as $from => $to)
|
||||
{
|
||||
$wordForms[] = str_replace(
|
||||
$from,
|
||||
$to,
|
||||
$wordForm
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove duplicates
|
||||
return array_unique(
|
||||
$wordForms
|
||||
);
|
||||
}
|
||||
}
|
||||
23
src/Repository/TorrentSensitiveRepository.php
Normal file
23
src/Repository/TorrentSensitiveRepository.php
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\TorrentSensitive;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<TorrentSensitive>
|
||||
*
|
||||
* @method TorrentSensitive|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method TorrentSensitive|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method TorrentSensitive[] findAll()
|
||||
* @method TorrentSensitive[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class TorrentSensitiveRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, TorrentSensitive::class);
|
||||
}
|
||||
}
|
||||
36
src/Repository/TorrentStarRepository.php
Normal file
36
src/Repository/TorrentStarRepository.php
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\TorrentStar;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<TorrentStar>
|
||||
*
|
||||
* @method TorrentStar|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method TorrentStar|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method TorrentStar[] findAll()
|
||||
* @method TorrentStar[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class TorrentStarRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, TorrentStar::class);
|
||||
}
|
||||
|
||||
public function findTorrentStarsTotalByTorrentId(
|
||||
int $torrentId
|
||||
): int
|
||||
{
|
||||
return $this->createQueryBuilder('ts')
|
||||
->select('count(ts.id)')
|
||||
->where('ts.torrentId = :torrentId')
|
||||
->setParameter('torrentId', $torrentId)
|
||||
->getQuery()
|
||||
->getSingleScalarResult()
|
||||
;
|
||||
}
|
||||
}
|
||||
23
src/Repository/UserRepository.php
Normal file
23
src/Repository/UserRepository.php
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
||||
36
src/Repository/UserStarRepository.php
Normal file
36
src/Repository/UserStarRepository.php
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\UserStar;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<UserStar>
|
||||
*
|
||||
* @method UserStar|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method UserStar|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method UserStar[] findAll()
|
||||
* @method UserStar[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class UserStarRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, UserStar::class);
|
||||
}
|
||||
|
||||
public function findUserStarsTotalByUserIdTarget(
|
||||
int $userIdTarget
|
||||
): int
|
||||
{
|
||||
return $this->createQueryBuilder('us')
|
||||
->select('count(us.userId)')
|
||||
->where('us.userIdTarget = :userIdTarget')
|
||||
->setParameter('userIdTarget', $userIdTarget)
|
||||
->getQuery()
|
||||
->getSingleScalarResult()
|
||||
;
|
||||
}
|
||||
}
|
||||
1806
src/Service/ActivityService.php
Normal file
1806
src/Service/ActivityService.php
Normal file
File diff suppressed because it is too large
Load diff
1548
src/Service/TorrentService.php
Normal file
1548
src/Service/TorrentService.php
Normal file
File diff suppressed because it is too large
Load diff
256
src/Service/UserService.php
Normal file
256
src/Service/UserService.php
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
<?php
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\Entity\User;
|
||||
use App\Entity\UserStar;
|
||||
use App\Repository\UserRepository;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||
|
||||
class UserService
|
||||
{
|
||||
private EntityManagerInterface $entityManagerInterface;
|
||||
private ParameterBagInterface $parameterBagInterface;
|
||||
|
||||
public function __construct(
|
||||
EntityManagerInterface $entityManagerInterface,
|
||||
ParameterBagInterface $parameterBagInterface
|
||||
)
|
||||
{
|
||||
$this->entityManagerInterface = $entityManagerInterface;
|
||||
$this->parameterBagInterface = $parameterBagInterface;
|
||||
}
|
||||
|
||||
public function addUser(
|
||||
string $address,
|
||||
string $added,
|
||||
string $locale,
|
||||
array $locales,
|
||||
array $events,
|
||||
string $theme,
|
||||
bool $sensitive = true,
|
||||
bool $yggdrasil = true,
|
||||
bool $posters = true,
|
||||
bool $approved = false,
|
||||
bool $moderator = false,
|
||||
bool $status = true
|
||||
): ?User
|
||||
{
|
||||
// Create new user
|
||||
$user = new User();
|
||||
|
||||
$user->setAddress(
|
||||
$address
|
||||
);
|
||||
|
||||
$user->setAdded(
|
||||
$added
|
||||
);
|
||||
|
||||
$user->setApproved(
|
||||
$approved
|
||||
);
|
||||
|
||||
$user->setModerator(
|
||||
$moderator
|
||||
);
|
||||
|
||||
$user->setStatus(
|
||||
$status
|
||||
);
|
||||
|
||||
$user->setLocale(
|
||||
$locale
|
||||
);
|
||||
|
||||
$user->setLocales(
|
||||
$locales
|
||||
);
|
||||
|
||||
$user->setTheme(
|
||||
$theme
|
||||
);
|
||||
|
||||
$user->setEvents(
|
||||
$events
|
||||
);
|
||||
|
||||
$user->setSensitive(
|
||||
$sensitive
|
||||
);
|
||||
|
||||
$user->setYggdrasil(
|
||||
$yggdrasil
|
||||
);
|
||||
|
||||
$user->setPosters(
|
||||
$posters
|
||||
);
|
||||
|
||||
$this->entityManagerInterface->persist($user);
|
||||
$this->entityManagerInterface->flush();
|
||||
|
||||
// Set initial user as approved & moderator
|
||||
if (1 === $user->getId())
|
||||
{
|
||||
$user->setApproved(true);
|
||||
$user->setModerator(true);
|
||||
$user->setSensitive(false);
|
||||
|
||||
$this->entityManagerInterface->persist($user);
|
||||
$this->entityManagerInterface->flush();
|
||||
}
|
||||
|
||||
// Return user data
|
||||
return $user;
|
||||
}
|
||||
|
||||
public function getUser(int $userId): ?User
|
||||
{
|
||||
return $this->entityManagerInterface
|
||||
->getRepository(User::class)
|
||||
->find($userId);
|
||||
}
|
||||
|
||||
public function findUserByAddress(string $address): ?User
|
||||
{
|
||||
return $this->entityManagerInterface
|
||||
->getRepository(User::class)
|
||||
->findOneBy(
|
||||
[
|
||||
'address' => $address
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function identicon(
|
||||
mixed $value,
|
||||
int $size = 16,
|
||||
array $style =
|
||||
[
|
||||
'backgroundColor' => 'rgba(255, 255, 255, 0)',
|
||||
'padding' => 0
|
||||
],
|
||||
string $format = 'webp'
|
||||
): string
|
||||
{
|
||||
$identicon = new \Jdenticon\Identicon();
|
||||
|
||||
$identicon->setValue($value);
|
||||
$identicon->setSize($size);
|
||||
$identicon->setStyle($style);
|
||||
|
||||
return $identicon->getImageDataUri($format);
|
||||
}
|
||||
|
||||
public function save(User $user) : void // @TODO delete
|
||||
{
|
||||
$this->entityManagerInterface->persist($user);
|
||||
$this->entityManagerInterface->flush();
|
||||
}
|
||||
|
||||
// User star
|
||||
public function findUserStar(
|
||||
int $userId,
|
||||
int $userIdTarget
|
||||
): ?UserStar
|
||||
{
|
||||
return $this->entityManagerInterface
|
||||
->getRepository(UserStar::class)
|
||||
->findOneBy(
|
||||
[
|
||||
'userId' => $userId,
|
||||
'userIdTarget' => $userIdTarget
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function findUserStarsTotalByUserIdTarget(int $torrentId): int
|
||||
{
|
||||
return $this->entityManagerInterface
|
||||
->getRepository(UserStar::class)
|
||||
->findUserStarsTotalByUserIdTarget($torrentId);
|
||||
}
|
||||
|
||||
public function toggleUserStar(
|
||||
int $userId,
|
||||
int $userIdTarget,
|
||||
int $added
|
||||
): bool
|
||||
{
|
||||
if ($userStar = $this->findUserStar($userId, $userIdTarget))
|
||||
{
|
||||
$this->entityManagerInterface->remove($userStar);
|
||||
$this->entityManagerInterface->flush();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
$userStar = new UserStar();
|
||||
|
||||
$userStar->setUserId($userId);
|
||||
$userStar->setUserIdTarget($userIdTarget);
|
||||
$userStar->setAdded($added);
|
||||
|
||||
$this->entityManagerInterface->persist($userStar);
|
||||
$this->entityManagerInterface->flush();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public function toggleUserModerator(
|
||||
int $userId
|
||||
): ?User
|
||||
{
|
||||
if ($user = $this->getUser($userId))
|
||||
{
|
||||
$user->setModerator(
|
||||
!$user->isModerator()
|
||||
);
|
||||
|
||||
$this->entityManagerInterface->persist($user);
|
||||
$this->entityManagerInterface->flush();
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
public function toggleUserStatus(
|
||||
int $userId
|
||||
): ?User
|
||||
{
|
||||
if ($user = $this->getUser($userId))
|
||||
{
|
||||
$user->setStatus(
|
||||
!$user->isStatus()
|
||||
);
|
||||
|
||||
$this->entityManagerInterface->persist($user);
|
||||
$this->entityManagerInterface->flush();
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
public function toggleUserApproved(
|
||||
int $userId
|
||||
): ?User
|
||||
{
|
||||
if ($user = $this->getUser($userId))
|
||||
{
|
||||
$user->setApproved(
|
||||
!$user->isApproved()
|
||||
);
|
||||
|
||||
$this->entityManagerInterface->persist($user);
|
||||
$this->entityManagerInterface->flush();
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
}
|
||||
192
src/Twig/AppExtension.php
Normal file
192
src/Twig/AppExtension.php
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
<?php
|
||||
|
||||
namespace App\Twig;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFilter;
|
||||
|
||||
class AppExtension extends AbstractExtension
|
||||
{
|
||||
protected ContainerInterface $container;
|
||||
protected TranslatorInterface $translator;
|
||||
|
||||
public function __construct(
|
||||
ContainerInterface $container,
|
||||
TranslatorInterface $translator
|
||||
)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
public function getFilters()
|
||||
{
|
||||
return
|
||||
[
|
||||
new TwigFilter(
|
||||
'format_bytes',
|
||||
[
|
||||
$this,
|
||||
'formatBytes'
|
||||
]
|
||||
),
|
||||
new TwigFilter(
|
||||
'format_ago',
|
||||
[
|
||||
$this,
|
||||
'formatAgo'
|
||||
]
|
||||
),
|
||||
new TwigFilter(
|
||||
'url_to_markdown',
|
||||
[
|
||||
$this,
|
||||
'urlToMarkdown'
|
||||
]
|
||||
),
|
||||
new TwigFilter(
|
||||
'trans_category',
|
||||
[
|
||||
$this,
|
||||
'transCategory'
|
||||
]
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
public function formatBytes(
|
||||
int $bytes,
|
||||
int $precision = 2
|
||||
): string
|
||||
{
|
||||
$size = [
|
||||
$this->translator->trans('B'),
|
||||
$this->translator->trans('Kb'),
|
||||
$this->translator->trans('Mb'),
|
||||
$this->translator->trans('Gb'),
|
||||
$this->translator->trans('Tb'),
|
||||
$this->translator->trans('Pb'),
|
||||
$this->translator->trans('Eb'),
|
||||
$this->translator->trans('Zb'),
|
||||
$this->translator->trans('Yb')
|
||||
];
|
||||
|
||||
$factor = floor((strlen($bytes) - 1) / 3);
|
||||
|
||||
return sprintf("%.{$precision}f", $bytes / pow(1024, $factor)) . ' ' . @$size[$factor];
|
||||
}
|
||||
|
||||
public function formatAgo(
|
||||
int $time,
|
||||
): string
|
||||
{
|
||||
$diff = time() - $time;
|
||||
|
||||
if ($diff < 1)
|
||||
{
|
||||
return $this->translator->trans('now');
|
||||
}
|
||||
|
||||
$values =
|
||||
[
|
||||
365 * 24 * 60 * 60 =>
|
||||
[
|
||||
$this->translator->trans('year ago'),
|
||||
$this->translator->trans('years ago'),
|
||||
$this->translator->trans(' years ago')
|
||||
],
|
||||
30 * 24 * 60 * 60 =>
|
||||
[
|
||||
$this->translator->trans('month ago'),
|
||||
$this->translator->trans('months ago'),
|
||||
$this->translator->trans(' months ago')
|
||||
],
|
||||
24 * 60 * 60 =>
|
||||
[
|
||||
$this->translator->trans('day ago'),
|
||||
$this->translator->trans('days ago'),
|
||||
$this->translator->trans(' days ago')
|
||||
],
|
||||
60 * 60 =>
|
||||
[
|
||||
$this->translator->trans('hour ago'),
|
||||
$this->translator->trans('hours ago'),
|
||||
$this->translator->trans(' hours ago')
|
||||
],
|
||||
60 =>
|
||||
[
|
||||
$this->translator->trans('minute ago'),
|
||||
$this->translator->trans('minutes ago'),
|
||||
$this->translator->trans(' minutes ago')
|
||||
],
|
||||
1 =>
|
||||
[
|
||||
$this->translator->trans('second ago'),
|
||||
$this->translator->trans('seconds ago'),
|
||||
$this->translator->trans(' seconds ago')
|
||||
]
|
||||
];
|
||||
|
||||
foreach ($values as $key => $value)
|
||||
{
|
||||
$result = $diff / $key;
|
||||
|
||||
if ($result >= 1)
|
||||
{
|
||||
$round = round($result);
|
||||
|
||||
return sprintf(
|
||||
'%s %s',
|
||||
$round,
|
||||
$this->plural(
|
||||
$round,
|
||||
$value
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function urlToMarkdown(
|
||||
string $text
|
||||
): string
|
||||
{
|
||||
return preg_replace(
|
||||
'~(https?://(?:www\.)?[^\(\s\)]+)~i',
|
||||
'[$1]($1)',
|
||||
$text
|
||||
);
|
||||
}
|
||||
|
||||
public function transCategory(
|
||||
string $name
|
||||
): string
|
||||
{
|
||||
switch ($name)
|
||||
{
|
||||
case 'movie': return $this->translator->trans('movie');
|
||||
case 'series': return $this->translator->trans('series');
|
||||
case 'tv': return $this->translator->trans('tv');
|
||||
case 'animation': return $this->translator->trans('animation');
|
||||
case 'music': return $this->translator->trans('music');
|
||||
case 'game': return $this->translator->trans('game');
|
||||
case 'audiobook': return $this->translator->trans('audiobook');
|
||||
case 'podcast': return $this->translator->trans('podcast');
|
||||
case 'book': return $this->translator->trans('book');
|
||||
case 'archive': return $this->translator->trans('archive');
|
||||
case 'picture': return $this->translator->trans('picture');
|
||||
case 'software': return $this->translator->trans('software');
|
||||
case 'other': return $this->translator->trans('other');
|
||||
default: return $name;
|
||||
}
|
||||
}
|
||||
|
||||
private function plural(int $number, array $texts)
|
||||
{
|
||||
$cases = [2, 0, 1, 1, 1, 2];
|
||||
|
||||
return $texts[(($number % 100) > 4 && ($number % 100) < 20) ? 2 : $cases[min($number % 10, 5)]];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
<?php
|
||||
|
||||
// PHP
|
||||
declare(strict_types=1);
|
||||
|
||||
// Init environment
|
||||
if (!file_exists(__DIR__ . '/.env'))
|
||||
{
|
||||
if ($handle = fopen(__DIR__ . '/.env', 'w+'))
|
||||
{
|
||||
fwrite($handle, 'default');
|
||||
fclose($handle);
|
||||
|
||||
chmod(__DIR__ . '/.env', 0770);
|
||||
}
|
||||
|
||||
else exit (_('Could not init environment file. Please check permissions.'));
|
||||
}
|
||||
|
||||
define('PHP_ENV', file_get_contents(__DIR__ . '/.env'));
|
||||
|
||||
// Init config
|
||||
if (!file_exists(__DIR__ . '/env.' . PHP_ENV . '.php'))
|
||||
{
|
||||
if (copy(__DIR__ . '/../../example/environment/env.example.php',
|
||||
__DIR__ . '/env.' . PHP_ENV . '.php'))
|
||||
{
|
||||
chmod(__DIR__ . '/env.' . PHP_ENV . '.php', 0770);
|
||||
}
|
||||
|
||||
else exit (_('Could not init configuration file. Please check permissions.'));
|
||||
}
|
||||
|
||||
// Load environment
|
||||
require_once __DIR__ . '/env.' . PHP_ENV . '.php';
|
||||
|
||||
// Local internal dependencies
|
||||
require_once __DIR__ . '/../library/database.php';
|
||||
require_once __DIR__ . '/../library/sphinx.php';
|
||||
require_once __DIR__ . '/../library/scrapeer.php';
|
||||
require_once __DIR__ . '/../library/time.php';
|
||||
require_once __DIR__ . '/../library/curl.php';
|
||||
require_once __DIR__ . '/../library/valid.php';
|
||||
require_once __DIR__ . '/../library/filter.php';
|
||||
|
||||
// Vendors autoload
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
// Connect database
|
||||
try {
|
||||
|
||||
$db = new Database(DB_HOST, DB_PORT, DB_NAME, DB_USERNAME, DB_PASSWORD);
|
||||
|
||||
} catch (Exception $e) {
|
||||
|
||||
var_dump($e);
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
// Connect Sphinx
|
||||
try {
|
||||
|
||||
$sphinx = new Sphinx(SPHINX_HOST, SPHINX_PORT);
|
||||
|
||||
} catch(Exception $e) {
|
||||
|
||||
var_dump($e);
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
// Connect memcached
|
||||
try {
|
||||
|
||||
$memory = new Yggverse\Cache\Memory(MEMCACHED_HOST, MEMCACHED_PORT, MEMCACHED_NAMESPACE, MEMCACHED_TIMEOUT + time());
|
||||
|
||||
} catch(Exception $e) {
|
||||
|
||||
var_dump($e);
|
||||
|
||||
exit;
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
[
|
||||
{
|
||||
"description":"YGGtracker instance #1 with main branch updates",
|
||||
"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 with main branch updates",
|
||||
"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 @@
|
|||
[
|
||||
{
|
||||
"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,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,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(),
|
||||
|
||||
'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_VERSION' => (string) API_VERSION,
|
||||
'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 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -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 @@
|
|||
<?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 @@
|
|||
<?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;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -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 @@
|
|||
<?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,111 +0,0 @@
|
|||
<?php
|
||||
|
||||
class Sphinx {
|
||||
|
||||
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,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,670 +0,0 @@
|
|||
<?php
|
||||
|
||||
// Bootstrap
|
||||
require_once __DIR__ . '/../config/bootstrap.php';
|
||||
|
||||
// Define response
|
||||
$response = (object)
|
||||
[
|
||||
'success' => true,
|
||||
'message' => _('Internal server error'),
|
||||
'title' => sprintf(_('Oops - %s'), WEBSITE_NAME)
|
||||
];
|
||||
|
||||
// Begin action request
|
||||
switch (isset($_GET['target']) ? urldecode($_GET['target']) : false)
|
||||
{
|
||||
case 'profile':
|
||||
|
||||
switch (isset($_GET['toggle']) ? $_GET['toggle'] : false)
|
||||
{
|
||||
case 'jidenticon':
|
||||
|
||||
// Yggdrasil connections only
|
||||
if (!Valid::host($_SERVER['REMOTE_ADDR']))
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = _('Yggdrasil connection required for this action');
|
||||
}
|
||||
|
||||
// Init session
|
||||
else if (!$userId = $db->initUserId($_SERVER['REMOTE_ADDR'], USER_DEFAULT_APPROVED, time()))
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = _('Could not init user session');
|
||||
}
|
||||
|
||||
// Get user
|
||||
else if (!$user = $db->getUser($userId))
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = _('Could not init user info');
|
||||
}
|
||||
|
||||
// On first visit, redirect user to the welcome page with access level question
|
||||
else if (is_null($user->public))
|
||||
{
|
||||
header(
|
||||
sprintf('Location: %s/welcome.php', WEBSITE_URL)
|
||||
);
|
||||
}
|
||||
|
||||
// Render icon
|
||||
else
|
||||
{
|
||||
header('Cache-Control: max-age=604800');
|
||||
|
||||
|
||||
$icon = new Jdenticon\Identicon();
|
||||
|
||||
$icon->setValue($user->{USER_IDENTICON_FIELD});
|
||||
$icon->setSize(empty($_GET['size']) ? 100 : (int) $_GET['size']);
|
||||
$icon->setStyle(
|
||||
[
|
||||
'backgroundColor' => 'rgba(255, 255, 255, 0)',
|
||||
]
|
||||
);
|
||||
$icon->displayImage('webp');
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'comment':
|
||||
|
||||
switch (isset($_GET['toggle']) ? $_GET['toggle'] : false)
|
||||
{
|
||||
case 'approved':
|
||||
|
||||
// Yggdrasil connections only
|
||||
if (!Valid::host($_SERVER['REMOTE_ADDR']))
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = _('Yggdrasil connection required for this action');
|
||||
}
|
||||
|
||||
// Init session
|
||||
else if (!$userId = $db->initUserId($_SERVER['REMOTE_ADDR'], USER_DEFAULT_APPROVED, time()))
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = _('Could not init user session');
|
||||
}
|
||||
|
||||
// Get user
|
||||
else if (!$user = $db->getUser($userId))
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = _('Could not init user info');
|
||||
}
|
||||
|
||||
// On first visit, redirect user to the welcome page with access level question
|
||||
else if (is_null($user->public))
|
||||
{
|
||||
header(
|
||||
sprintf('Location: %s/welcome.php', WEBSITE_URL)
|
||||
);
|
||||
}
|
||||
|
||||
// Magnet comment exists
|
||||
else if (!$magnetComment = $db->getMagnetComment(isset($_GET['magnetCommentId']) && $_GET['magnetCommentId'] > 0 ? (int) $_GET['magnetCommentId'] : 0))
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = _('Requested magnet comment not found');
|
||||
}
|
||||
|
||||
// Access allowed
|
||||
else if (!in_array($user->address, MODERATOR_IP_LIST)) {
|
||||
|
||||
$response->success = false;
|
||||
$response->message = _('Access denied');
|
||||
}
|
||||
|
||||
// Validate callback
|
||||
else if (empty($_GET['callback']))
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = _('Callback required');
|
||||
}
|
||||
|
||||
// Validate base64
|
||||
else if (!$callback = (string) @base64_decode($_GET['callback']))
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = _('Invalid callback encoding');
|
||||
}
|
||||
|
||||
// Request valid
|
||||
else
|
||||
{
|
||||
if ($magnetComment->approved)
|
||||
{
|
||||
$db->updateMagnetCommentApproved($magnetComment->magnetCommentId, false);
|
||||
|
||||
if (USER_AUTO_APPROVE_ON_COMMENT_APPROVE)
|
||||
{
|
||||
$db->updateUserApproved($magnetComment->userId, false, time());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$db->updateMagnetCommentApproved($magnetComment->magnetCommentId, true);
|
||||
|
||||
if (USER_AUTO_APPROVE_ON_COMMENT_APPROVE)
|
||||
{
|
||||
$db->updateUserApproved($magnetComment->userId, true, time());
|
||||
}
|
||||
}
|
||||
|
||||
// Redirect to edit page
|
||||
header(
|
||||
sprintf('Location: %s', $callback)
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'new':
|
||||
|
||||
// Yggdrasil connections only
|
||||
if (!Valid::host($_SERVER['REMOTE_ADDR']))
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = _('Yggdrasil connection required for this action');
|
||||
}
|
||||
|
||||
// Init session
|
||||
else if (!$userId = $db->initUserId($_SERVER['REMOTE_ADDR'], USER_DEFAULT_APPROVED, time()))
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = _('Could not init user session');
|
||||
}
|
||||
|
||||
// Get user
|
||||
else if (!$user = $db->getUser($userId))
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = _('Could not init user info');
|
||||
}
|
||||
|
||||
// On first visit, redirect user to the welcome page with access level question
|
||||
else if (is_null($user->public))
|
||||
{
|
||||
header(
|
||||
sprintf('Location: %s/welcome.php', WEBSITE_URL)
|
||||
);
|
||||
}
|
||||
|
||||
// Magnet exists
|
||||
else if (!$magnet = $db->getMagnet(isset($_GET['magnetId']) && $_GET['magnetId'] > 0 ? (int) $_GET['magnetId'] : 0))
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = _('Requested magnet not found');
|
||||
}
|
||||
|
||||
// Access allowed
|
||||
else if (!($user->address == $db->getUser($magnet->userId)->address || in_array($user->address, MODERATOR_IP_LIST) || ($magnet->public && $magnet->approved))) {
|
||||
|
||||
$response->success = false;
|
||||
$response->message = _('Magnet not available for this action');
|
||||
}
|
||||
|
||||
// Validate callback
|
||||
else if (empty($_GET['callback']))
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = _('Callback required');
|
||||
}
|
||||
|
||||
// Validate base64
|
||||
else if (!$callback = (string) @base64_decode($_GET['callback']))
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = _('Invalid callback encoding');
|
||||
}
|
||||
|
||||
// Validate comment value
|
||||
else if (empty($_POST['comment']) ||
|
||||
mb_strlen($_POST['comment']) < MAGNET_COMMENT_MIN_LENGTH ||
|
||||
mb_strlen($_POST['comment']) > MAGNET_COMMENT_MAX_LENGTH)
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = sprintf(_('Valid comment value required, %s-%s chars allowed'), MAGNET_COMMENT_MIN_LENGTH, MAGNET_COMMENT_MAX_LENGTH);
|
||||
}
|
||||
|
||||
// Request valid
|
||||
else
|
||||
{
|
||||
if ($magnetCommentId = $db->addMagnetComment($magnet->magnetId,
|
||||
$user->userId,
|
||||
null, // @TODO implement threads
|
||||
trim($_POST['comment']),
|
||||
$user->approved || in_array($user->address, MODERATOR_IP_LIST) ? true : MAGNET_COMMENT_DEFAULT_APPROVED,
|
||||
MAGNET_COMMENT_DEFAULT_PUBLIC,
|
||||
time()))
|
||||
{
|
||||
|
||||
// Push event to other nodes
|
||||
if (API_EXPORT_ENABLED &&
|
||||
API_EXPORT_PUSH_ENABLED &&
|
||||
API_EXPORT_USERS_ENABLED &&
|
||||
API_EXPORT_MAGNETS_ENABLED &&
|
||||
API_EXPORT_MAGNET_COMMENTS_ENABLED)
|
||||
{
|
||||
if (!$memoryApiExportPush = $memory->get('api.export.push'))
|
||||
{
|
||||
$memoryApiExportPush = [];
|
||||
}
|
||||
|
||||
$memoryApiExportPush[] = (object)
|
||||
[
|
||||
'time' => time(),
|
||||
'userId' => $user->userId,
|
||||
'magnetId' => $magnet->magnetId,
|
||||
'magnetCommentId' => $magnetCommentId
|
||||
];
|
||||
|
||||
$memory->set('api.export.push', $memoryApiExportPush, 3600);
|
||||
}
|
||||
|
||||
// Redirect to referrer page
|
||||
header(
|
||||
sprintf('Location: %s#comment-%s', $callback, $magnetCommentId)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
header(
|
||||
sprintf('Location: %s', WEBSITE_URL)
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'magnet':
|
||||
|
||||
switch (isset($_GET['toggle']) ? $_GET['toggle'] : false)
|
||||
{
|
||||
case 'star':
|
||||
|
||||
// Yggdrasil connections only
|
||||
if (!Valid::host($_SERVER['REMOTE_ADDR']))
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = _('Yggdrasil connection required for this action');
|
||||
}
|
||||
|
||||
// Init session
|
||||
else if (!$userId = $db->initUserId($_SERVER['REMOTE_ADDR'], USER_DEFAULT_APPROVED, time()))
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = _('Could not init user session');
|
||||
}
|
||||
|
||||
// Get user
|
||||
else if (!$user = $db->getUser($userId))
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = _('Could not init user info');
|
||||
}
|
||||
|
||||
// On first visit, redirect user to the welcome page with access level question
|
||||
else if (is_null($user->public))
|
||||
{
|
||||
header(
|
||||
sprintf('Location: %s/welcome.php', WEBSITE_URL)
|
||||
);
|
||||
}
|
||||
|
||||
// Magnet exists
|
||||
else if (!$magnet = $db->getMagnet(isset($_GET['magnetId']) && $_GET['magnetId'] > 0 ? (int) $_GET['magnetId'] : 0))
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = _('Requested magnet not found');
|
||||
}
|
||||
|
||||
// Access allowed
|
||||
else if (!($_SERVER['REMOTE_ADDR'] == $db->getUser($magnet->userId)->address || in_array($_SERVER['REMOTE_ADDR'], MODERATOR_IP_LIST) || ($magnet->public && $magnet->approved))) {
|
||||
|
||||
$response->success = false;
|
||||
$response->message = _('Magnet not available for this action');
|
||||
}
|
||||
|
||||
// Validate callback
|
||||
else if (empty($_GET['callback']))
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = _('Callback required');
|
||||
}
|
||||
|
||||
// Validate base64
|
||||
else if (!$callback = (string) @base64_decode($_GET['callback']))
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = _('Invalid callback encoding');
|
||||
}
|
||||
|
||||
// Request valid
|
||||
else
|
||||
{
|
||||
// Save star
|
||||
if ($magnetStarId = $db->addMagnetStar( $magnet->magnetId,
|
||||
$user->userId,
|
||||
!$db->findLastMagnetStarValue($magnet->magnetId, $user->userId),
|
||||
time()))
|
||||
{
|
||||
// Push event to other nodes
|
||||
if (API_EXPORT_ENABLED &&
|
||||
API_EXPORT_PUSH_ENABLED &&
|
||||
API_EXPORT_USERS_ENABLED &&
|
||||
API_EXPORT_MAGNETS_ENABLED &&
|
||||
API_EXPORT_MAGNET_STARS_ENABLED)
|
||||
{
|
||||
if (!$memoryApiExportPush = $memory->get('api.export.push'))
|
||||
{
|
||||
$memoryApiExportPush = [];
|
||||
}
|
||||
|
||||
$memoryApiExportPush[] = (object)
|
||||
[
|
||||
'time' => time(),
|
||||
'userId' => $user->userId,
|
||||
'magnetId' => $magnet->magnetId,
|
||||
'magnetStarId' => $magnetStarId
|
||||
];
|
||||
|
||||
$memory->set('api.export.push', $memoryApiExportPush, 3600);
|
||||
}
|
||||
|
||||
// Redirect to edit page
|
||||
header(
|
||||
sprintf('Location: %s', $callback)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'new':
|
||||
|
||||
// Yggdrasil connections only
|
||||
if (!Valid::host($_SERVER['REMOTE_ADDR']))
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = _('Yggdrasil connection required for this action');
|
||||
}
|
||||
|
||||
// Init session
|
||||
else if (!$userId = $db->initUserId($_SERVER['REMOTE_ADDR'], USER_DEFAULT_APPROVED, time()))
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = _('Could not init user session');
|
||||
}
|
||||
|
||||
// Get user
|
||||
else if (!$user = $db->getUser($userId))
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = _('Could not init user info');
|
||||
}
|
||||
|
||||
// On first visit, redirect user to the welcome page with access level question
|
||||
else if (is_null($user->public))
|
||||
{
|
||||
header(
|
||||
sprintf('Location: %s/welcome.php', WEBSITE_URL)
|
||||
);
|
||||
}
|
||||
|
||||
// Validate link
|
||||
if (empty($_GET['magnet']))
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = _('Link required');
|
||||
}
|
||||
|
||||
// Validate magnet
|
||||
else if (!$magnet = Yggverse\Parser\Magnet::parse($_GET['magnet']))
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = _('Invalid magnet link');
|
||||
}
|
||||
|
||||
// Request valid
|
||||
else
|
||||
{
|
||||
// Begin magnet registration
|
||||
try
|
||||
{
|
||||
$db->beginTransaction();
|
||||
|
||||
// Init magnet
|
||||
if ($magnetId = $db->addMagnet( $user->userId,
|
||||
$magnet->xl,
|
||||
$magnet->dn,
|
||||
'', // @TODO deprecated, remove
|
||||
MAGNET_DEFAULT_PUBLIC,
|
||||
MAGNET_DEFAULT_COMMENTS,
|
||||
MAGNET_DEFAULT_SENSITIVE,
|
||||
$user->approved ? true : MAGNET_DEFAULT_APPROVED,
|
||||
time()))
|
||||
{
|
||||
foreach ($magnet as $key => $value)
|
||||
{
|
||||
switch ($key)
|
||||
{
|
||||
case 'xt':
|
||||
foreach ($value as $xt)
|
||||
{
|
||||
if (Yggverse\Parser\Magnet::isXTv1($xt))
|
||||
{
|
||||
$db->addMagnetToInfoHash(
|
||||
$magnetId,
|
||||
$db->initInfoHashId(
|
||||
Yggverse\Parser\Magnet::filterInfoHash($xt), 1
|
||||
)
|
||||
);
|
||||
}
|
||||
if (Yggverse\Parser\Magnet::isXTv2($xt))
|
||||
{
|
||||
$db->addMagnetToInfoHash(
|
||||
$magnetId,
|
||||
$db->initInfoHashId(
|
||||
Yggverse\Parser\Magnet::filterInfoHash($xt), 2
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'tr':
|
||||
foreach ($value as $tr)
|
||||
{
|
||||
if (Valid::url($tr))
|
||||
{
|
||||
if ($url = Yggverse\Parser\Url::parse($tr))
|
||||
{
|
||||
$db->initMagnetToAddressTrackerId(
|
||||
$magnetId,
|
||||
$db->initAddressTrackerId(
|
||||
$db->initSchemeId($url->host->scheme),
|
||||
$db->initHostId($url->host->name),
|
||||
$db->initPortId($url->host->port),
|
||||
$db->initUriId($url->page->uri)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'ws':
|
||||
foreach ($value as $ws)
|
||||
{
|
||||
// @TODO
|
||||
}
|
||||
break;
|
||||
case 'as':
|
||||
foreach ($value as $as)
|
||||
{
|
||||
if (Valid::url($as))
|
||||
{
|
||||
if ($url = Yggverse\Parser\Url::parse($as))
|
||||
{
|
||||
$db->initMagnetToAcceptableSourceId(
|
||||
$magnetId,
|
||||
$db->initAcceptableSourceId(
|
||||
$db->initSchemeId($url->host->scheme),
|
||||
$db->initHostId($url->host->name),
|
||||
$db->initPortId($url->host->port),
|
||||
$db->initUriId($url->page->uri)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'xs':
|
||||
foreach ($value as $xs)
|
||||
{
|
||||
if (Valid::url($xs))
|
||||
{
|
||||
if ($url = Yggverse\Parser\Url::parse($xs))
|
||||
{
|
||||
$db->initMagnetToExactSourceId(
|
||||
$magnetId,
|
||||
$db->initExactSourceId(
|
||||
$db->initSchemeId($url->host->scheme),
|
||||
$db->initHostId($url->host->name),
|
||||
$db->initPortId($url->host->port),
|
||||
$db->initUriId($url->page->uri)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'mt':
|
||||
foreach ($value as $mt)
|
||||
{
|
||||
// @TODO
|
||||
}
|
||||
break;
|
||||
case 'x.pe':
|
||||
foreach ($value as $xPe)
|
||||
{
|
||||
// @TODO
|
||||
}
|
||||
break;
|
||||
case 'kt':
|
||||
foreach ($value as $kt)
|
||||
{
|
||||
$db->initMagnetToKeywordTopicId(
|
||||
$magnetId,
|
||||
$db->initKeywordTopicId(trim(mb_strtolower(strip_tags(html_entity_decode($kt)))))
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$db->commit();
|
||||
|
||||
// Redirect to edit page
|
||||
header(sprintf('Location: %s/edit.php?magnetId=%s', trim(WEBSITE_URL, '/'), $magnetId));
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
|
||||
var_dump($e);
|
||||
|
||||
$db->rollBack();
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="<?php echo WEBSITE_URL ?>/assets/theme/default/css/common.css?<?php echo WEBSITE_CSS_VERSION ?>" />
|
||||
<link rel="stylesheet" type="text/css" href="<?php echo WEBSITE_URL ?>/assets/theme/default/css/framework.css?<?php echo WEBSITE_CSS_VERSION ?>" />
|
||||
<title>
|
||||
<?php echo $response->title ?>
|
||||
</title>
|
||||
<meta name="robots" content="noindex,nofollow"/>
|
||||
<meta name="author" content="YGGtracker" />
|
||||
<meta charset="UTF-8" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div class="container">
|
||||
<div class="row margin-t-8 text-center">
|
||||
<a class="logo" href="<?php echo WEBSITE_URL ?>"><?php echo str_replace('YGG', '<span>YGG</span>', WEBSITE_NAME) ?></a>
|
||||
<form class="margin-t-8" name="search" method="get" action="<?php echo WEBSITE_URL ?>/index.php">
|
||||
<input type="text" name="query" value="" placeholder="<?php echo _('search or submit magnet link') ?>" />
|
||||
<input type="submit" value="<?php echo _('submit') ?>" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<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">
|
||||
<div class="text-center"><?php echo $response->message ?></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php if (!empty($_SERVER['HTTP_REFERER']) && false !== strpos($_SERVER['HTTP_REFERER'], WEBSITE_URL)) { ?>
|
||||
<div class="row">
|
||||
<div class="column width-100 text-right">
|
||||
<a class="button margin-l-8"
|
||||
rel="nofollow"
|
||||
href="<?php echo $_SERVER['HTTP_REFERER'] ?>">
|
||||
<?php echo _('back') ?>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="column width-100 text-center margin-y-8">
|
||||
<?php foreach (json_decode(file_get_contents(__DIR__ . '/../config/trackers.json')) as $i => $tracker) { ?>
|
||||
<?php if (!empty($tracker->announce) && !empty($tracker->stats)) { ?>
|
||||
<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 } ?>
|
||||
<?php } ?>
|
||||
<a href="<?php echo WEBSITE_URL ?>/faq.php"><?php echo _('F.A.Q') ?></a>
|
||||
|
|
||||
<a href="<?php echo WEBSITE_URL ?>/node.php"><?php echo _('Node') ?></a>
|
||||
|
|
||||
<a rel="nofollow" href="<?php echo WEBSITE_URL ?>/index.php?rss"><?php echo _('RSS') ?></a>
|
||||
<?php if (API_EXPORT_ENABLED) { ?>
|
||||
|
|
||||
<a rel="nofollow" href="<?php echo WEBSITE_URL ?>/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,938 +0,0 @@
|
|||
<?php
|
||||
|
||||
// Bootstrap
|
||||
require_once __DIR__ . '/../../config/bootstrap.php';
|
||||
|
||||
// Init Debug
|
||||
$debug =
|
||||
[
|
||||
'time' => [
|
||||
'ISO8601' => date('c'),
|
||||
'total' => microtime(true),
|
||||
],
|
||||
'memory' =>
|
||||
[
|
||||
'start' => memory_get_usage(),
|
||||
'total' => 0,
|
||||
'peaks' => 0
|
||||
],
|
||||
'exception' => []
|
||||
];
|
||||
|
||||
// Define response
|
||||
$response =
|
||||
[
|
||||
'status' => false,
|
||||
'message' => _('Internal server error'),
|
||||
'data' => [
|
||||
'user' => [],
|
||||
'magnet' => [],
|
||||
'magnetDownload' => [],
|
||||
'magnetComment' => [],
|
||||
'magnetView' => [],
|
||||
'magnetStar' => [],
|
||||
]
|
||||
];
|
||||
|
||||
// Init connections whitelist
|
||||
$connectionWhiteList = [];
|
||||
|
||||
foreach (json_decode(file_get_contents(__DIR__ . '/../../config/nodes.json')) as $node)
|
||||
{
|
||||
// Skip non-condition addresses
|
||||
if (!Valid::url($node->manifest))
|
||||
{
|
||||
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 on same host sub-folders, improve condition
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$connectionWhiteList[] = str_replace(['[',']'], false, $manifestUrl->host->name);
|
||||
}
|
||||
|
||||
// API import enabled
|
||||
$error = [];
|
||||
|
||||
if (!API_IMPORT_ENABLED)
|
||||
{
|
||||
$response =
|
||||
[
|
||||
'status' => false,
|
||||
'message' => _('Import API disabled')
|
||||
];
|
||||
}
|
||||
|
||||
// Push API import enabled
|
||||
else if (!API_IMPORT_PUSH_ENABLED)
|
||||
{
|
||||
$response =
|
||||
[
|
||||
'status' => false,
|
||||
'message' => _('Push API import disabled')
|
||||
];
|
||||
}
|
||||
|
||||
// Yggdrasil connections only
|
||||
else if (!Valid::host($_SERVER['REMOTE_ADDR'], $error))
|
||||
{
|
||||
$response =
|
||||
[
|
||||
'status' => false,
|
||||
'message' => $error
|
||||
];
|
||||
}
|
||||
|
||||
// Init session
|
||||
else if (!in_array($_SERVER['REMOTE_ADDR'], $connectionWhiteList))
|
||||
{
|
||||
$response =
|
||||
[
|
||||
'status' => false,
|
||||
'message' => sprintf(
|
||||
_('Push API access denied for host "%s"'),
|
||||
$_SERVER['REMOTE_ADDR']
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
// Init session
|
||||
else if (!$userId = $db->initUserId($_SERVER['REMOTE_ADDR'], USER_DEFAULT_APPROVED, time()))
|
||||
{
|
||||
$response =
|
||||
[
|
||||
'status' => false,
|
||||
'message' => _('Could not init user session for this connection')
|
||||
];
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
else if (empty($_POST['data']))
|
||||
{
|
||||
$response =
|
||||
[
|
||||
'status' => false,
|
||||
'message' => _('Request protocol invalid')
|
||||
];
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
else if (false === $data = json_decode($_POST['data']))
|
||||
{
|
||||
$response =
|
||||
[
|
||||
'status' => false,
|
||||
'message' => _('Could not decode data requested')
|
||||
];
|
||||
}
|
||||
|
||||
// Import begin
|
||||
else
|
||||
{
|
||||
$response =
|
||||
[
|
||||
'status' => true,
|
||||
'message' => sprintf(
|
||||
_('Connection for "%s" established'),
|
||||
$_SERVER['REMOTE_ADDR']
|
||||
)
|
||||
];
|
||||
|
||||
// Init alias registry
|
||||
$aliasUserId = [];
|
||||
$aliasMagnetId = [];
|
||||
|
||||
try {
|
||||
|
||||
// Transaction begin
|
||||
$db->beginTransaction();
|
||||
|
||||
// Process request
|
||||
foreach ((object) $data as $field => $remote)
|
||||
{
|
||||
// Process alias fields
|
||||
switch ($field)
|
||||
{
|
||||
case 'user':
|
||||
|
||||
if (!API_IMPORT_USERS_ENABLED)
|
||||
{
|
||||
$response['user'][] = [
|
||||
'status' => false,
|
||||
'message' => _('Users import disabled on this node. Related content skipped.')
|
||||
];
|
||||
|
||||
continue 2;
|
||||
}
|
||||
|
||||
// Validate remote fields
|
||||
$error = [];
|
||||
|
||||
if (!Valid::user($remote, $error))
|
||||
{
|
||||
$response['user'][] = [
|
||||
'status' => false,
|
||||
'message' => sprintf(
|
||||
_('User data mismatch protocol with error: %s'),
|
||||
print_r($error, true)
|
||||
),
|
||||
];
|
||||
|
||||
continue 2;
|
||||
}
|
||||
|
||||
// Skip import on user approved required
|
||||
if (API_IMPORT_USERS_APPROVED_ONLY && !$remote->approved)
|
||||
{
|
||||
$response['user'][] = [
|
||||
'status' => false,
|
||||
'message' => _('Node accepting approved users only')
|
||||
];
|
||||
|
||||
continue 2;
|
||||
}
|
||||
|
||||
// Init local user by remote address
|
||||
if (!$local = $db->getUser($db->initUserId($remote->address,
|
||||
USER_AUTO_APPROVE_ON_IMPORT_APPROVED ? $remote->approved : USER_DEFAULT_APPROVED,
|
||||
$remote->timeAdded)))
|
||||
{
|
||||
$response['user'][] = [
|
||||
'status' => false,
|
||||
'message' => _('Could not init user profile')
|
||||
];
|
||||
|
||||
continue 2;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
$response['user'][] = [
|
||||
'status' => true,
|
||||
'message' => sprintf(
|
||||
_('User profile successfully associated with ID "%s"'),
|
||||
$local->userId
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
// Register user alias
|
||||
$aliasUserId[$remote->userId] = $local->userId;
|
||||
|
||||
// Update time added if newer
|
||||
if ($local->timeAdded < $remote->timeAdded)
|
||||
{
|
||||
$db->updateUserTimeAdded(
|
||||
$local->userId,
|
||||
$remote->timeAdded
|
||||
);
|
||||
|
||||
$response['user'][] = [
|
||||
'status' => true,
|
||||
'message' => sprintf(
|
||||
_('Field "timeAdded" changed to newer value for user ID "%s"'),
|
||||
$local->userId
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
// Update user info if newer
|
||||
if ($local->timeUpdated < $remote->timeUpdated)
|
||||
{
|
||||
// Update time updated
|
||||
$db->updateUserTimeUpdated(
|
||||
$local->userId,
|
||||
$remote->timeUpdated
|
||||
);
|
||||
|
||||
$response['user'][] = [
|
||||
'status' => true,
|
||||
'message' => sprintf(
|
||||
_('Field "timeUpdated" changed to newer value for user ID "%s"'),
|
||||
$local->userId
|
||||
)
|
||||
];
|
||||
|
||||
// Update approved for existing user
|
||||
if (USER_AUTO_APPROVE_ON_IMPORT_APPROVED && $local->approved !== $remote->approved && $remote->approved)
|
||||
{
|
||||
$db->updateUserApproved(
|
||||
$local->userId,
|
||||
$remote->approved,
|
||||
$remote->timeUpdated
|
||||
);
|
||||
|
||||
$response['user'][] = [
|
||||
'status' => true,
|
||||
'message' => sprintf(
|
||||
_('Field "approved" changed to newer value for user ID "%s"'),
|
||||
$local->userId
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
// Set public as received remotely
|
||||
if (!$local->public)
|
||||
{
|
||||
$db->updateUserPublic(
|
||||
$local->userId,
|
||||
true,
|
||||
$remote->timeUpdated
|
||||
);
|
||||
|
||||
$response['user'][] = [
|
||||
'status' => true,
|
||||
'message' => sprintf(
|
||||
_('Field "public" changed to newer value for user ID "%s"'),
|
||||
$local->userId
|
||||
)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case 'magnet':
|
||||
|
||||
if (!API_IMPORT_MAGNETS_ENABLED)
|
||||
{
|
||||
$response['magnet'][] = [
|
||||
'status' => false,
|
||||
'message' => _('Magnets import disabled on this node. Related content skipped.')
|
||||
];
|
||||
|
||||
continue 2;
|
||||
}
|
||||
|
||||
// Validate remote fields
|
||||
$error = [];
|
||||
|
||||
if (!Valid::magnet($remote, $error))
|
||||
{
|
||||
$response['magnet'][] = [
|
||||
'status' => false,
|
||||
'message' => sprintf(
|
||||
_('Magnet data mismatch protocol with error: %s'),
|
||||
print_r($error, true)
|
||||
),
|
||||
];
|
||||
|
||||
continue 2;
|
||||
}
|
||||
|
||||
// User local alias required
|
||||
if (!isset($aliasUserId[$remote->userId]))
|
||||
{
|
||||
$response['magnet'][] = [
|
||||
'status' => false,
|
||||
'message' => _('User data relation not found for magnet'),
|
||||
];
|
||||
|
||||
continue 2;
|
||||
}
|
||||
|
||||
// Skip import on magnet approved required
|
||||
if (API_IMPORT_MAGNETS_APPROVED_ONLY && !$remote->approved)
|
||||
{
|
||||
$response['magnet'][] = [
|
||||
'status' => false,
|
||||
'message' => _('Node accepting approved magnets only')
|
||||
];
|
||||
|
||||
continue 2;
|
||||
}
|
||||
|
||||
/// Add new magnet if not exist by timestamp added for this user
|
||||
if ($local = $db->findMagnet($aliasUserId[$remote->userId], $remote->timeAdded))
|
||||
{
|
||||
$response['magnet'][] = [
|
||||
'status' => true,
|
||||
'message' => sprintf(
|
||||
_('Magnet successfully associated with ID "%s"'),
|
||||
$local->magnetId
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
/// Add and init new magnet if not exist
|
||||
else if ($local = $db->getMagnet(
|
||||
$db->addMagnet(
|
||||
$aliasUserId[$remote->userId],
|
||||
$remote->xl,
|
||||
$remote->dn,
|
||||
'', // @TODO linkSource used for debug only, will be deleted later
|
||||
true,
|
||||
$remote->comments,
|
||||
$remote->sensitive,
|
||||
MAGNET_AUTO_APPROVE_ON_IMPORT_APPROVED ? $remote->approved : MAGNET_DEFAULT_APPROVED,
|
||||
$remote->timeAdded
|
||||
)
|
||||
)
|
||||
)
|
||||
{
|
||||
$response['magnet'][] = [
|
||||
'status' => true,
|
||||
'message' => sprintf(
|
||||
_('Magnet successfully synced with ID "%s"'),
|
||||
$local->magnetId
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
$response['magnet'][] = [
|
||||
'status' => false,
|
||||
'message' => sprintf(
|
||||
_('Could not init magnet: %s'),
|
||||
$remote
|
||||
)
|
||||
];
|
||||
|
||||
continue 2;
|
||||
}
|
||||
|
||||
/// Add magnet alias for this host
|
||||
$aliasMagnetId[$remote->magnetId] = $local->magnetId;
|
||||
|
||||
/// Update time added if newer
|
||||
if ($local->timeAdded < $remote->timeAdded)
|
||||
{
|
||||
$db->updateMagnetTimeAdded(
|
||||
$local->magnetId,
|
||||
$remote->timeAdded
|
||||
);
|
||||
|
||||
$response['magnet'][] = [
|
||||
'status' => true,
|
||||
'message' => sprintf(
|
||||
_('Field "timeAdded" changed to newer value for magnet ID "%s"'),
|
||||
$local->magnetId
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
/// Update info if remote newer
|
||||
if ($local->timeUpdated < $remote->timeUpdated)
|
||||
{
|
||||
// Magnet fields
|
||||
$db->updateMagnetXl($local->magnetId, $remote->xl, $remote->timeUpdated);
|
||||
$db->updateMagnetDn($local->magnetId, $remote->dn, $remote->timeUpdated);
|
||||
$db->updateMagnetTitle($local->magnetId, $remote->title, $remote->timeUpdated);
|
||||
$db->updateMagnetPreview($local->magnetId, $remote->preview, $remote->timeUpdated);
|
||||
$db->updateMagnetDescription($local->magnetId, $remote->description, $remote->timeUpdated);
|
||||
$db->updateMagnetComments($local->magnetId, $remote->comments, $remote->timeUpdated);
|
||||
$db->updateMagnetSensitive($local->magnetId, $remote->sensitive, $remote->timeUpdated);
|
||||
|
||||
if (MAGNET_AUTO_APPROVE_ON_IMPORT_APPROVED && $local->approved !== $remote->approved && $remote->approved)
|
||||
{
|
||||
$db->updateMagnetApproved($local->magnetId, $remote->approved, $remote->timeUpdated);
|
||||
}
|
||||
|
||||
// xt
|
||||
foreach ((array) $remote->xt as $xt)
|
||||
{
|
||||
switch ($xt->version)
|
||||
{
|
||||
case 1:
|
||||
|
||||
$exist = false;
|
||||
|
||||
foreach ($db->findMagnetToInfoHashByMagnetId($local->magnetId) as $result)
|
||||
{
|
||||
if ($infoHash = $db->getInfoHash($result->infoHashId))
|
||||
{
|
||||
if ($infoHash->version == 1)
|
||||
{
|
||||
$exist = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$exist)
|
||||
{
|
||||
$db->addMagnetToInfoHash(
|
||||
$local->magnetId,
|
||||
$db->initInfoHashId(
|
||||
$xt->value, 1
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
|
||||
$exist = false;
|
||||
|
||||
foreach ($db->findMagnetToInfoHashByMagnetId($local->magnetId) as $result)
|
||||
{
|
||||
if ($infoHash = $db->getInfoHash($result->infoHashId))
|
||||
{
|
||||
if ($infoHash->version == 2)
|
||||
{
|
||||
$exist = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$exist)
|
||||
{
|
||||
$db->addMagnetToInfoHash(
|
||||
$local->magnetId,
|
||||
$db->initInfoHashId(
|
||||
$xt->value, 2
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// kt
|
||||
$db->deleteMagnetToKeywordTopicByMagnetId($local->magnetId);
|
||||
|
||||
foreach ($remote->kt as $kt)
|
||||
{
|
||||
$db->initMagnetToKeywordTopicId(
|
||||
$local->magnetId,
|
||||
$db->initKeywordTopicId(trim(mb_strtolower($kt)))
|
||||
);
|
||||
}
|
||||
|
||||
// tr
|
||||
$db->deleteMagnetToAddressTrackerByMagnetId($local->magnetId);
|
||||
|
||||
foreach ($remote->tr as $tr)
|
||||
{
|
||||
if ($url = Yggverse\Parser\Url::parse($tr))
|
||||
{
|
||||
$db->initMagnetToAddressTrackerId(
|
||||
$local->magnetId,
|
||||
$db->initAddressTrackerId(
|
||||
$db->initSchemeId($url->host->scheme),
|
||||
$db->initHostId($url->host->name),
|
||||
$db->initPortId($url->host->port),
|
||||
$db->initUriId($url->page->uri)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// as
|
||||
$db->deleteMagnetToAcceptableSourceByMagnetId($local->magnetId);
|
||||
|
||||
foreach ($remote->as as $as)
|
||||
{
|
||||
if ($url = Yggverse\Parser\Url::parse($as))
|
||||
{
|
||||
$db->initMagnetToAcceptableSourceId(
|
||||
$local->magnetId,
|
||||
$db->initAcceptableSourceId(
|
||||
$db->initSchemeId($url->host->scheme),
|
||||
$db->initHostId($url->host->name),
|
||||
$db->initPortId($url->host->port),
|
||||
$db->initUriId($url->page->uri)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// xs
|
||||
$db->deleteMagnetToExactSourceByMagnetId($local->magnetId);
|
||||
|
||||
foreach ($remote->xs as $xs)
|
||||
{
|
||||
if ($url = Yggverse\Parser\Url::parse($xs))
|
||||
{
|
||||
$db->initMagnetToExactSourceId(
|
||||
$local->magnetId,
|
||||
$db->initExactSourceId(
|
||||
$db->initSchemeId($url->host->scheme),
|
||||
$db->initHostId($url->host->name),
|
||||
$db->initPortId($url->host->port),
|
||||
$db->initUriId($url->page->uri)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$response['magnet'][] = [
|
||||
'status' => true,
|
||||
'message' => sprintf(
|
||||
_('Magnet fields updated to newer version for magnet ID "%s"'),
|
||||
$local->magnetId
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
break;
|
||||
case 'magnetComment':
|
||||
|
||||
if (!API_IMPORT_MAGNET_COMMENTS_ENABLED)
|
||||
{
|
||||
$response['magnetComment'][] = [
|
||||
'status' => false,
|
||||
'message' => _('Magnet comments import disabled on this node')
|
||||
];
|
||||
|
||||
continue 2;
|
||||
}
|
||||
|
||||
// Validate
|
||||
$error = [];
|
||||
|
||||
if (!Valid::magnetComment($remote, $error))
|
||||
{
|
||||
$response['magnetComment'][] = [
|
||||
'status' => false,
|
||||
'message' => sprintf(
|
||||
_('Magnet comment data mismatch protocol with error: %s'),
|
||||
print_r($error, true)
|
||||
),
|
||||
];
|
||||
|
||||
continue 2;
|
||||
}
|
||||
|
||||
// Skip import on magnet approved required
|
||||
if (API_IMPORT_MAGNET_COMMENTS_APPROVED_ONLY && !$remote->approved)
|
||||
{
|
||||
$response['magnetComment'][] = [
|
||||
'status' => false,
|
||||
'message' => _('Node accepting approved magnet comments only: %s')
|
||||
];
|
||||
|
||||
continue 2;
|
||||
}
|
||||
|
||||
// User local alias required
|
||||
if (!isset($aliasUserId[$remote->userId]) || !isset($aliasMagnetId[$remote->magnetId]))
|
||||
{
|
||||
$response['magnetComment'][] = [
|
||||
'status' => false,
|
||||
'message' => _('Magnet comment data relation not found for: %s')
|
||||
];
|
||||
|
||||
continue 2;
|
||||
}
|
||||
|
||||
// Parent comment provided
|
||||
if (is_int($remote->magnetCommentIdParent))
|
||||
{
|
||||
$localMagnetCommentIdParent = null; // @TODO feature not in use yet
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
$localMagnetCommentIdParent = null;
|
||||
}
|
||||
|
||||
// Magnet comment exists by timestamp added for this user
|
||||
if ($local = $db->findMagnetComment($aliasMagnetId[$remote->magnetId],
|
||||
$aliasUserId[$remote->userId],
|
||||
$remote->timeAdded))
|
||||
{
|
||||
$response['magnetComment'][] = [
|
||||
'status' => true,
|
||||
'message' => sprintf(
|
||||
_('Magnet comment successfully associated with ID "%s"'),
|
||||
$local->magnetCommentId
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
// Magnet comment exists by timestamp added for this user, register new one
|
||||
else if ($magnetCommentId = $db->addMagnetComment($aliasMagnetId[$remote->magnetId],
|
||||
$aliasUserId[$remote->userId],
|
||||
$localMagnetCommentIdParent,
|
||||
$remote->value,
|
||||
$remote->approved,
|
||||
true,
|
||||
$remote->timeAdded))
|
||||
{
|
||||
$response['magnetComment'][] = [
|
||||
'status' => true,
|
||||
'message' => sprintf(
|
||||
_('Magnet comment successfully synced with ID "%s"'),
|
||||
$magnetCommentId
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
break;
|
||||
case 'magnetDownload':
|
||||
|
||||
// Magnet downloads
|
||||
if (!API_IMPORT_MAGNET_DOWNLOADS_ENABLED)
|
||||
{
|
||||
$response['magnetDownload'][] = [
|
||||
'status' => false,
|
||||
'message' => _('Magnet downloads import disabled on this node')
|
||||
];
|
||||
|
||||
continue 2;
|
||||
}
|
||||
|
||||
// Validate
|
||||
$error = [];
|
||||
|
||||
if (!Valid::magnetDownload($remote, $error))
|
||||
{
|
||||
$response['magnetDownload'][] = [
|
||||
'status' => false,
|
||||
'message' => sprintf(
|
||||
_('Magnet download data mismatch protocol with error: %s'),
|
||||
print_r($error, true)
|
||||
),
|
||||
];
|
||||
|
||||
continue 2;
|
||||
}
|
||||
|
||||
// User local alias required
|
||||
if (!isset($aliasUserId[$remote->userId]) || !isset($aliasMagnetId[$remote->magnetId]))
|
||||
{
|
||||
$response['magnetDownload'][] = [
|
||||
'status' => false,
|
||||
'message' => _('Magnet download data relation not found')
|
||||
];
|
||||
|
||||
continue 2;
|
||||
}
|
||||
|
||||
// Magnet download exists by timestamp added for this user
|
||||
if ($local = $db->findMagnetDownload($aliasMagnetId[$remote->magnetId],
|
||||
$aliasUserId[$remote->userId],
|
||||
$remote->timeAdded))
|
||||
{
|
||||
$response['magnetDownload'][] = [
|
||||
'status' => true,
|
||||
'message' => sprintf(
|
||||
_('Magnet download successfully associated with ID "%s"'),
|
||||
$local->magnetDownloadId
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
// Magnet download exists by timestamp added for this user, register new one
|
||||
else if ($magnetDownloadId = $db->addMagnetDownload($aliasMagnetId[$remote->magnetId],
|
||||
$aliasUserId[$remote->userId],
|
||||
$remote->timeAdded))
|
||||
{
|
||||
$response['magnetDownload'][] = [
|
||||
'status' => true,
|
||||
'message' => sprintf(
|
||||
_('Magnet download successfully synced with ID "%s"'),
|
||||
$magnetDownloadId
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
break;
|
||||
case 'magnetStar':
|
||||
|
||||
if (!API_IMPORT_MAGNET_STARS_ENABLED)
|
||||
{
|
||||
$response['magnetStar'][] = [
|
||||
'status' => false,
|
||||
'message' => _('Magnet stars import disabled on this node')
|
||||
];
|
||||
|
||||
continue 2;
|
||||
}
|
||||
|
||||
// Validate
|
||||
$error = [];
|
||||
|
||||
if (!Valid::magnetStar($remote, $error))
|
||||
{
|
||||
$response['magnetStar'][] = [
|
||||
'status' => false,
|
||||
'message' => sprintf(
|
||||
_('Magnet star data mismatch protocol with error: %s'),
|
||||
print_r($error, true)
|
||||
),
|
||||
];
|
||||
|
||||
continue 2;
|
||||
}
|
||||
|
||||
// User local alias required
|
||||
if (!isset($aliasUserId[$remote->userId]) || !isset($aliasMagnetId[$remote->magnetId]))
|
||||
{
|
||||
$response['magnetStar'][] = [
|
||||
'status' => false,
|
||||
'message' => _('Magnet star data relation not found')
|
||||
];
|
||||
|
||||
continue 2;
|
||||
}
|
||||
|
||||
// Magnet star exists by timestamp added for this user
|
||||
if ($local = $db->findMagnetStar($aliasMagnetId[$remote->magnetId],
|
||||
$aliasUserId[$remote->userId],
|
||||
$remote->timeAdded))
|
||||
{
|
||||
$response['magnetStar'][] = [
|
||||
'status' => true,
|
||||
'message' => sprintf(
|
||||
_('Magnet star successfully associated with ID "%s"'),
|
||||
$local->magnetStarId
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
// Magnet star exists by timestamp added for this user, register new one
|
||||
else if ($magnetStarId = $db->addMagnetStar($aliasMagnetId[$remote->magnetId],
|
||||
$aliasUserId[$remote->userId],
|
||||
$remote->value,
|
||||
$remote->timeAdded))
|
||||
{
|
||||
$response['magnetStar'][] = [
|
||||
'status' => true,
|
||||
'message' => sprintf(
|
||||
_('Magnet star successfully synced with ID "%s"'),
|
||||
$magnetStarId
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
break;
|
||||
case 'magnetView':
|
||||
|
||||
if (!API_IMPORT_MAGNET_VIEWS_ENABLED)
|
||||
{
|
||||
$response['magnetView'][] = [
|
||||
'status' => false,
|
||||
'message' => _('Magnet views import disabled on this node')
|
||||
];
|
||||
|
||||
continue 2;
|
||||
}
|
||||
|
||||
// Validate
|
||||
$error = [];
|
||||
|
||||
if (!Valid::magnetView($remote, $error))
|
||||
{
|
||||
$response['magnetView'][] = [
|
||||
'status' => false,
|
||||
'message' => sprintf(
|
||||
_('Magnet view data mismatch protocol with error: %s'),
|
||||
print_r($error, true)
|
||||
),
|
||||
];
|
||||
|
||||
continue 2;
|
||||
}
|
||||
|
||||
// User local alias required
|
||||
if (!isset($aliasUserId[$remote->userId]) || !isset($aliasMagnetId[$remote->magnetId]))
|
||||
{
|
||||
$response['magnetView'][] = [
|
||||
'status' => false,
|
||||
'message' => _('Magnet view data relation not found for: %s')
|
||||
];
|
||||
|
||||
continue 2;
|
||||
}
|
||||
|
||||
// Magnet view exists by timestamp added for this user
|
||||
if ($local = $db->findMagnetView($aliasMagnetId[$remote->magnetId],
|
||||
$aliasUserId[$remote->userId],
|
||||
$remote->timeAdded))
|
||||
{
|
||||
$response['magnetView'][] = [
|
||||
'status' => true,
|
||||
'message' => sprintf(
|
||||
_('Magnet view successfully associated with ID "%s"'),
|
||||
$local->magnetViewId
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
// Magnet view exists by timestamp added for this user, register new one
|
||||
else if ($magnetViewId = $db->addMagnetView($aliasMagnetId[$remote->magnetId],
|
||||
$aliasUserId[$remote->userId],
|
||||
$remote->timeAdded))
|
||||
{
|
||||
$response['magnetView'][] = [
|
||||
'status' => true,
|
||||
'message' => sprintf(
|
||||
_('Magnet view successfully synced with ID "%s"'),
|
||||
$magnetViewId
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
|
||||
$response[$field][] =
|
||||
[
|
||||
'status' => false,
|
||||
'message' => _('Field "%s" not supported by protocol')
|
||||
];
|
||||
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
$db->commit();
|
||||
}
|
||||
|
||||
catch (Exception $error)
|
||||
{
|
||||
$debug['exception'][] = print_r($error, true);
|
||||
|
||||
$db->rollBack();
|
||||
}
|
||||
}
|
||||
|
||||
// Debug log
|
||||
if (LOG_API_PUSH_ENABLED)
|
||||
{
|
||||
@mkdir(LOG_DIRECTORY, 0770, true);
|
||||
|
||||
if ($handle = fopen(LOG_DIRECTORY . '/' . LOG_API_PUSH_FILENAME, 'a+'))
|
||||
{
|
||||
$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;
|
||||
|
||||
fwrite(
|
||||
$handle,
|
||||
print_r(
|
||||
[
|
||||
'response' => $response,
|
||||
'debug' => $debug
|
||||
],
|
||||
true
|
||||
)
|
||||
);
|
||||
|
||||
fclose($handle);
|
||||
|
||||
chmod(LOG_DIRECTORY . '/' . LOG_API_PUSH_FILENAME, 0770);
|
||||
}
|
||||
}
|
||||
|
||||
// Output
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
echo json_encode($response);
|
||||
|
|
@ -1,115 +0,0 @@
|
|||
* {
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
background: #282b3c;
|
||||
color: #ccc;
|
||||
font-family: Sans-serif;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
a,
|
||||
a:visited,
|
||||
a:active {
|
||||
color: #96d9a1;
|
||||
text-decoration: none;
|
||||
opacity: .9;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
opacity: 1;
|
||||
transition: opacity .5s ease-in-out;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5 {
|
||||
display: inline-block;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #ccc;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
a h2,
|
||||
a:visited h2,
|
||||
a:active h2 {
|
||||
/* @TODO doubts
|
||||
color: #a4d4ff;
|
||||
*/
|
||||
}
|
||||
|
||||
input,
|
||||
textarea {
|
||||
background: #5d627d;
|
||||
color: #ccc;
|
||||
border: 0;
|
||||
border-radius: 3px;
|
||||
padding: 6px 8px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
textarea:focus,
|
||||
input:focus {
|
||||
outline: none;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
textarea {
|
||||
min-height: 86px;
|
||||
}
|
||||
|
||||
/* @TODO improve focus out
|
||||
textarea:focus {
|
||||
min-height: 120px;
|
||||
}
|
||||
*/
|
||||
|
||||
textarea::placeholder,
|
||||
input::placeholder {
|
||||
color: #9698a5;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
input:hover,
|
||||
textarea:hover {
|
||||
background: #636884;
|
||||
}
|
||||
|
||||
input[type="submit"] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 2px 0;
|
||||
}
|
||||
|
||||
header a.logo {
|
||||
color: #ccc;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
header a.logo > span {
|
||||
color: #96d9a1;
|
||||
}
|
||||
|
||||
a.button,
|
||||
a.button:visited,
|
||||
a.button:active,
|
||||
a.button:hover,
|
||||
.button {
|
||||
background: #5d627d;
|
||||
color: #ccc;
|
||||
border: 0;
|
||||
border-radius: 3px;
|
||||
padding: 6px 8px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
|
@ -1,338 +0,0 @@
|
|||
.container {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
max-width: 748px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.row {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.column {
|
||||
position: relative;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.float-right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.text-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.text-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.text-color-green {
|
||||
color: #96d9a1;
|
||||
}
|
||||
|
||||
.text-color-red {
|
||||
color: #d77575;
|
||||
}
|
||||
|
||||
.text-color-pink {
|
||||
color: #b55cab;
|
||||
}
|
||||
|
||||
.text-color-default {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
/*
|
||||
.text-color-pink {
|
||||
color: #a44399;
|
||||
}
|
||||
*/
|
||||
|
||||
.text-color-blue {
|
||||
color: #5785b7;
|
||||
}
|
||||
|
||||
.text-color-night {
|
||||
color: #838695;
|
||||
}
|
||||
|
||||
.label {
|
||||
padding: 4px 8px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.label-green {
|
||||
color: #fff;
|
||||
background-color: #65916d;
|
||||
}
|
||||
|
||||
.position-relative {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.top--2 {
|
||||
top: -2px;
|
||||
}
|
||||
|
||||
.top-2 {
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
.line-height-26 {
|
||||
line-height: 26px;
|
||||
}
|
||||
|
||||
.border-radius-3 {
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.border-pink-light {
|
||||
border: 1px #9b6895 solid;
|
||||
}
|
||||
|
||||
.border-pink {
|
||||
border: 1px #a44399 solid;
|
||||
}
|
||||
|
||||
.border-bottom-pink {
|
||||
border-bottom: 1px #a44399 solid;
|
||||
}
|
||||
|
||||
.border-default {
|
||||
border: 1px #5d627d solid;
|
||||
}
|
||||
|
||||
.border-bottom-default {
|
||||
border-bottom: 1px #5d627d solid;
|
||||
}
|
||||
|
||||
.border-top-default {
|
||||
border-top: 1px #5d627d solid;
|
||||
}
|
||||
|
||||
.background-color-night {
|
||||
background-color: #34384f;
|
||||
}
|
||||
|
||||
/*
|
||||
.background-color-hover-night-light:hover {
|
||||
background-color: #363a51;
|
||||
}
|
||||
*/
|
||||
|
||||
.background-color-red {
|
||||
background-color: #9b4a4a;
|
||||
}
|
||||
|
||||
.cursor-default {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.cursor-help {
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.font-width-normal {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.font-size-10 {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.font-size-12 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.font-size-22 {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.padding-0 {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.padding-x-0 {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.padding-4 {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.padding-t-4 {
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.padding-y-4 {
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.padding-x-4 {
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.padding-x-8 {
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.padding-8 {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.padding-t-8 {
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.padding-b-8 {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.padding-y-8 {
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.padding-b-16 {
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.padding-t-16 {
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
.padding-y-16 {
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.padding-x-16 {
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
.padding-16 {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.margin-l-4 {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.margin-l-8 {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.margin-l-16 {
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
.margin-x-4 {
|
||||
margin-left: 4px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.margin-r-4 {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.margin-r-8 {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.margin-l-12 {
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.margin-y-8 {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.margin-t-8 {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.margin-b-8 {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.margin-t-16 {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.margin-b-16 {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.margin-b-24 {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.display-block {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.opacity-0 {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.opacity-06 {
|
||||
opacity: .6;
|
||||
}
|
||||
|
||||
.opacity-hover-1:hover {
|
||||
opacity: 1;
|
||||
transition: opacity .2s;
|
||||
}
|
||||
|
||||
*:hover > .parent-hover-opacity-09 {
|
||||
opacity: .9;
|
||||
transition: opacity .2s;
|
||||
}
|
||||
|
||||
.blur-2 {
|
||||
filter: blur(2px);
|
||||
}
|
||||
|
||||
.blur-hover-0:hover {
|
||||
filter: blur(0);
|
||||
}
|
||||
|
||||
/* responsive rules */
|
||||
|
||||
.width-100 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.width-50 {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.width-13px {
|
||||
width: 13px;
|
||||
}
|
||||
|
||||
@media (max-width: 1220px) {
|
||||
|
||||
.width-tablet-100 {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 512px) {
|
||||
|
||||
.width-mobile-100 {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,335 +0,0 @@
|
|||
<?php
|
||||
|
||||
// Bootstrap
|
||||
require_once __DIR__ . '/../config/bootstrap.php';
|
||||
|
||||
// Define response
|
||||
$response = (object)
|
||||
[
|
||||
'success' => true,
|
||||
'message' => _('Internal server error'),
|
||||
'html' => (object)
|
||||
[
|
||||
'title' => sprintf(_('Oops - %s'), WEBSITE_NAME),
|
||||
'h1' => false,
|
||||
'link' => (object) [],
|
||||
]
|
||||
];
|
||||
|
||||
// Yggdrasil connections only
|
||||
if (!Valid::host($_SERVER['REMOTE_ADDR']))
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = _('Yggdrasil connection required for this action');
|
||||
}
|
||||
|
||||
// Init session
|
||||
else if (!$userId = $db->initUserId($_SERVER['REMOTE_ADDR'], USER_DEFAULT_APPROVED, time()))
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = _('Could not init user session');
|
||||
}
|
||||
|
||||
// Magnet exists
|
||||
else if (!$magnet = $db->getMagnet(isset($_GET['magnetId']) && $_GET['magnetId'] > 0 ? (int) $_GET['magnetId'] : 0))
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = _('Requested magnet not found');
|
||||
}
|
||||
|
||||
// Access allowed
|
||||
else if (!($_SERVER['REMOTE_ADDR'] == $db->getUser($magnet->userId)->address || in_array($_SERVER['REMOTE_ADDR'], MODERATOR_IP_LIST) || ($magnet->public && $magnet->approved))) {
|
||||
|
||||
$response->success = false;
|
||||
$response->message = _('Magnet not available for this action');
|
||||
}
|
||||
|
||||
// Get user
|
||||
else if (!$user = $db->getUser($userId))
|
||||
{
|
||||
$response->success = false;
|
||||
$response->message = _('Could not init user info');
|
||||
}
|
||||
|
||||
// On first visit, redirect user to the welcome page with access level question
|
||||
else if (is_null($user->public))
|
||||
{
|
||||
header(
|
||||
sprintf('Location: %s/welcome.php', WEBSITE_URL)
|
||||
);
|
||||
}
|
||||
|
||||
// Request valid
|
||||
else
|
||||
{
|
||||
// Register magnet download
|
||||
if ($magnetDownloadId = $db->addMagnetDownload($magnet->magnetId, $user->userId, time()))
|
||||
{
|
||||
// Push event to other nodes
|
||||
if (API_EXPORT_ENABLED &&
|
||||
API_EXPORT_PUSH_ENABLED &&
|
||||
API_EXPORT_USERS_ENABLED &&
|
||||
API_EXPORT_MAGNETS_ENABLED &&
|
||||
API_EXPORT_MAGNET_DOWNLOADS_ENABLED)
|
||||
{
|
||||
if (!$memoryApiExportPush = $memory->get('api.export.push'))
|
||||
{
|
||||
$memoryApiExportPush = [];
|
||||
}
|
||||
|
||||
$memoryApiExportPush[] = (object)
|
||||
[
|
||||
'time' => time(),
|
||||
'userId' => $user->userId,
|
||||
'magnetId' => $magnet->magnetId,
|
||||
'magnetDownloadId' => $magnetDownloadId
|
||||
];
|
||||
|
||||
$memory->set('api.export.push', $memoryApiExportPush, 3600);
|
||||
}
|
||||
}
|
||||
|
||||
// Build magnet link
|
||||
$link = (object)
|
||||
[
|
||||
'magnet' => [],
|
||||
'direct' => [],
|
||||
];
|
||||
|
||||
/// Exact Topic
|
||||
$xt = [];
|
||||
|
||||
foreach ($db->findMagnetToInfoHashByMagnetId($magnet->magnetId) as $result)
|
||||
{
|
||||
if ($infoHash = $db->getInfoHash($result->infoHashId))
|
||||
{
|
||||
switch ($infoHash->version)
|
||||
{
|
||||
case 1:
|
||||
|
||||
$xt[] = sprintf('xt=urn:btih:%s', $infoHash->value);
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
|
||||
$xt[] = sprintf('xt=urn:btmh:1220%s', $infoHash->value);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$link->magnet[] = sprintf('magnet:?%s', implode('&', $xt));
|
||||
|
||||
/// Display Name
|
||||
$link->magnet[] = sprintf('dn=%s', urlencode($magnet->dn));
|
||||
|
||||
// Keyword Topic
|
||||
$kt = [];
|
||||
|
||||
foreach ($db->findKeywordTopicByMagnetId($magnet->magnetId) as $result)
|
||||
{
|
||||
$kt[] = urlencode($db->getKeywordTopic($result->keywordTopicId)->value);
|
||||
}
|
||||
|
||||
$link->magnet[] = sprintf('kt=%s', implode('+', $kt));
|
||||
|
||||
/// Address Tracker
|
||||
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;
|
||||
}
|
||||
|
||||
$link->magnet[] = sprintf('tr=%s', urlencode($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)));
|
||||
}
|
||||
|
||||
// Append trackers.json
|
||||
foreach (json_decode(file_get_contents(__DIR__ . '/../config/trackers.json')) as $tracker)
|
||||
{
|
||||
$link->magnet[] = sprintf('tr=%s', urlencode($tracker->announce));
|
||||
}
|
||||
|
||||
/// Acceptable Source
|
||||
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;
|
||||
}
|
||||
|
||||
$link->magnet[] = sprintf('as=%s', urlencode($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)));
|
||||
$link->direct[] = $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
|
||||
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;
|
||||
}
|
||||
|
||||
$link->magnet[] = sprintf('xs=%s', urlencode($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)));
|
||||
}
|
||||
|
||||
// Return html
|
||||
$response->html->title = sprintf(
|
||||
_('%s - Download - %s'),
|
||||
htmlentities($magnet->title),
|
||||
WEBSITE_NAME
|
||||
);
|
||||
|
||||
$response->html->h1 = htmlentities($magnet->title);
|
||||
|
||||
// @TODO implement .bittorrent, separated v1/v2 magnet links
|
||||
$response->html->link->magnet = implode('&', array_unique($link->magnet));
|
||||
$response->html->link->direct = $link->direct;
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="<?php echo WEBSITE_URL ?>/assets/theme/default/css/common.css?<?php echo WEBSITE_CSS_VERSION ?>" />
|
||||
<link rel="stylesheet" type="text/css" href="<?php echo WEBSITE_URL ?>/assets/theme/default/css/framework.css?<?php echo WEBSITE_CSS_VERSION ?>" />
|
||||
<title>
|
||||
<?php echo $response->html->title ?>
|
||||
</title>
|
||||
<meta name="robots" content="noindex,nofollow"/>
|
||||
<meta name="author" content="YGGtracker" />
|
||||
<meta charset="UTF-8" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div class="container">
|
||||
<div class="row margin-t-8 text-center">
|
||||
<a class="logo" href="<?php echo WEBSITE_URL ?>"><?php echo str_replace('YGG', '<span>YGG</span>', WEBSITE_NAME) ?></a>
|
||||
<form class="margin-t-8" name="search" method="get" action="<?php echo WEBSITE_URL ?>/index.php">
|
||||
<input type="text" name="query" value="" placeholder="<?php echo _('search or submit magnet link') ?>" />
|
||||
<input type="submit" value="<?php echo _('submit') ?>" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<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">
|
||||
<?php if ($response->success) { ?>
|
||||
<div class="text-center">
|
||||
<h1 class="display-block margin-b-16 font-size-16"><?php echo $response->html->h1 ?></h1>
|
||||
<div class="margin-b-16 text-color-night">
|
||||
<?php echo _('* make sure BitTorrent client listen Yggdrasil interface!') ?>
|
||||
</div>
|
||||
<a class="padding-x-4" href="<?php echo $response->html->link->magnet ?>" title="<?php echo _('Magnet') ?>">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-magnet" viewBox="0 0 16 16">
|
||||
<path d="M8 1a7 7 0 0 0-7 7v3h4V8a3 3 0 0 1 6 0v3h4V8a7 7 0 0 0-7-7Zm7 11h-4v3h4v-3ZM5 12H1v3h4v-3ZM0 8a8 8 0 1 1 16 0v8h-6V8a2 2 0 1 0-4 0v8H0V8Z"/>
|
||||
</svg>
|
||||
</a>
|
||||
<?php foreach ($response->html->link->direct as $direct) { ?>
|
||||
<a class="padding-x-4" href="<?php echo $direct ?>" title="<?php echo _('Direct') ?>">
|
||||
<svg 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>
|
||||
</a>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<?php } else { ?>
|
||||
<div class="text-center">
|
||||
<?php echo $response->message ?>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php if (!empty($_SERVER['HTTP_REFERER']) && false !== strpos($_SERVER['HTTP_REFERER'], WEBSITE_URL)) { ?>
|
||||
<div class="row">
|
||||
<div class="column width-100 text-right">
|
||||
<a class="button margin-l-8"
|
||||
rel="nofollow"
|
||||
href="<?php echo $_SERVER['HTTP_REFERER'] ?>">
|
||||
<?php echo _('back') ?>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</main>
|
||||
<footer>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="column width-100 text-center margin-y-8">
|
||||
<?php foreach (json_decode(file_get_contents(__DIR__ . '/../config/trackers.json')) as $i => $tracker) { ?>
|
||||
<?php if (!empty($tracker->announce) && !empty($tracker->stats)) { ?>
|
||||
<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 } ?>
|
||||
<?php } ?>
|
||||
<a href="<?php echo WEBSITE_URL ?>/faq.php"><?php echo _('F.A.Q') ?></a>
|
||||
|
|
||||
<a href="<?php echo WEBSITE_URL ?>/node.php"><?php echo _('Node') ?></a>
|
||||
|
|
||||
<a rel="nofollow" href="<?php echo WEBSITE_URL ?>/index.php?rss"><?php echo _('RSS') ?></a>
|
||||
<?php if (API_EXPORT_ENABLED) { ?>
|
||||
|
|
||||
<a rel="nofollow" href="<?php echo WEBSITE_URL ?>/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>
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue