diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4a6ffcd --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.vscode +storage/* \ No newline at end of file diff --git a/README.md b/README.md index cc8c467..149a88c 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,129 @@ # yggtracker-wanted-torrents-receiver -Crontab script that allows to receive wanted torrents from multiple YGGtracker nodes + +Crontab script that allows to receive wanted torrents from multiple [YGGtracker](https://github.com/YGGverse/YGGtracker) nodes + +#### Install + +Latest version of toolkit could be installed with single command: + +`git clone https://github.com/YGGverse/yggtracker-wanted-torrents-receiver.git` + +#### Config + +All configuration files placed at `/config` folder + +##### local.json + +``` +{ + "import": + { + // Common rules for FTP connections + "ftp": + { + // How many of seconds wait for each provider response + "timeout":5, + // Which of remote folders grab to + // array of target directories for each provider, eg. all, sensitive/no, locale/en, etc + "directories": + [ + "all" + ] + }, + // Requirements filter before import something from providers + "require": + { + // Require approved torrents import only (according to provide.approved option in remote.json for each provider) + "approved":true + }, + // Local storage settings for imported content + "storage": + { + // Storage directory by default, feel free to change like `/home/qbittorrent-nox/import` + "directory":"storage", + + // Copy all torrents imported from providers folders to the storage/_common/{hash}.torrent + // This mode check files MD5 hash sum to prevent duplicates in single folder from different providers + // Useful when bittorrent client does not support support listening of multiple folders, recursive mode + "common":true + } + }, + "update": + { + "config": + { + // This option allows to auto-update remote.json file on the fly without updating codebase with git clone + "remote": + { + // If disabled, local file will not be updated, but manually + "enabled": true, + + // Don't worry, just this repository + "repository":"https://raw.githubusercontent.com/YGGverse/yggtracker-wanted-torrents-receiver/main/config/remote.json", + + // How many seconds to wait before ask repository for remote.json updates (after last file write) + "cache":86400 + } + } + } +} +``` + +##### remote.json + +The remote configuration contains available YGGtracker providers. + +File also could be auto-updated from this repository when owner enabled `update.config.remote.enabled` option in `local.json` +that makes registry actualization simpler for recipients and providers, as update details without `git pull`. + +``` +[ + { + "description": + { + "name":"YGGtracker", // Used as storage subfolder + "description":"YGGtracker official node", // Just provider description + "url":"http://[201:23b4:991a:634d:8359:4521:5576:15b7]/yggtracker/" // Provider's website + }, + "ftp": + { + "host":"201:23b4:991a:634d:8359:4521:5576:15b7", // Connection host + "port":21, // Connection port + "passive":true, // Recommended passive mode for better compatibility + "username":"anonymous", // YGGtracker instances usually provides public FTP access + "password":"anonymous", + "directory":"/yggtracker/torrents/wanted" // Directory where wanted torrent files placed + }, + "provide": + { + "approved":true // Tells to receiver that node administrator check the torrents before send to API + } + } +], +... +``` + +#### Usage + +`php src/receiver.php` + +or add to crontab: + +`* * * * * /usr/bin/php src/receiver.php > /dev/null 2>&1` + +#### Bash, python? + +Feel free to contribute! + +#### Add new YGGtracker node + +Just send PR to nodes.json file + +#### Feedback + +Any questions and bug reports, please send to the [Issues](https://github.com/YGGverse/yggtracker-wanted-torrents-receiver/issues)! + +#### See also + +* [YGGtracker - BitTorrent Network for Yggdrasil](https://github.com/YGGverse/YGGtracker) +* [YGGtracker Search Plugin for qBittorrent](https://github.com/YGGverse/qbittorrent-yggtracker-search-plugin) \ No newline at end of file diff --git a/config/local.json b/config/local.json new file mode 100644 index 0000000..2e6c550 --- /dev/null +++ b/config/local.json @@ -0,0 +1,34 @@ +{ + "import": + { + "ftp": + { + "timeout":5, + "directories": + [ + "all" + ] + }, + "require": + { + "approved":true + }, + "storage": + { + "directory":"storage", + "common":true + } + }, + "update": + { + "config": + { + "remote": + { + "enabled": true, + "repository":"https://raw.githubusercontent.com/YGGverse/yggtracker-wanted-torrents-receiver/main/config/remote.json", + "cache":86400 + } + } + } +} \ No newline at end of file diff --git a/config/remote.json b/config/remote.json new file mode 100644 index 0000000..c82b28c --- /dev/null +++ b/config/remote.json @@ -0,0 +1,23 @@ +[ + { + "description": + { + "name":"YGGtracker", + "description":"YGGtracker official node", + "url":"http://[201:23b4:991a:634d:8359:4521:5576:15b7]/yggtracker/" + }, + "ftp": + { + "host":"201:23b4:991a:634d:8359:4521:5576:15b7", + "port":21, + "passive":true, + "username":"anonymous", + "password":"anonymous", + "directory":"/yggtracker/torrents/wanted" + }, + "provide": + { + "approved":true + } + } +] \ No newline at end of file diff --git a/src/cli.php b/src/cli.php new file mode 100644 index 0000000..6a4f1d1 --- /dev/null +++ b/src/cli.php @@ -0,0 +1,122 @@ +update->config->remote->enabled) +{ + // Reset remote config cache + if ($local->update->config->remote->cache + (int) filectime(__DIR__ . '/../config/remote.json') < time()) + { + // Cache results + if ($result = @file_get_contents($local->update->config->remote->repository)) + { + file_put_contents( + __DIR__ . '/../config/remote.json', + $result + ); + } + } +} + +// Sync remotes +foreach ( + json_decode( + file_get_contents(__DIR__ . '/../config/remote.json') + ) as $remote) +{ + // Apply approved filters + if ($local->import->require->approved && !$remote->provide->approved) + { + continue; + } + + // Connect remote + if (!$connection = ftp_connect($remote->ftp->host, $remote->ftp->port, $local->import->ftp->timeout)) + { + continue; + } + + // Login + if (!ftp_login($connection, $remote->ftp->username, $remote->ftp->password)) + { + continue; + } + + // Apply passive mode if required + if (!ftp_pasv($connection, $remote->ftp->passive)) + { + continue; + } + + // Navigate to wanted directory + if (!ftp_chdir($connection, $remote->ftp->directory)) + { + continue; + } + + // Scan directories + foreach ($local->import->ftp->directories as $directory) + { + // Get torrents + foreach (ftp_nlist($connection, $directory) as $torrent) + { + // Init provider directory + @mkdir( + $local->import->storage->directory . '/' . $remote->description->name . '/' . $directory, + 0755, + true + ); + + // Save torrents + ftp_get( + $connection, + $local->import->storage->directory . '/' . $remote->description->name . '/' . $torrent, + $torrent + ); + + // Common storage mode enabled + if ($local->import->storage->common) + { + // Init common folder + @mkdir( + $local->import->storage->directory . '/_common', + 0755, + true + ); + + // Prevent same file duplicates from different providers + $hash = md5_file( + $local->import->storage->directory . '/' . $remote->description->name . '/' . $torrent + ); + + // Copy torrent file into the common directory if not exists yet + if (!file_exists($local->import->storage->directory . '/_common/' . $hash . '.torrent')) + { + copy( + $local->import->storage->directory . '/' . $remote->description->name . '/' . $torrent, + $local->import->storage->directory . '/_common/' . $hash . '.torrent' + ); + } + } + } + } +} \ No newline at end of file