commit c8bbc35886ef354ae4cfb7af19fe858f27b4d6af Author: Clément Date: Mon Feb 22 16:38:47 2021 +0100 :tada: Hello world diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..23d2d48 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +/.idea +/main.php +/test.jpg +/pool/* +!/pool/.gitkeep +/vendor +/config.json diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..08e27f5 --- /dev/null +++ b/composer.json @@ -0,0 +1,21 @@ +{ + "name": "shikiryu/webgobbler", + "description": "WebGobbler in PHP", + "type": "project", + "authors": [ + { + "name": "Clément", + "email": "clement@desmidt.fr" + } + ], + "require": { + "fabpot/goutte": "^4.0", + "ext-imagick": "*" + }, + "autoload": { + "psr-4": { + "Shikiryu\\WebGobbler\\": "src/", + "Shikiryu\\WebGobbler\\Collector\\": "src/Collector" + } + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..70317ed --- /dev/null +++ b/composer.lock @@ -0,0 +1,1348 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "d22eac6a3ef39dd7fd1941592118e42a", + "packages": [ + { + "name": "fabpot/goutte", + "version": "v4.0.1", + "source": { + "type": "git", + "url": "https://github.com/FriendsOfPHP/Goutte.git", + "reference": "293e754f0be2f1e85f9b31262cb811de39874e03" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FriendsOfPHP/Goutte/zipball/293e754f0be2f1e85f9b31262cb811de39874e03", + "reference": "293e754f0be2f1e85f9b31262cb811de39874e03", + "shasum": "" + }, + "require": { + "php": ">=7.1.3", + "symfony/browser-kit": "^4.4|^5.0", + "symfony/css-selector": "^4.4|^5.0", + "symfony/dom-crawler": "^4.4|^5.0", + "symfony/http-client": "^4.4|^5.0", + "symfony/mime": "^4.4|^5.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^5.0" + }, + "type": "application", + "autoload": { + "psr-4": { + "Goutte\\": "Goutte" + }, + "exclude-from-classmap": [ + "Goutte/Tests" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "A simple PHP Web Scraper", + "homepage": "https://github.com/FriendsOfPHP/Goutte", + "keywords": [ + "scraper" + ], + "support": { + "issues": "https://github.com/FriendsOfPHP/Goutte/issues", + "source": "https://github.com/FriendsOfPHP/Goutte/tree/v4.0.1" + }, + "time": "2020-10-14T06:49:09+00:00" + }, + { + "name": "psr/container", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/master" + }, + "time": "2017-02-14T16:28:37+00:00" + }, + { + "name": "psr/log", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc", + "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.3" + }, + "time": "2020-03-23T09:12:05+00:00" + }, + { + "name": "symfony/browser-kit", + "version": "v5.2.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/browser-kit.git", + "reference": "b03b2057ed53ee4eab2e8f372084d7722b7b8ffd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/b03b2057ed53ee4eab2e8f372084d7722b7b8ffd", + "reference": "b03b2057ed53ee4eab2e8f372084d7722b7b8ffd", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/dom-crawler": "^4.4|^5.0" + }, + "require-dev": { + "symfony/css-selector": "^4.4|^5.0", + "symfony/http-client": "^4.4|^5.0", + "symfony/mime": "^4.4|^5.0", + "symfony/process": "^4.4|^5.0" + }, + "suggest": { + "symfony/process": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\BrowserKit\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/browser-kit/tree/v5.2.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-01-27T12:56:27+00:00" + }, + { + "name": "symfony/css-selector", + "version": "v5.2.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "f65f217b3314504a1ec99c2d6ef69016bb13490f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/f65f217b3314504a1ec99c2d6ef69016bb13490f", + "reference": "f65f217b3314504a1ec99c2d6ef69016bb13490f", + "shasum": "" + }, + "require": { + "php": ">=7.2.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Converts CSS selectors to XPath expressions", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/css-selector/tree/v5.2.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-01-27T10:01:46+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v2.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5fa56b4074d1ae755beb55617ddafe6f5d78f665", + "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/master" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-09-07T11:33:47+00:00" + }, + { + "name": "symfony/dom-crawler", + "version": "v5.2.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/dom-crawler.git", + "reference": "5d89ceb53ec65e1973a555072fac8ed5ecad3384" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/5d89ceb53ec65e1973a555072fac8ed5ecad3384", + "reference": "5d89ceb53ec65e1973a555072fac8ed5ecad3384", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php80": "^1.15" + }, + "conflict": { + "masterminds/html5": "<2.6" + }, + "require-dev": { + "masterminds/html5": "^2.6", + "symfony/css-selector": "^4.4|^5.0" + }, + "suggest": { + "symfony/css-selector": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\DomCrawler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases DOM navigation for HTML and XML documents", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/dom-crawler/tree/v5.2.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-01-27T10:01:46+00:00" + }, + { + "name": "symfony/http-client", + "version": "v5.2.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client.git", + "reference": "22cb1a7844fff206cc5186409776e78865405ea5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client/zipball/22cb1a7844fff206cc5186409776e78865405ea5", + "reference": "22cb1a7844fff206cc5186409776e78865405ea5", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/log": "^1.0", + "symfony/http-client-contracts": "^2.2", + "symfony/polyfill-php73": "^1.11", + "symfony/polyfill-php80": "^1.15", + "symfony/service-contracts": "^1.0|^2" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "1.0", + "symfony/http-client-implementation": "1.1" + }, + "require-dev": { + "amphp/amp": "^2.5", + "amphp/http-client": "^4.2.1", + "amphp/http-tunnel": "^1.0", + "amphp/socket": "^1.1", + "guzzlehttp/promises": "^1.4", + "nyholm/psr7": "^1.0", + "php-http/httplug": "^1.0|^2.0", + "psr/http-client": "^1.0", + "symfony/dependency-injection": "^4.4|^5.0", + "symfony/http-kernel": "^4.4.13|^5.1.5", + "symfony/process": "^4.4|^5.0", + "symfony/stopwatch": "^4.4|^5.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-client/tree/v5.2.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-01-27T10:15:41+00:00" + }, + { + "name": "symfony/http-client-contracts", + "version": "v2.3.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client-contracts.git", + "reference": "41db680a15018f9c1d4b23516059633ce280ca33" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/41db680a15018f9c1d4b23516059633ce280ca33", + "reference": "41db680a15018f9c1d4b23516059633ce280ca33", + "shasum": "" + }, + "require": { + "php": ">=7.2.5" + }, + "suggest": { + "symfony/http-client-implementation": "" + }, + "type": "library", + "extra": { + "branch-version": "2.3", + "branch-alias": { + "dev-main": "2.3-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\HttpClient\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to HTTP clients", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/http-client-contracts/tree/v2.3.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-14T17:08:19+00:00" + }, + { + "name": "symfony/mime", + "version": "v5.2.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "7dee6a43493f39b51ff6c5bb2bd576fe40a76c86" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/7dee6a43493f39b51ff6c5bb2bd576fe40a76c86", + "reference": "7dee6a43493f39b51ff6c5bb2bd576fe40a76c86", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0", + "symfony/polyfill-php80": "^1.15" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/mailer": "<4.4" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/dependency-injection": "^4.4|^5.0", + "symfony/property-access": "^4.4|^5.1", + "symfony/property-info": "^4.4|^5.1", + "symfony/serializer": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows manipulating MIME messages", + "homepage": "https://symfony.com", + "keywords": [ + "mime", + "mime-type" + ], + "support": { + "source": "https://github.com/symfony/mime/tree/v5.2.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-02-02T06:10:15+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "c6c942b1ac76c82448322025e084cadc56048b4e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/c6c942b1ac76c82448322025e084cadc56048b4e", + "reference": "c6c942b1ac76c82448322025e084cadc56048b4e", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.22.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-01-07T16:49:33+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "2d63434d922daf7da8dd863e7907e67ee3031483" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/2d63434d922daf7da8dd863e7907e67ee3031483", + "reference": "2d63434d922daf7da8dd863e7907e67ee3031483", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "symfony/polyfill-intl-normalizer": "^1.10", + "symfony/polyfill-php72": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.22.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-01-22T09:19:47+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/43a0283138253ed1d48d352ab6d0bdb3f809f248", + "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.22.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-01-22T09:19:47+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "5232de97ee3b75b0360528dae24e73db49566ab1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/5232de97ee3b75b0360528dae24e73db49566ab1", + "reference": "5232de97ee3b75b0360528dae24e73db49566ab1", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.22.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-01-22T09:19:47+00:00" + }, + { + "name": "symfony/polyfill-php72", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9", + "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php72\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php72/tree/v1.22.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-01-07T16:49:33+00:00" + }, + { + "name": "symfony/polyfill-php73", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/a678b42e92f86eca04b7fa4c0f6f19d097fb69e2", + "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php73\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.22.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-01-07T16:49:33+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.22.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dc3063ba22c2a1fd2f45ed856374d79114998f91", + "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.22-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.22.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-01-07T16:49:33+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v2.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d15da7ba4957ffb8f1747218be9e1a121fd298a1", + "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/container": "^1.0" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/master" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-09-07T11:33:47+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.0.0" +} diff --git a/config.json.template b/config.json.template new file mode 100644 index 0000000..5a096a1 --- /dev/null +++ b/config.json.template @@ -0,0 +1,16 @@ +{ + "collectors": [], + "pool": { + "directory": "/home/user/webcollage/pool", + "nb_images": 10 + }, + "assembler": { + "sizex": 2000, + "sizey": 2000, + "mirror": false, + "emboss": true, + "invert": false, + "nbx": 5, + "nby": 5 + } +} \ No newline at end of file diff --git a/pool/.gitkeep b/pool/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/Assembler.php b/src/Assembler.php new file mode 100644 index 0000000..27f3492 --- /dev/null +++ b/src/Assembler.php @@ -0,0 +1,37 @@ +pool = $pool; + $this->config = $config; + } + + /** + * @param string $file + * + * @return void + */ + abstract public function saveTo($file); + + abstract public function display(); +} \ No newline at end of file diff --git a/src/Assembler/Mosaic.php b/src/Assembler/Mosaic.php new file mode 100644 index 0000000..ecb0330 --- /dev/null +++ b/src/Assembler/Mosaic.php @@ -0,0 +1,48 @@ +setColorspace(\Imagick::COLORSPACE_RGB); + $final_image->newImage($this->config->get('assembler.sizex'), $this->config->get('assembler.sizey'), 'none'); + $image_size_x = $this->config->get('assembler.sizex') / $this->config->get('assembler.nbx'); + $image_size_y = $this->config->get('assembler.sizey') / $this->config->get('assembler.nby'); + for ($y = 0; $y < $this->config->get('assembler.nby'); $y++) { + for ($x = 0; $x < $this->config->get('assembler.nbx'); $x++) { + $image = new \Imagick($this->pool->getImage()); + if ($image->getColorspace() !== \Imagick::COLORSPACE_RGB) { + $image->setColorspace(\Imagick::COLORSPACE_RGB); + } + $image->scaleImage($image_size_x, $image_size_y, true); + $final_image->compositeImage($image, \Imagick::COMPOSITE_DEFAULT, $x * $image_size_x, $y * $image_size_y); + } + } + if (true === $this->config->get('assembler.mirror')) { + $final_image->flopImage(); + } + if (true === $this->config->get('assembler.emboss')) { + $final_image->embossImage(0, 1); + } + if (true === $this->config->get('assembler.invert')) { + $final_image->negateImage(false); + } + $final_image->writeImage($file); + } + + public function display() + { + // TODO: Implement display() method. + } +} \ No newline at end of file diff --git a/src/Assembler/Simple.php b/src/Assembler/Simple.php new file mode 100644 index 0000000..e786787 --- /dev/null +++ b/src/Assembler/Simple.php @@ -0,0 +1,44 @@ +pool->getImage()); + if ($image->getColorspace() !== \Imagick::COLORSPACE_RGB) { + $image->setColorspace(\Imagick::COLORSPACE_RGB); + } + $size = $image->getSize(); + $imagex = $size['columns']; + $imagey = $size['rows']; + if ($imagex !== $this->config->get('assembler.sizex') || $imagey !== $this->config->get('assembler.sizey')) { + $image->thumbnailImage($this->config->get('assembler.sizex'), $this->config->get('assembler.sizey')); + } + if (true === $this->config->get('assembler.mirror')) { + $image->flopImage(); + } + if (true === $this->config->get('assembler.emboss')) { + $image->embossImage(0, 1); + } + if (true === $this->config->get('assembler.invert')) { + $image->flipImage(); + } + $image->writeImage($file); + } catch (\ImagickException $e) { + echo $e->getMessage(); + } + } + + public function display() + { + // TODO: Implement display() method. + } +} \ No newline at end of file diff --git a/src/Assembler/Superpose.php b/src/Assembler/Superpose.php new file mode 100644 index 0000000..3d812db --- /dev/null +++ b/src/Assembler/Superpose.php @@ -0,0 +1,177 @@ +pool->getImage()); + $image_x = $image_to_superpose->getImageWidth(); + $image_y = $image_to_superpose->getImageHeight(); + if ($image_x < 32 || $image_y < 32) { + throw new \Exception('Image too small.'); + } + + $this->current_image = $image_to_superpose; + + $nb_images = $this->config->get('assembler.superpose.min_num_images', 5); + for ($i = 0; $i < $nb_images; $i++) { + $this->current_image = $this->superpose(); + } + $this->current_image->writeImage($file); + } + + public function superpose() + { + return $this->superposeOneImage($this->current_image, new \Imagick($this->pool->getImage())); + } + + /** + * Superposes one image in the current image. + * This method must only be called by the assembler_superpose thread ! + * + * @param \Imagick $current_image + * @param \Imagick $image_to_superpose + * + * @return \Imagick + * @throws \Exception + */ + private function superposeOneImage($current_image, $image_to_superpose) + { + if ($this->config->get('assembler.superpose.variante', 0) === 1) { + $current_image->brightnessContrastImage(0.99, 1); + } + if ($image_to_superpose->getColorspace() !== \Imagick::COLORSPACE_RGB) { + $image_to_superpose->setColorspace(\Imagick::COLORSPACE_RGB); + } + + # If the image is bigger than current image, scale it down to 1/2 of final picture dimensions + # (while keeping its ratio) + $image_x = $image_to_superpose->getImageWidth(); + $image_y = $image_to_superpose->getImageHeight(); + if ($image_x > $this->config->get('assembler.sizex') || $image_y > $this->config->get('assembler.sizey')) { + try { + $image_to_superpose->scaleImage($this->config->get('assembler.sizex') / 2, $this->config->get('assembler.sizey') / 2, true); + } catch (\ImagickException $e) { + } + } + + # Scale down/up image if required. + /*scaleValue = self.CONFIG["assembler.superpose.scale"] + if str(scaleValue) != "1.0": + try: + imageToSuperpose.thumbnail((int(float(imagex)*scaleValue),int(float(imagey)*scaleValue)),Image.ANTIALIAS) + except TypeError: #TypeError: unsubscriptable object ; Spurious exception in PIL. :-( + raise BadImage + (imagex,imagey) = imageToSuperpose.size*/ + + # Compensate for poorly-contrasted images on the web + /*try: + imageToSuperpose = ImageOps.autocontrast(imageToSuperpose) + except TypeError: # Aaron tells me that this exception occurs with PNG images. + raise BadImage*/ + + # Some image are too white. + # For example, the photo of a coin on a white background. + # These picture degrad the quality of the final image. + # We try to dectect them by summing the value of the pixels + # on the borders. + # If the image is considered "white", we invert it. + /*pixelcount = 1 # 1 to prevent divide by zero error. + valuecount = 0 + try: + for x in range(0,imagex,20): + (r,g,b) = imageToSuperpose.getpixel((x,5)) + valuecount += r+g+b + (r,g,b) = imageToSuperpose.getpixel((x,imagey-5)) + valuecount += r+g+b + pixelcount += 2 + for y in range(0,imagey,20): + (r,g,b) = imageToSuperpose.getpixel((5,y)) + valuecount += r+g+b + (r,g,b) = imageToSuperpose.getpixel((imagex-5,y)) + valuecount += r+g+b + pixelcount += 2 + except TypeError: #unsubscriptable object Arrggghh... not again ! + raise BadImage # Aggrrreeeuuuu... + + # If the average r+g+b of the border pixels exceed this value, + # we consider the image is too white, and we invert it. + if (100*(valuecount/(255*3))/pixelcount)>60: # Cut at 60%. (100% is RGB=(255,255,255)) + imageToSuperpose = ImageOps.invert(imageToSuperpose)*/ + + $paste_coords_x = random_int(-$image_x, $this->config->get('assembler.sizex')); + $paste_coords_y = random_int(-$image_y, $this->config->get('assembler.sizey')); + + # Darken image borders + $image_to_superpose = $this->darkenImageBorder($image_to_superpose, 30); + + if ($this->config->get('assembler.superpose.randomrotation', false)) { + $image_to_superpose->rotateImage('none', random_int(0, 359)); + $image_to_superpose = $this->darkenImageBorder($image_to_superpose, 30); + } + +// mask_image = ImageOps.autocontrast(imageToSuperpose.convert('L')) + +// if (self.CONFIG["assembler.superpose.variante"]==1) and (random.randint(0,100)<5): # Invert the transparency of 5% of the images (Except if we are in variante 1 mode) +// mask_image = ImageOps.invert(mask_image) + $current_image->compositeImage($image_to_superpose, \Imagick::COMPOSITE_DEFAULT, $paste_coords_x, $paste_coords_y); + + if ($this->config->get('assembler.superpose.variante') === 0) { + $current_image->equalizeImage(); + } else { + $current_image->autoLevelImage(); + } + + return $current_image; + } + + /** + * @param \Imagick $image + * @param int|null $border_size + * + * @return \Imagick + */ + private function darkenImageBorder(Imagick $image, $border_size = null) + { + if (null === $border_size) { + $border_size = $this->config->get('assembler.superpose.bordersmooth'); + } + + $image_x = $image->getImageWidth(); + $image_y = $image->getImageHeight(); + + for ($i = 0; $i < $border_size; $i++) { + $draw = new \ImagickDraw(); + $draw->setStrokeOpacity(($border_size-$i)/$border_size); + $draw->setStrokeWidth(1); + $draw->setStrokeColor('black'); + $draw->setFillColor('none'); + $draw->rectangle($i, $i, $image_x - $i, $image_y - $i); + $image->drawImage($draw); + } + + return $image; + } + + public function display() + { + // TODO: Implement display() method. + } +} \ No newline at end of file diff --git a/src/Collector.php b/src/Collector.php new file mode 100644 index 0000000..7a0b9bd --- /dev/null +++ b/src/Collector.php @@ -0,0 +1,120 @@ +pool = $pool; + } + + /** + * @return string + */ + abstract public function getName(); + + /** + * @return string + */ + public function getPoolDirectory() + { + return sprintf('%s/%s', $this->pool->getPoolDirectory(), $this->getName()); + } + + /** + * @return int + */ + abstract public function getRandomImage(); + + /** + * @param int $number + * + * @return int + */ + abstract public function getRandomImages(int $number); + + + /** + * Generates a random word. + * This method can be used by all derived classes. + * Usefull to get random result from search engines when you do not have + * a dictionnary at hand. + * The generated word can be a number (containing only digits), + * a word (containing only letters) or both mixed. + * Output: string (a random word) + * + * @return string + */ + protected function generateRandomWord() + { + $word = '1'; + try { + if (random_int(0, 100) < 30) { // Sometimes use only digits + if (random_int(0, 100) < 30) { + $word = random_int(1, 999); + } else { + $word = random_int(1, 999999); + } + } else { // Generate a word containing letters + $word = ''; + $charset = 'abcdefghijklmnopqrstuvwxyz'; // Search for random word containing letter only. + if (random_int(0, 100) < 60) { // Sometimes include digits with letters + $charset = 'abcdefghijklmnopqrstuvwxyz' . 'abcdefghijklmnopqrstuvwxyz' . '0123456789'; // *2 to have more letters than digits + } + $charset = str_split($charset); + for ($i = 0, $l = random_int(2, 5); $i <= $l; $i++) { // Only generate short words (2 to 5 characters) + $word .= $charset[array_rand($charset)]; + } + } + } catch (\Exception $e) { + } + + return $word; + } + + /** + * @param array $parts + * + * @return string + */ + protected function reverse_url(array $parts) { + if (array_key_exists('query', $parts) && is_array($parts['query'])) { + $parts['query'] = http_build_query($parts['query']); + } + return (isset($parts['scheme']) ? "{$parts['scheme']}:" : '') . + ((isset($parts['user']) || isset($parts['host'])) ? '//' : '') . + (isset($parts['user']) ? "{$parts['user']}" : '') . + (isset($parts['pass']) ? ":{$parts['pass']}" : '') . + (isset($parts['user']) ? '@' : '') . + (isset($parts['host']) ? "{$parts['host']}" : '') . + (isset($parts['port']) ? ":{$parts['port']}" : '') . + (isset($parts['path']) ? "{$parts['path']}" : '') . + (isset($parts['query']) ? "?{$parts['query']}" : '') . + (isset($parts['fragment']) ? "#{$parts['fragment']}" : ''); + } + + /** + * @param \Shikiryu\WebGobbler\Pool $pool + * + * @return \Shikiryu\WebGobbler\Collector[] + */ + public static function getAllCollectors(Pool $pool) + { + $collectors = []; + foreach (glob(__DIR__ . '/Collector/*.php') as $class) { + $classname = sprintf('Shikiryu\\WebGobbler\\Collector\\%s', basename($class, '.php')); + $tmp_class = new $classname($pool); + if (is_subclass_of($tmp_class, __CLASS__)) { + $collectors[] = $tmp_class; + } + } + + return $collectors; + } +} diff --git a/src/Collector/DeviantartCollector.php b/src/Collector/DeviantartCollector.php new file mode 100644 index 0000000..b71d725 --- /dev/null +++ b/src/Collector/DeviantartCollector.php @@ -0,0 +1,65 @@ +getRandomImages(1); + } + + /** + * @param int $number + * + * @return int + */ + public function getRandomImages(int $number = 1) + { + $html = file_get_contents(self::RANDOM_URL); + preg_match_all(self::RE_ALLDEVIATIONID, $html, $deviant_ids); + $deviant_ids = array_map(static function ($deviant_id) { + $array = explode('-', $deviant_id); + + return (int)end($array); + }, $deviant_ids[1]); + $deviant_ids = array_unique($deviant_ids); + + $index_to_download = array_rand($deviant_ids, $number); + if (!is_array($index_to_download)) { + $index_to_download = [$index_to_download]; + } + + foreach ($index_to_download as $deviant_id) { + $client = new Client(); + $crawler = $client->request('GET', 'https://www.deviantart.com/deviation/'.$deviant_ids[$deviant_id]); + $img_url = $crawler->filter('[data-hook="art_stage"] img')->eq(0)->attr('src'); + file_put_contents($this->getPoolDirectory() . '/' . basename(parse_url($img_url, PHP_URL_PATH)), file_get_contents($img_url)); + } + + return $number; // Fixme + } + + /** + * @return string + */ + public function getName() + { + return 'collector_deviantart'; + } +} diff --git a/src/Collector/LocalCollector.php b/src/Collector/LocalCollector.php new file mode 100644 index 0000000..035af67 --- /dev/null +++ b/src/Collector/LocalCollector.php @@ -0,0 +1,43 @@ +generateRandomWord(); + + $client = new Client(); + $crawler = $client->request('GET', sprintf(self::SEARCH_URL, $word_to_search)); + $imgs = $crawler->filter('noscript img'); + if ($imgs->count() < 20) { + return $this->getRandomImage(); + } + + $img_url = $imgs->eq(random_int(0, $imgs->count() - 1))->attr('src'); + $parsed_url = parse_url( $img_url ); + parse_str( $parsed_url['query'] , $url_vars ); + $name = $url_vars['id']; + unset($url_vars['w'], $url_vars['h']); + $parsed_url['query'] = $url_vars; + $img_url = $this->reverse_url($parsed_url); + + file_put_contents($this->getPoolDirectory() . '/'. $name . '.jpg', file_get_contents($img_url)); + + return 1; + } + + /** + * @param int $number + * + * @return int + */ + public function getRandomImages(int $number) + { + $count = 0; + for ($i = 0; $i < $number; $i++) { + $count += $this->getRandomImage(); + } + + return $count; + } +} diff --git a/src/Config.php b/src/Config.php new file mode 100644 index 0000000..43ca335 --- /dev/null +++ b/src/Config.php @@ -0,0 +1,72 @@ +data = json_decode(file_get_contents($config_file), true, 512, JSON_THROW_ON_ERROR); + } + + /** + * @param $key + * @param null $default + * + * @return mixed|null + */ + public function get($key, $default = null) + { + if ($this->has($key)) { + return $this->cache[$key]; + } + + return $default; + } + + /** + * @param string $key + * + * @return bool + */ + public function has($key) + { + // Check if already cached + if (isset($this->cache[$key])) { + return true; + } + + $segments = explode('.', $key); + $root = $this->data; + + // nested case + foreach ($segments as $segment) { + if (array_key_exists($segment, $root)) { + $root = $root[$segment]; + continue; + } + + return false; + } + + // Set cache for the given key + $this->cache[$key] = $root; + + return true; + } + +} diff --git a/src/Pool.php b/src/Pool.php new file mode 100644 index 0000000..9d0e2ae --- /dev/null +++ b/src/Pool.php @@ -0,0 +1,109 @@ +collectors = $all_collectors; + } else { + foreach ($all_collectors as $collector) { + if (in_array($collector->getName(), $collectors, true)) { + $this->collectors[] = $collector; + } + } + } + + $pool_dir = $config['directory']; + if (!is_dir($pool_dir) && !mkdir($pool_dir) && !is_dir($pool_dir)) { + throw new \RuntimeException(sprintf('Directory "%s" was not created', $pool_dir)); + } + + $this->pool_directory = $pool_dir; + + $this->nb_images = $config['nb_images']; // FIXME + + $this->prepareCollectors(); + } + + public function getPoolDirectory() + { + return $this->pool_directory; + } + + /** + * @return string + */ + public function getImage() + { + $images = $this->getFileList(); + + $index = array_rand($images); + $image = $images[$index]; + + unset($this->file_list[$index]); + + return $image; + } + + /** + * @return array + */ + private function getFileList() + { + if (!empty($this->file_list)) { + return $this->file_list; + } + + $file_list = []; + foreach ($this->collectors as $collector) { + $directory = $collector->getPoolDirectory(); + $file_list = array_merge($file_list, glob($directory.'/*.{jpg,gif,png}', GLOB_BRACE)); + } + + $this->file_list = $file_list; + + return $file_list; + } + + private function prepareCollectors() + { + foreach ($this->collectors as $collector) { + $directory = $collector->getPoolDirectory(); + $images = glob($directory.'/*.{jpg,gif,png}', GLOB_BRACE); + if (count($images) < $this->nb_images) { + $collector->getRandomImages($this->nb_images - count($images)); + } + } + } +}