aboutsummaryrefslogtreecommitdiff
path: root/bridges/FurAffinityBridge.php
diff options
context:
space:
mode:
Diffstat (limited to 'bridges/FurAffinityBridge.php')
-rw-r--r--bridges/FurAffinityBridge.php1857
1 files changed, 940 insertions, 917 deletions
diff --git a/bridges/FurAffinityBridge.php b/bridges/FurAffinityBridge.php
index b5bd3ead..7e1dfd82 100644
--- a/bridges/FurAffinityBridge.php
+++ b/bridges/FurAffinityBridge.php
@@ -1,903 +1,925 @@
<?php
-class FurAffinityBridge extends BridgeAbstract {
- const NAME = 'FurAffinity Bridge';
- const URI = 'https://www.furaffinity.net';
- const CACHE_TIMEOUT = 300; // 5min
- const DESCRIPTION = 'Returns posts from various sections of FurAffinity';
- const MAINTAINER = 'Roliga';
- const PARAMETERS = array(
- 'Search' => array(
- 'q' => array(
- 'name' => 'Query',
- 'required' => true,
- 'exampleValue' => 'dog',
- ),
- 'rating-general' => array(
- 'name' => 'General',
- 'type' => 'checkbox',
- 'defaultValue' => 'checked'
- ),
- 'rating-mature' => array(
- 'name' => 'Mature',
- 'type' => 'checkbox',
- ),
- 'rating-adult' => array(
- 'name' => 'Adult',
- 'type' => 'checkbox',
- ),
- 'range' => array(
- 'name' => 'Time range',
- 'type' => 'list',
- 'values' => array(
- 'A Day' => 'day',
- '3 Days' => '3days',
- 'A Week' => 'week',
- 'A Month' => 'month',
- 'All time' => 'all'
- ),
- 'defaultValue' => 'all'
- ),
- 'type-art' => array(
- 'name' => 'Art',
- 'type' => 'checkbox',
- 'defaultValue' => 'checked'
- ),
- 'type-flash' => array(
- 'name' => 'Flash',
- 'type' => 'checkbox',
- 'defaultValue' => 'checked'
- ),
- 'type-photo' => array(
- 'name' => 'Photography',
- 'type' => 'checkbox',
- 'defaultValue' => 'checked'
- ),
- 'type-music' => array(
- 'name' => 'Music',
- 'type' => 'checkbox',
- 'defaultValue' => 'checked'
- ),
- 'type-story' => array(
- 'name' => 'Story',
- 'type' => 'checkbox',
- 'defaultValue' => 'checked'
- ),
- 'type-poetry' => array(
- 'name' => 'Poetry',
- 'type' => 'checkbox',
- 'defaultValue' => 'checked'
- ),
- 'mode' => array(
- 'name' => 'Match mode',
- 'type' => 'list',
- 'values' => array(
- 'All of the words' => 'all',
- 'Any of the words' => 'any',
- 'Extended' => 'extended'
- ),
- 'defaultValue' => 'extended'
- ),
- 'limit' => array(
- 'name' => 'Limit',
- 'type' => 'number',
- 'required' => true,
- 'defaultValue' => 10,
- 'title' => 'Limit number of submissions to return. -1 for unlimited.'
- ),
- 'full' => array(
- 'name' => 'Full view',
- 'title' => 'Include description, tags, date and larger image in article. Uses more bandwidth.',
- 'type' => 'checkbox',
- 'defaultValue' => 'checked'
- ),
- 'cache' => array(
- 'name' => 'Cache submission pages',
- 'title' => 'Reduces requests to FA when Full view is enabled. Changes to submission details may be delayed.',
- 'type' => 'checkbox',
- 'defaultValue' => 'checked'
- )
- ),
- 'Browse' => array(
- 'cat' => array(
- 'name' => 'Category',
- 'type' => 'list',
- 'values' => array(
- 'Visual Art' => array(
- 'All' => 1,
- 'Artwork (Digital)' => 2,
- 'Artwork (Traditional)' => 3,
- 'Cellshading' => 4,
- 'Crafting' => 5,
- 'Designs' => 6,
- 'Flash' => 7,
- 'Fursuiting' => 8,
- 'Icons' => 9,
- 'Mosaics' => 10,
- 'Photography' => 11,
- 'Sculpting' => 12
- ),
- 'Readable Art' => array(
- 'Story' => 13,
- 'Poetry' => 14,
- 'Prose' => 15
- ),
- 'Audio Art' => array(
- 'Music' => 16,
- 'Podcasts' => 17
- ),
- 'Downloadable' => array(
- 'Skins' => 18,
- 'Handhelds' => 19,
- 'Resources' => 20
- ),
- 'Other Stuff' => array(
- 'Adoptables' => 21,
- 'Auctions' => 22,
- 'Contests' => 23,
- 'Current Events' => 24,
- 'Desktops' => 25,
- 'Stockart' => 26,
- 'Screenshots' => 27,
- 'Scraps' => 28,
- 'Wallpaper' => 29,
- 'YCH / Sale' => 30,
- 'Other' => 31
- )
- ),
- 'defaultValue' => 1
- ),
- 'atype' => array(
- 'name' => 'Type',
- 'type' => 'list',
- 'values' => array(
- 'General Things' => array(
- 'All' => 1,
- 'Abstract' => 2,
- 'Animal related (non-anthro)' => 3,
- 'Anime' => 4,
- 'Comics' => 5,
- 'Doodle' => 6,
- 'Fanart' => 7,
- 'Fantasy' => 8,
- 'Human' => 9,
- 'Portraits' => 10,
- 'Scenery' => 11,
- 'Still Life' => 12,
- 'Tutorials' => 13,
- 'Miscellaneous' => 14
- ),
- 'Fetish / Furry specialty' => array(
- 'Baby fur' => 101,
- 'Bondage' => 102,
- 'Digimon' => 103,
- 'Fat Furs' => 104,
- 'Fetish Other' => 105,
- 'Fursuit' => 106,
- 'Gore / Macabre Art' => 119,
- 'Hyper' => 107,
- 'Inflation' => 108,
- 'Macro / Micro' => 109,
- 'Muscle' => 110,
- 'My Little Pony / Brony' => 111,
- 'Paw' => 112,
- 'Pokemon' => 113,
- 'Pregnancy' => 114,
- 'Sonic' => 115,
- 'Transformation' => 116,
- 'Vore' => 117,
- 'Water Sports' => 118,
- 'General Furry Art' => 100
- ),
- 'Music' => array(
- 'Techno' => 201,
- 'Trance' => 202,
- 'House' => 203,
- '90s' => 204,
- '80s' => 205,
- '70s' => 206,
- '60s' => 207,
- 'Pre-60s' => 208,
- 'Classical' => 209,
- 'Game Music' => 210,
- 'Rock' => 211,
- 'Pop' => 212,
- 'Rap' => 213,
- 'Industrial' => 214,
- 'Other Music' => 200
- )
- ),
- 'defaultValue' => 1
- ),
- 'species' => array(
- 'name' => 'Species',
- 'type' => 'list',
- 'values' => array(
- 'Unspecified / Any' => 1,
- 'Amphibian' => array(
- 'Frog' => 1001,
- 'Newt' => 1002,
- 'Salamander' => 1003,
- 'Amphibian (Other)' => 1000
- ),
- 'Aquatic' => array(
- 'Cephalopod' => 2001,
- 'Dolphin' => 2002,
- 'Fish' => 2005,
- 'Porpoise' => 2004,
- 'Seal' => 6068,
- 'Shark' => 2006,
- 'Whale' => 2003,
- 'Aquatic (Other)' => 2000
- ),
- 'Avian' => array(
- 'Corvid' => 3001,
- 'Crow' => 3002,
- 'Duck' => 3003,
- 'Eagle' => 3004,
- 'Falcon' => 3005,
- 'Goose' => 3006,
- 'Gryphon' => 3007,
- 'Hawk' => 3008,
- 'Owl' => 3009,
- 'Phoenix' => 3010,
- 'Swan' => 3011,
- 'Avian (Other)' => 3000
- ),
- 'Bears &amp; Ursines' => array(
- 'Bear' => 6002
- ),
- 'Camelids' => array(
- 'Camel' => 6074,
- 'Llama' => 6036
- ),
- 'Canines &amp; Lupines' => array(
- 'Coyote' => 6008,
- 'Doberman' => 6009,
- 'Dog' => 6010,
- 'Dingo' => 6011,
- 'German Shepherd' => 6012,
- 'Jackal' => 6013,
- 'Husky' => 6014,
- 'Wolf' => 6016,
- 'Canine (Other)' => 6017
- ),
- 'Cervines' => array(
- 'Cervine (Other)' => 6018
- ),
- 'Cows &amp; Bovines' => array(
- 'Antelope' => 6004,
- 'Cows' => 6003,
- 'Gazelle' => 6005,
- 'Goat' => 6006,
- 'Bovines (General)' => 6007
- ),
- 'Dragons' => array(
- 'Eastern Dragon' => 4001,
- 'Hydra' => 4002,
- 'Serpent' => 4003,
- 'Western Dragon' => 4004,
- 'Wyvern' => 4005,
- 'Dragon (Other)' => 4000
- ),
- 'Equestrians' => array(
- 'Donkey' => 6019,
- 'Horse' => 6034,
- 'Pony' => 6073,
- 'Zebra' => 6071
- ),
- 'Exotic &amp; Mythicals' => array(
- 'Argonian' => 5002,
- 'Chakat' => 5003,
- 'Chocobo' => 5004,
- 'Citra' => 5005,
- 'Crux' => 5006,
- 'Daemon' => 5007,
- 'Digimon' => 5008,
- 'Dracat' => 5009,
- 'Draenei' => 5010,
- 'Elf' => 5011,
- 'Gargoyle' => 5012,
- 'Iksar' => 5013,
- 'Kaiju/Monster' => 5015,
- 'Langurhali' => 5014,
- 'Moogle' => 5017,
- 'Naga' => 5016,
- 'Orc' => 5018,
- 'Pokemon' => 5019,
- 'Satyr' => 5020,
- 'Sergal' => 5021,
- 'Tanuki' => 5022,
- 'Unicorn' => 5023,
- 'Xenomorph' => 5024,
- 'Alien (Other)' => 5001,
- 'Exotic (Other)' => 5000
- ),
- 'Felines' => array(
- 'Domestic Cat' => 6020,
- 'Cheetah' => 6021,
- 'Cougar' => 6022,
- 'Jaguar' => 6023,
- 'Leopard' => 6024,
- 'Lion' => 6025,
- 'Lynx' => 6026,
- 'Ocelot' => 6027,
- 'Panther' => 6028,
- 'Tiger' => 6029,
- 'Feline (Other)' => 6030
- ),
- 'Insects' => array(
- 'Arachnid' => 8000,
- 'Mantid' => 8004,
- 'Scorpion' => 8005,
- 'Insect (Other)' => 8003
- ),
- 'Mammals (Other)' => array(
- 'Bat' => 6001,
- 'Giraffe' => 6031,
- 'Hedgehog' => 6032,
- 'Hippopotamus' => 6033,
- 'Hyena' => 6035,
- 'Panda' => 6052,
- 'Pig/Swine' => 6053,
- 'Rabbit/Hare' => 6059,
- 'Raccoon' => 6060,
- 'Red Panda' => 6062,
- 'Meerkat' => 6043,
- 'Mongoose' => 6044,
- 'Rhinoceros' => 6063,
- 'Mammals (Other)' => 6000
- ),
- 'Marsupials' => array(
- 'Opossum' => 6037,
- 'Kangaroo' => 6038,
- 'Koala' => 6039,
- 'Quoll' => 6040,
- 'Wallaby' => 6041,
- 'Marsupial (Other)' => 6042
- ),
- 'Mustelids' => array(
- 'Badger' => 6045,
- 'Ferret' => 6046,
- 'Mink' => 6048,
- 'Otter' => 6047,
- 'Skunk' => 6069,
- 'Weasel' => 6049,
- 'Mustelid (Other)' => 6051
- ),
- 'Primates' => array(
- 'Gorilla' => 6054,
- 'Human' => 6055,
- 'Lemur' => 6056,
- 'Monkey' => 6057,
- 'Primate (Other)' => 6058
- ),
- 'Reptillian' => array(
- 'Alligator &amp; Crocodile' => 7001,
- 'Gecko' => 7003,
- 'Iguana' => 7004,
- 'Lizard' => 7005,
- 'Snakes &amp; Serpents' => 7006,
- 'Turtle' => 7007,
- 'Reptilian (Other)' => 7000
- ),
- 'Rodents' => array(
- 'Beaver' => 6064,
- 'Mouse' => 6065,
- 'Rat' => 6061,
- 'Squirrel' => 6070,
- 'Rodent (Other)' => 6067
- ),
- 'Vulpines' => array(
- 'Fennec' => 6072,
- 'Fox' => 6075,
- 'Vulpine (Other)' => 6015
- ),
- 'Other' => array(
- 'Dinosaur' => 8001,
- 'Wolverine' => 6050
- )
- ),
- 'defaultValue' => 1
- ),
- 'gender' => array(
- 'name' => 'Gender',
- 'type' => 'list',
- 'values' => array(
- 'Any' => 0,
- 'Male' => 2,
- 'Female' => 3,
- 'Herm' => 4,
- 'Transgender' => 5,
- 'Multiple characters' => 6,
- 'Other / Not Specified' => 7
- ),
- 'defaultValue' => 0
- ),
- 'rating_general' => array(
- 'name' => 'General',
- 'type' => 'checkbox',
- 'defaultValue' => 'checked'
- ),
- 'rating_mature' => array(
- 'name' => 'Mature',
- 'type' => 'checkbox',
- ),
- 'rating_adult' => array(
- 'name' => 'Adult',
- 'type' => 'checkbox',
- ),
- 'limit-browse' => array(
- 'name' => 'Limit',
- 'type' => 'number',
- 'required' => true,
- 'defaultValue' => 10,
- 'title' => 'Limit number of submissions to return. -1 for unlimited.'
- ),
- 'full' => array(
- 'name' => 'Full view',
- 'title' => 'Include description, tags, date and larger image in article. Uses more bandwidth.',
- 'type' => 'checkbox',
- 'defaultValue' => 'checked'
- ),
- 'cache' => array(
- 'name' => 'Cache submission pages',
- 'title' => 'Reduces requests to FA when Full view is enabled. Changes to submission details may be delayed.',
- 'type' => 'checkbox',
- 'defaultValue' => 'checked'
- )
-
- ),
- 'Journals' => array(
- 'username-journals' => array(
- 'name' => 'Username',
- 'required' => true,
- 'exampleValue' => 'dhw',
- 'title' => 'Lowercase username as seen in URLs'
- ),
- 'limit' => array(
- 'name' => 'Limit',
- 'type' => 'number',
- 'defaultValue' => -1,
- 'title' => 'Limit number of journals to return. -1 for unlimited.'
- )
-
- ),
- 'Single Journal' => array(
- 'journal-id' => array(
- 'name' => 'Journal ID',
- 'required' => true,
- 'exampleValue' => '10008853',
- 'type' => 'number',
- 'title' => 'Number seen in journal URL'
- )
- ),
- 'Gallery' => array(
- 'username-gallery' => array(
- 'name' => 'Username',
- 'required' => true,
- 'exampleValue' => 'dhw',
- 'title' => 'Lowercase username as seen in URLs'
- ),
- 'limit' => array(
- 'name' => 'Limit',
- 'type' => 'number',
- 'required' => true,
- 'defaultValue' => 10,
- 'title' => 'Limit number of submissions to return. -1 for unlimited.'
- ),
- 'full' => array(
- 'name' => 'Full view',
- 'title' => 'Include description, tags, date and larger image in article. Uses more bandwidth.',
- 'type' => 'checkbox',
- 'defaultValue' => 'checked'
- ),
- 'cache' => array(
- 'name' => 'Cache submission pages',
- 'title' => 'Reduces requests to FA when Full view is enabled. Changes to submission details may be delayed.',
- 'type' => 'checkbox',
- 'defaultValue' => 'checked'
- )
- ),
- 'Scraps' => array(
- 'username-scraps' => array(
- 'name' => 'Username',
- 'required' => true,
- 'exampleValue' => 'dhw',
- 'title' => 'Lowercase username as seen in URLs'
- ),
- 'limit' => array(
- 'name' => 'Limit',
- 'type' => 'number',
- 'required' => true,
- 'defaultValue' => 10,
- 'title' => 'Limit number of submissions to return. -1 for unlimited.'
- ),
- 'full' => array(
- 'name' => 'Full view',
- 'title' => 'Include description, tags, date and larger image in article. Uses more bandwidth.',
- 'type' => 'checkbox',
- 'defaultValue' => 'checked'
- ),
- 'cache' => array(
- 'name' => 'Cache submission pages',
- 'title' => 'Reduces requests to FA when Full view is enabled. Changes to submission details may be delayed.',
- 'type' => 'checkbox',
- 'defaultValue' => 'checked'
- )
- ),
- 'Favorites' => array(
- 'username-favorites' => array(
- 'name' => 'Username',
- 'required' => true,
- 'exampleValue' => 'dhw',
- 'title' => 'Lowercase username as seen in URLs'
- ),
- 'limit' => array(
- 'name' => 'Limit',
- 'type' => 'number',
- 'required' => true,
- 'defaultValue' => 10,
- 'title' => 'Limit number of submissions to return. -1 for unlimited.'
- ),
- 'full' => array(
- 'name' => 'Full view',
- 'title' => 'Include description, tags, date and larger image in article. Uses more bandwidth.',
- 'type' => 'checkbox',
- 'defaultValue' => 'checked'
- ),
- 'cache' => array(
- 'name' => 'Cache submission pages',
- 'title' => 'Reduces requests to FA when Full view is enabled. Changes to submission details may be delayed.',
- 'type' => 'checkbox',
- 'defaultValue' => 'checked'
- )
- ),
- 'Gallery Folder' => array(
- 'username-folder' => array(
- 'name' => 'Username',
- 'required' => true,
- 'exampleValue' => 'kopk',
- 'title' => 'Lowercase username as seen in URLs'
- ),
- 'folder-id' => array(
- 'name' => 'Folder ID',
- 'required' => true,
- 'exampleValue' => '1031990',
- 'type' => 'number',
- 'title' => 'Number seen in folder URL'
- ),
- 'limit' => array(
- 'name' => 'Limit',
- 'type' => 'number',
- 'required' => true,
- 'defaultValue' => 10,
- 'title' => 'Limit number of submissions to return. -1 for unlimited.'
- ),
- 'full' => array(
- 'name' => 'Full view',
- 'title' => 'Include description, tags, date and larger image in article. Uses more bandwidth.',
- 'type' => 'checkbox',
- 'defaultValue' => 'checked'
- ),
- 'cache' => array(
- 'name' => 'Cache submission pages',
- 'title' => 'Reduces requests to FA when Full view is enabled. Changes to submission details may be delayed.',
- 'type' => 'checkbox',
- 'defaultValue' => 'checked'
- )
- )
- );
-
- /*
- * This was aquired by creating a new user on FA then
- * extracting the cookie from the browsers dev console.
- */
- const FA_AUTH_COOKIE = 'b=4ce65691-b50f-4742-a990-bf28d6de16ee; a=ca6e4566-9d81-4263-9444-653b142e35f8';
-
- public function detectParameters($url) {
- $params = array();
-
- // Single journal
- $regex = '/^(https?:\/\/)?(www\.)?furaffinity.net\/journal\/(\d+)/';
- if(preg_match($regex, $url, $matches) > 0) {
- $params['journal-id'] = urldecode($matches[3]);
- return $params;
- }
-
- // Journals
- $regex = '/^(https?:\/\/)?(www\.)?furaffinity.net\/journals\/([^\/&?\n]+)/';
- if(preg_match($regex, $url, $matches) > 0) {
- $params['username-journals'] = urldecode($matches[3]);
- return $params;
- }
-
- // Gallery folder
- $regex = '/^(https?:\/\/)?(www\.)?furaffinity.net\/gallery\/([^\/&?\n]+)\/folder\/(\d+)/';
- if(preg_match($regex, $url, $matches) > 0) {
- $params['username-folder'] = urldecode($matches[3]);
- $params['folder-id'] = urldecode($matches[4]);
- $params['full'] = 'on';
- return $params;
- }
-
- // Gallery (must be after gallery folder)
- $regex = '/^(https?:\/\/)?(www\.)?furaffinity.net\/(gallery|scraps|favorites)\/([^\/&?\n]+)/';
- if(preg_match($regex, $url, $matches) > 0) {
- $params['username-' . $matches[3]] = urldecode($matches[4]);
- $params['full'] = 'on';
- return $params;
- }
-
- return null;
- }
-
- public function getName() {
- switch($this->queriedContext) {
- case 'Search':
- return 'Search For '
- . $this->getInput('q');
- case 'Browse':
- return 'Browse';
- case 'Journals':
- return $this->getInput('username-journals');
- case 'Single Journal':
- return 'Journal '
- . $this->getInput('journal-id');
- case 'Gallery':
- return $this->getInput('username-gallery');
- case 'Scraps':
- return $this->getInput('username-scraps');
- case 'Favorites':
- return $this->getInput('username-favorites');
- case 'Gallery Folder':
- return $this->getInput('username-folder')
- . '\'s Folder '
- . $this->getInput('folder-id');
- default: return parent::getName();
- }
- }
-
- public function getDescription() {
- switch($this->queriedContext) {
- case 'Search':
- return 'FurAffinity Search For '
- . $this->getInput('q');
- case 'Browse':
- return 'FurAffinity Browse';
- case 'Journals':
- return 'FurAffinity Journals By '
- . $this->getInput('username-journals');
- case 'Single Journal':
- return 'FurAffinity Journal '
- . $this->getInput('journal-id');
- case 'Gallery':
- return 'FurAffinity Gallery By '
- . $this->getInput('username-gallery');
- case 'Scraps':
- return 'FurAffinity Scraps By '
- . $this->getInput('username-scraps');
- case 'Favorites':
- return 'FurAffinity Favorites By '
- . $this->getInput('username-favorites');
- case 'Gallery Folder':
- return 'FurAffinity Gallery Folder '
- . $this->getInput('folder-id')
- . ' By '
- . $this->getInput('username-folder');
- default: return parent::getDescription();
- }
- }
-
- public function getURI() {
- switch($this->queriedContext) {
- case 'Search':
- return SELF::URI
- . '/search';
- case 'Browse':
- return SELF::URI
- . '/browse';
- case 'Journals':
- return SELF::URI
- . '/journals/'
- . $this->getInput('username-journals');
- case 'Single Journal':
- return SELF::URI
- . '/journal/'
- . $this->getInput('journal-id');
- case 'Gallery':
- return SELF::URI
- . '/gallery/'
- . $this->getInput('username-gallery');
- case 'Scraps':
- return SELF::URI
- . '/scraps/'
- . $this->getInput('username-scraps');
- case 'Favorites':
- return SELF::URI
- . '/favorites/'
- . $this->getInput('username-favorites');
- case 'Gallery Folder':
- return SELF::URI
- . '/gallery/'
- . $this->getInput('username-folder')
- . '/folder/'
- . $this->getInput('folder-id');
- default: return parent::getURI();
- }
- }
-
- public function collectData() {
- switch($this->queriedContext) {
- case 'Search':
- $data = array(
- 'q' => $this->getInput('q'),
- 'perpage' => 72,
- 'rating-general' => ($this->getInput('rating-general') === true ? 'on' : 0),
- 'rating-mature' => ($this->getInput('rating-mature') === true ? 'on' : 0),
- 'rating-adult' => ($this->getInput('rating-adult') === true ? 'on' : 0),
- 'range' => $this->getInput('range'),
- 'type-art' => ($this->getInput('type-art') === true ? 'on' : 0),
- 'type-flash' => ($this->getInput('type-flash') === true ? 'on' : 0),
- 'type-photo' => ($this->getInput('type-photo') === true ? 'on' : 0),
- 'type-music' => ($this->getInput('type-music') === true ? 'on' : 0),
- 'type-story' => ($this->getInput('type-story') === true ? 'on' : 0),
- 'type-poetry' => ($this->getInput('type-poetry') === true ? 'on' : 0),
- 'mode' => $this->getInput('mode')
- );
- $html = $this->postFASimpleHTMLDOM($data);
- $limit = (is_int($this->getInput('limit')) ? $this->getInput('limit') : 10);
- $this->itemsFromSubmissionList($html, $limit);
- break;
- case 'Browse':
- $data = array(
- 'cat' => $this->getInput('cat'),
- 'atype' => $this->getInput('atype'),
- 'species' => $this->getInput('species'),
- 'gender' => $this->getInput('gender'),
- 'perpage' => 72,
- 'rating_general' => ($this->getInput('rating_general') === true ? 'on' : 0),
- 'rating_mature' => ($this->getInput('rating_mature') === true ? 'on' : 0),
- 'rating_adult' => ($this->getInput('rating_adult') === true ? 'on' : 0)
- );
- $html = $this->postFASimpleHTMLDOM($data);
- $limit = (is_int($this->getInput('limit-browse')) ? $this->getInput('limit-browse') : 10);
- $this->itemsFromSubmissionList($html, $limit);
- break;
- case 'Journals':
- $html = $this->getFASimpleHTMLDOM($this->getURI());
- $limit = (is_int($this->getInput('limit')) ? $this->getInput('limit') : -1);
- $this->itemsFromJournalList($html, $limit);
- break;
- case 'Single Journal':
- $html = $this->getFASimpleHTMLDOM($this->getURI());
- $this->itemsFromJournal($html);
- break;
- case 'Gallery':
- case 'Scraps':
- case 'Favorites':
- case 'Gallery Folder':
- $html = $this->getFASimpleHTMLDOM($this->getURI());
- $limit = (is_int($this->getInput('limit')) ? $this->getInput('limit') : 10);
- $this->itemsFromSubmissionList($html, $limit);
- break;
- }
- }
-
- private function postFASimpleHTMLDOM($data) {
- $opts = array(
- CURLOPT_CUSTOMREQUEST => 'POST',
- CURLOPT_POSTFIELDS => http_build_query($data)
- );
- $header = array(
- 'Host: ' . parse_url(self::URI, PHP_URL_HOST),
- 'Content-Type: application/x-www-form-urlencoded',
- 'Cookie: ' . self::FA_AUTH_COOKIE
- );
-
- $html = getSimpleHTMLDOM($this->getURI(), $header, $opts);
- $html = defaultLinkTo($html, $this->getURI());
-
- return $html;
- }
-
- private function getFASimpleHTMLDOM($url, $cache = false) {
- $header = array(
- 'Cookie: ' . self::FA_AUTH_COOKIE
- );
-
- if($cache) {
- $html = getSimpleHTMLDOMCached($url, 86400, $header); // 24 hours
- } else {
- $html = getSimpleHTMLDOM($url, $header);
- }
-
- $html = defaultLinkTo($html, $url);
-
- return $html;
- }
-
- private function itemsFromJournalList($html, $limit) {
- foreach($html->find('table[id^=jid:]') as $journal) {
- # allows limit = -1 to mean 'unlimited'
- if($limit-- === 0) break;
-
- $item = array();
-
- $this->setReferrerPolicy($journal);
-
- $item['uri'] = $journal->find('a', 0)->href;
- $item['title'] = html_entity_decode($journal->find('a', 0)->plaintext);
- $item['author'] = $this->getInput('username-journals');
- $item['timestamp'] = strtotime(
- $journal->find('span.popup_date', 0)->plaintext);
- $item['content'] = $journal
- ->find('.alt1 table div.no_overflow', 0)
- ->innertext;
-
- $this->items[] = $item;
- }
- }
-
- private function itemsFromJournal($html) {
- $this->setReferrerPolicy($html);
- $item = array();
-
- $item['uri'] = $this->getURI();
-
- $title = $html->find('.journal-title-box .no_overflow', 0)->plaintext;
- $title = html_entity_decode($title);
- $title = trim($title, " \t\n\r\0\x0B" . chr(0xC2) . chr(0xA0));
- $item['title'] = $title;
-
- $item['author'] = $html->find('.journal-title-box a', 0)->plaintext;
- $item['timestamp'] = strtotime(
- $html->find('.journal-title-box span.popup_date', 0)->plaintext);
- $item['content'] = $html->find('.journal-body', 0)->innertext;
-
- $this->items[] = $item;
- }
-
- private function itemsFromSubmissionList($html, $limit) {
- $cache = ($this->getInput('cache') === true);
-
- foreach($html->find('section.gallery figure') as $figure) {
- # allows limit = -1 to mean 'unlimited'
- if($limit-- === 0) break;
-
- $item = array();
-
- $submissionURL = $figure->find('b u a', 0)->href;
- $imgURL = 'https:' . $figure->find('b u a img', 0)->src;
-
- $item['uri'] = $submissionURL;
- $item['title'] = html_entity_decode(
- $figure->find('figcaption p a[href*=/view/]', 0)->title);
- $item['author'] = $figure->find('figcaption p a[href*=/user/]', 0)->title;
-
- if($this->getInput('full') === true) {
- $submissionHTML = $this->getFASimpleHTMLDOM($submissionURL, $cache);
-
- $stats = $submissionHTML->find('.stats-container', 0);
- $item['timestamp'] = strtotime($stats->find('.popup_date', 0)->title);
- $item['enclosures'] = array(
- $submissionHTML->find('.actions a[href^=https://d.facdn]', 0)->href
- );
- foreach($stats->find('#keywords a') as $keyword) {
- $item['categories'][] = $keyword->plaintext;
- }
-
- $previewSrc = $submissionHTML->find('#submissionImg', 0)
- ->{'data-preview-src'};
- if($previewSrc) {
- $imgURL = 'https:' . $previewSrc;
- }
- $description = $submissionHTML
- ->find('.maintable .maintable tr td.alt1', -1);
- $this->setReferrerPolicy($description);
- $description = $description->innertext;
-
- $item['content'] = <<<EOD
+class FurAffinityBridge extends BridgeAbstract
+{
+ const NAME = 'FurAffinity Bridge';
+ const URI = 'https://www.furaffinity.net';
+ const CACHE_TIMEOUT = 300; // 5min
+ const DESCRIPTION = 'Returns posts from various sections of FurAffinity';
+ const MAINTAINER = 'Roliga';
+ const PARAMETERS = [
+ 'Search' => [
+ 'q' => [
+ 'name' => 'Query',
+ 'required' => true,
+ 'exampleValue' => 'dog',
+ ],
+ 'rating-general' => [
+ 'name' => 'General',
+ 'type' => 'checkbox',
+ 'defaultValue' => 'checked'
+ ],
+ 'rating-mature' => [
+ 'name' => 'Mature',
+ 'type' => 'checkbox',
+ ],
+ 'rating-adult' => [
+ 'name' => 'Adult',
+ 'type' => 'checkbox',
+ ],
+ 'range' => [
+ 'name' => 'Time range',
+ 'type' => 'list',
+ 'values' => [
+ 'A Day' => 'day',
+ '3 Days' => '3days',
+ 'A Week' => 'week',
+ 'A Month' => 'month',
+ 'All time' => 'all'
+ ],
+ 'defaultValue' => 'all'
+ ],
+ 'type-art' => [
+ 'name' => 'Art',
+ 'type' => 'checkbox',
+ 'defaultValue' => 'checked'
+ ],
+ 'type-flash' => [
+ 'name' => 'Flash',
+ 'type' => 'checkbox',
+ 'defaultValue' => 'checked'
+ ],
+ 'type-photo' => [
+ 'name' => 'Photography',
+ 'type' => 'checkbox',
+ 'defaultValue' => 'checked'
+ ],
+ 'type-music' => [
+ 'name' => 'Music',
+ 'type' => 'checkbox',
+ 'defaultValue' => 'checked'
+ ],
+ 'type-story' => [
+ 'name' => 'Story',
+ 'type' => 'checkbox',
+ 'defaultValue' => 'checked'
+ ],
+ 'type-poetry' => [
+ 'name' => 'Poetry',
+ 'type' => 'checkbox',
+ 'defaultValue' => 'checked'
+ ],
+ 'mode' => [
+ 'name' => 'Match mode',
+ 'type' => 'list',
+ 'values' => [
+ 'All of the words' => 'all',
+ 'Any of the words' => 'any',
+ 'Extended' => 'extended'
+ ],
+ 'defaultValue' => 'extended'
+ ],
+ 'limit' => [
+ 'name' => 'Limit',
+ 'type' => 'number',
+ 'required' => true,
+ 'defaultValue' => 10,
+ 'title' => 'Limit number of submissions to return. -1 for unlimited.'
+ ],
+ 'full' => [
+ 'name' => 'Full view',
+ 'title' => 'Include description, tags, date and larger image in article. Uses more bandwidth.',
+ 'type' => 'checkbox',
+ 'defaultValue' => 'checked'
+ ],
+ 'cache' => [
+ 'name' => 'Cache submission pages',
+ 'title' => 'Reduces requests to FA when Full view is enabled. Changes to submission details may be delayed.',
+ 'type' => 'checkbox',
+ 'defaultValue' => 'checked'
+ ]
+ ],
+ 'Browse' => [
+ 'cat' => [
+ 'name' => 'Category',
+ 'type' => 'list',
+ 'values' => [
+ 'Visual Art' => [
+ 'All' => 1,
+ 'Artwork (Digital)' => 2,
+ 'Artwork (Traditional)' => 3,
+ 'Cellshading' => 4,
+ 'Crafting' => 5,
+ 'Designs' => 6,
+ 'Flash' => 7,
+ 'Fursuiting' => 8,
+ 'Icons' => 9,
+ 'Mosaics' => 10,
+ 'Photography' => 11,
+ 'Sculpting' => 12
+ ],
+ 'Readable Art' => [
+ 'Story' => 13,
+ 'Poetry' => 14,
+ 'Prose' => 15
+ ],
+ 'Audio Art' => [
+ 'Music' => 16,
+ 'Podcasts' => 17
+ ],
+ 'Downloadable' => [
+ 'Skins' => 18,
+ 'Handhelds' => 19,
+ 'Resources' => 20
+ ],
+ 'Other Stuff' => [
+ 'Adoptables' => 21,
+ 'Auctions' => 22,
+ 'Contests' => 23,
+ 'Current Events' => 24,
+ 'Desktops' => 25,
+ 'Stockart' => 26,
+ 'Screenshots' => 27,
+ 'Scraps' => 28,
+ 'Wallpaper' => 29,
+ 'YCH / Sale' => 30,
+ 'Other' => 31
+ ]
+ ],
+ 'defaultValue' => 1
+ ],
+ 'atype' => [
+ 'name' => 'Type',
+ 'type' => 'list',
+ 'values' => [
+ 'General Things' => [
+ 'All' => 1,
+ 'Abstract' => 2,
+ 'Animal related (non-anthro)' => 3,
+ 'Anime' => 4,
+ 'Comics' => 5,
+ 'Doodle' => 6,
+ 'Fanart' => 7,
+ 'Fantasy' => 8,
+ 'Human' => 9,
+ 'Portraits' => 10,
+ 'Scenery' => 11,
+ 'Still Life' => 12,
+ 'Tutorials' => 13,
+ 'Miscellaneous' => 14
+ ],
+ 'Fetish / Furry specialty' => [
+ 'Baby fur' => 101,
+ 'Bondage' => 102,
+ 'Digimon' => 103,
+ 'Fat Furs' => 104,
+ 'Fetish Other' => 105,
+ 'Fursuit' => 106,
+ 'Gore / Macabre Art' => 119,
+ 'Hyper' => 107,
+ 'Inflation' => 108,
+ 'Macro / Micro' => 109,
+ 'Muscle' => 110,
+ 'My Little Pony / Brony' => 111,
+ 'Paw' => 112,
+ 'Pokemon' => 113,
+ 'Pregnancy' => 114,
+ 'Sonic' => 115,
+ 'Transformation' => 116,
+ 'Vore' => 117,
+ 'Water Sports' => 118,
+ 'General Furry Art' => 100
+ ],
+ 'Music' => [
+ 'Techno' => 201,
+ 'Trance' => 202,
+ 'House' => 203,
+ '90s' => 204,
+ '80s' => 205,
+ '70s' => 206,
+ '60s' => 207,
+ 'Pre-60s' => 208,
+ 'Classical' => 209,
+ 'Game Music' => 210,
+ 'Rock' => 211,
+ 'Pop' => 212,
+ 'Rap' => 213,
+ 'Industrial' => 214,
+ 'Other Music' => 200
+ ]
+ ],
+ 'defaultValue' => 1
+ ],
+ 'species' => [
+ 'name' => 'Species',
+ 'type' => 'list',
+ 'values' => [
+ 'Unspecified / Any' => 1,
+ 'Amphibian' => [
+ 'Frog' => 1001,
+ 'Newt' => 1002,
+ 'Salamander' => 1003,
+ 'Amphibian (Other)' => 1000
+ ],
+ 'Aquatic' => [
+ 'Cephalopod' => 2001,
+ 'Dolphin' => 2002,
+ 'Fish' => 2005,
+ 'Porpoise' => 2004,
+ 'Seal' => 6068,
+ 'Shark' => 2006,
+ 'Whale' => 2003,
+ 'Aquatic (Other)' => 2000
+ ],
+ 'Avian' => [
+ 'Corvid' => 3001,
+ 'Crow' => 3002,
+ 'Duck' => 3003,
+ 'Eagle' => 3004,
+ 'Falcon' => 3005,
+ 'Goose' => 3006,
+ 'Gryphon' => 3007,
+ 'Hawk' => 3008,
+ 'Owl' => 3009,
+ 'Phoenix' => 3010,
+ 'Swan' => 3011,
+ 'Avian (Other)' => 3000
+ ],
+ 'Bears &amp; Ursines' => [
+ 'Bear' => 6002
+ ],
+ 'Camelids' => [
+ 'Camel' => 6074,
+ 'Llama' => 6036
+ ],
+ 'Canines &amp; Lupines' => [
+ 'Coyote' => 6008,
+ 'Doberman' => 6009,
+ 'Dog' => 6010,
+ 'Dingo' => 6011,
+ 'German Shepherd' => 6012,
+ 'Jackal' => 6013,
+ 'Husky' => 6014,
+ 'Wolf' => 6016,
+ 'Canine (Other)' => 6017
+ ],
+ 'Cervines' => [
+ 'Cervine (Other)' => 6018
+ ],
+ 'Cows &amp; Bovines' => [
+ 'Antelope' => 6004,
+ 'Cows' => 6003,
+ 'Gazelle' => 6005,
+ 'Goat' => 6006,
+ 'Bovines (General)' => 6007
+ ],
+ 'Dragons' => [
+ 'Eastern Dragon' => 4001,
+ 'Hydra' => 4002,
+ 'Serpent' => 4003,
+ 'Western Dragon' => 4004,
+ 'Wyvern' => 4005,
+ 'Dragon (Other)' => 4000
+ ],
+ 'Equestrians' => [
+ 'Donkey' => 6019,
+ 'Horse' => 6034,
+ 'Pony' => 6073,
+ 'Zebra' => 6071
+ ],
+ 'Exotic &amp; Mythicals' => [
+ 'Argonian' => 5002,
+ 'Chakat' => 5003,
+ 'Chocobo' => 5004,
+ 'Citra' => 5005,
+ 'Crux' => 5006,
+ 'Daemon' => 5007,
+ 'Digimon' => 5008,
+ 'Dracat' => 5009,
+ 'Draenei' => 5010,
+ 'Elf' => 5011,
+ 'Gargoyle' => 5012,
+ 'Iksar' => 5013,
+ 'Kaiju/Monster' => 5015,
+ 'Langurhali' => 5014,
+ 'Moogle' => 5017,
+ 'Naga' => 5016,
+ 'Orc' => 5018,
+ 'Pokemon' => 5019,
+ 'Satyr' => 5020,
+ 'Sergal' => 5021,
+ 'Tanuki' => 5022,
+ 'Unicorn' => 5023,
+ 'Xenomorph' => 5024,
+ 'Alien (Other)' => 5001,
+ 'Exotic (Other)' => 5000
+ ],
+ 'Felines' => [
+ 'Domestic Cat' => 6020,
+ 'Cheetah' => 6021,
+ 'Cougar' => 6022,
+ 'Jaguar' => 6023,
+ 'Leopard' => 6024,
+ 'Lion' => 6025,
+ 'Lynx' => 6026,
+ 'Ocelot' => 6027,
+ 'Panther' => 6028,
+ 'Tiger' => 6029,
+ 'Feline (Other)' => 6030
+ ],
+ 'Insects' => [
+ 'Arachnid' => 8000,
+ 'Mantid' => 8004,
+ 'Scorpion' => 8005,
+ 'Insect (Other)' => 8003
+ ],
+ 'Mammals (Other)' => [
+ 'Bat' => 6001,
+ 'Giraffe' => 6031,
+ 'Hedgehog' => 6032,
+ 'Hippopotamus' => 6033,
+ 'Hyena' => 6035,
+ 'Panda' => 6052,
+ 'Pig/Swine' => 6053,
+ 'Rabbit/Hare' => 6059,
+ 'Raccoon' => 6060,
+ 'Red Panda' => 6062,
+ 'Meerkat' => 6043,
+ 'Mongoose' => 6044,
+ 'Rhinoceros' => 6063,
+ 'Mammals (Other)' => 6000
+ ],
+ 'Marsupials' => [
+ 'Opossum' => 6037,
+ 'Kangaroo' => 6038,
+ 'Koala' => 6039,
+ 'Quoll' => 6040,
+ 'Wallaby' => 6041,
+ 'Marsupial (Other)' => 6042
+ ],
+ 'Mustelids' => [
+ 'Badger' => 6045,
+ 'Ferret' => 6046,
+ 'Mink' => 6048,
+ 'Otter' => 6047,
+ 'Skunk' => 6069,
+ 'Weasel' => 6049,
+ 'Mustelid (Other)' => 6051
+ ],
+ 'Primates' => [
+ 'Gorilla' => 6054,
+ 'Human' => 6055,
+ 'Lemur' => 6056,
+ 'Monkey' => 6057,
+ 'Primate (Other)' => 6058
+ ],
+ 'Reptillian' => [
+ 'Alligator &amp; Crocodile' => 7001,
+ 'Gecko' => 7003,
+ 'Iguana' => 7004,
+ 'Lizard' => 7005,
+ 'Snakes &amp; Serpents' => 7006,
+ 'Turtle' => 7007,
+ 'Reptilian (Other)' => 7000
+ ],
+ 'Rodents' => [
+ 'Beaver' => 6064,
+ 'Mouse' => 6065,
+ 'Rat' => 6061,
+ 'Squirrel' => 6070,
+ 'Rodent (Other)' => 6067
+ ],
+ 'Vulpines' => [
+ 'Fennec' => 6072,
+ 'Fox' => 6075,
+ 'Vulpine (Other)' => 6015
+ ],
+ 'Other' => [
+ 'Dinosaur' => 8001,
+ 'Wolverine' => 6050
+ ]
+ ],
+ 'defaultValue' => 1
+ ],
+ 'gender' => [
+ 'name' => 'Gender',
+ 'type' => 'list',
+ 'values' => [
+ 'Any' => 0,
+ 'Male' => 2,
+ 'Female' => 3,
+ 'Herm' => 4,
+ 'Transgender' => 5,
+ 'Multiple characters' => 6,
+ 'Other / Not Specified' => 7
+ ],
+ 'defaultValue' => 0
+ ],
+ 'rating_general' => [
+ 'name' => 'General',
+ 'type' => 'checkbox',
+ 'defaultValue' => 'checked'
+ ],
+ 'rating_mature' => [
+ 'name' => 'Mature',
+ 'type' => 'checkbox',
+ ],
+ 'rating_adult' => [
+ 'name' => 'Adult',
+ 'type' => 'checkbox',
+ ],
+ 'limit-browse' => [
+ 'name' => 'Limit',
+ 'type' => 'number',
+ 'required' => true,
+ 'defaultValue' => 10,
+ 'title' => 'Limit number of submissions to return. -1 for unlimited.'
+ ],
+ 'full' => [
+ 'name' => 'Full view',
+ 'title' => 'Include description, tags, date and larger image in article. Uses more bandwidth.',
+ 'type' => 'checkbox',
+ 'defaultValue' => 'checked'
+ ],
+ 'cache' => [
+ 'name' => 'Cache submission pages',
+ 'title' => 'Reduces requests to FA when Full view is enabled. Changes to submission details may be delayed.',
+ 'type' => 'checkbox',
+ 'defaultValue' => 'checked'
+ ]
+
+ ],
+ 'Journals' => [
+ 'username-journals' => [
+ 'name' => 'Username',
+ 'required' => true,
+ 'exampleValue' => 'dhw',
+ 'title' => 'Lowercase username as seen in URLs'
+ ],
+ 'limit' => [
+ 'name' => 'Limit',
+ 'type' => 'number',
+ 'defaultValue' => -1,
+ 'title' => 'Limit number of journals to return. -1 for unlimited.'
+ ]
+
+ ],
+ 'Single Journal' => [
+ 'journal-id' => [
+ 'name' => 'Journal ID',
+ 'required' => true,
+ 'exampleValue' => '10008853',
+ 'type' => 'number',
+ 'title' => 'Number seen in journal URL'
+ ]
+ ],
+ 'Gallery' => [
+ 'username-gallery' => [
+ 'name' => 'Username',
+ 'required' => true,
+ 'exampleValue' => 'dhw',
+ 'title' => 'Lowercase username as seen in URLs'
+ ],
+ 'limit' => [
+ 'name' => 'Limit',
+ 'type' => 'number',
+ 'required' => true,
+ 'defaultValue' => 10,
+ 'title' => 'Limit number of submissions to return. -1 for unlimited.'
+ ],
+ 'full' => [
+ 'name' => 'Full view',
+ 'title' => 'Include description, tags, date and larger image in article. Uses more bandwidth.',
+ 'type' => 'checkbox',
+ 'defaultValue' => 'checked'
+ ],
+ 'cache' => [
+ 'name' => 'Cache submission pages',
+ 'title' => 'Reduces requests to FA when Full view is enabled. Changes to submission details may be delayed.',
+ 'type' => 'checkbox',
+ 'defaultValue' => 'checked'
+ ]
+ ],
+ 'Scraps' => [
+ 'username-scraps' => [
+ 'name' => 'Username',
+ 'required' => true,
+ 'exampleValue' => 'dhw',
+ 'title' => 'Lowercase username as seen in URLs'
+ ],
+ 'limit' => [
+ 'name' => 'Limit',
+ 'type' => 'number',
+ 'required' => true,
+ 'defaultValue' => 10,
+ 'title' => 'Limit number of submissions to return. -1 for unlimited.'
+ ],
+ 'full' => [
+ 'name' => 'Full view',
+ 'title' => 'Include description, tags, date and larger image in article. Uses more bandwidth.',
+ 'type' => 'checkbox',
+ 'defaultValue' => 'checked'
+ ],
+ 'cache' => [
+ 'name' => 'Cache submission pages',
+ 'title' => 'Reduces requests to FA when Full view is enabled. Changes to submission details may be delayed.',
+ 'type' => 'checkbox',
+ 'defaultValue' => 'checked'
+ ]
+ ],
+ 'Favorites' => [
+ 'username-favorites' => [
+ 'name' => 'Username',
+ 'required' => true,
+ 'exampleValue' => 'dhw',
+ 'title' => 'Lowercase username as seen in URLs'
+ ],
+ 'limit' => [
+ 'name' => 'Limit',
+ 'type' => 'number',
+ 'required' => true,
+ 'defaultValue' => 10,
+ 'title' => 'Limit number of submissions to return. -1 for unlimited.'
+ ],
+ 'full' => [
+ 'name' => 'Full view',
+ 'title' => 'Include description, tags, date and larger image in article. Uses more bandwidth.',
+ 'type' => 'checkbox',
+ 'defaultValue' => 'checked'
+ ],
+ 'cache' => [
+ 'name' => 'Cache submission pages',
+ 'title' => 'Reduces requests to FA when Full view is enabled. Changes to submission details may be delayed.',
+ 'type' => 'checkbox',
+ 'defaultValue' => 'checked'
+ ]
+ ],
+ 'Gallery Folder' => [
+ 'username-folder' => [
+ 'name' => 'Username',
+ 'required' => true,
+ 'exampleValue' => 'kopk',
+ 'title' => 'Lowercase username as seen in URLs'
+ ],
+ 'folder-id' => [
+ 'name' => 'Folder ID',
+ 'required' => true,
+ 'exampleValue' => '1031990',
+ 'type' => 'number',
+ 'title' => 'Number seen in folder URL'
+ ],
+ 'limit' => [
+ 'name' => 'Limit',
+ 'type' => 'number',
+ 'required' => true,
+ 'defaultValue' => 10,
+ 'title' => 'Limit number of submissions to return. -1 for unlimited.'
+ ],
+ 'full' => [
+ 'name' => 'Full view',
+ 'title' => 'Include description, tags, date and larger image in article. Uses more bandwidth.',
+ 'type' => 'checkbox',
+ 'defaultValue' => 'checked'
+ ],
+ 'cache' => [
+ 'name' => 'Cache submission pages',
+ 'title' => 'Reduces requests to FA when Full view is enabled. Changes to submission details may be delayed.',
+ 'type' => 'checkbox',
+ 'defaultValue' => 'checked'
+ ]
+ ]
+ ];
+
+ /*
+ * This was aquired by creating a new user on FA then
+ * extracting the cookie from the browsers dev console.
+ */
+ const FA_AUTH_COOKIE = 'b=4ce65691-b50f-4742-a990-bf28d6de16ee; a=ca6e4566-9d81-4263-9444-653b142e35f8';
+
+ public function detectParameters($url)
+ {
+ $params = [];
+
+ // Single journal
+ $regex = '/^(https?:\/\/)?(www\.)?furaffinity.net\/journal\/(\d+)/';
+ if (preg_match($regex, $url, $matches) > 0) {
+ $params['journal-id'] = urldecode($matches[3]);
+ return $params;
+ }
+
+ // Journals
+ $regex = '/^(https?:\/\/)?(www\.)?furaffinity.net\/journals\/([^\/&?\n]+)/';
+ if (preg_match($regex, $url, $matches) > 0) {
+ $params['username-journals'] = urldecode($matches[3]);
+ return $params;
+ }
+
+ // Gallery folder
+ $regex = '/^(https?:\/\/)?(www\.)?furaffinity.net\/gallery\/([^\/&?\n]+)\/folder\/(\d+)/';
+ if (preg_match($regex, $url, $matches) > 0) {
+ $params['username-folder'] = urldecode($matches[3]);
+ $params['folder-id'] = urldecode($matches[4]);
+ $params['full'] = 'on';
+ return $params;
+ }
+
+ // Gallery (must be after gallery folder)
+ $regex = '/^(https?:\/\/)?(www\.)?furaffinity.net\/(gallery|scraps|favorites)\/([^\/&?\n]+)/';
+ if (preg_match($regex, $url, $matches) > 0) {
+ $params['username-' . $matches[3]] = urldecode($matches[4]);
+ $params['full'] = 'on';
+ return $params;
+ }
+
+ return null;
+ }
+
+ public function getName()
+ {
+ switch ($this->queriedContext) {
+ case 'Search':
+ return 'Search For '
+ . $this->getInput('q');
+ case 'Browse':
+ return 'Browse';
+ case 'Journals':
+ return $this->getInput('username-journals');
+ case 'Single Journal':
+ return 'Journal '
+ . $this->getInput('journal-id');
+ case 'Gallery':
+ return $this->getInput('username-gallery');
+ case 'Scraps':
+ return $this->getInput('username-scraps');
+ case 'Favorites':
+ return $this->getInput('username-favorites');
+ case 'Gallery Folder':
+ return $this->getInput('username-folder')
+ . '\'s Folder '
+ . $this->getInput('folder-id');
+ default:
+ return parent::getName();
+ }
+ }
+
+ public function getDescription()
+ {
+ switch ($this->queriedContext) {
+ case 'Search':
+ return 'FurAffinity Search For '
+ . $this->getInput('q');
+ case 'Browse':
+ return 'FurAffinity Browse';
+ case 'Journals':
+ return 'FurAffinity Journals By '
+ . $this->getInput('username-journals');
+ case 'Single Journal':
+ return 'FurAffinity Journal '
+ . $this->getInput('journal-id');
+ case 'Gallery':
+ return 'FurAffinity Gallery By '
+ . $this->getInput('username-gallery');
+ case 'Scraps':
+ return 'FurAffinity Scraps By '
+ . $this->getInput('username-scraps');
+ case 'Favorites':
+ return 'FurAffinity Favorites By '
+ . $this->getInput('username-favorites');
+ case 'Gallery Folder':
+ return 'FurAffinity Gallery Folder '
+ . $this->getInput('folder-id')
+ . ' By '
+ . $this->getInput('username-folder');
+ default:
+ return parent::getDescription();
+ }
+ }
+
+ public function getURI()
+ {
+ switch ($this->queriedContext) {
+ case 'Search':
+ return self::URI
+ . '/search';
+ case 'Browse':
+ return self::URI
+ . '/browse';
+ case 'Journals':
+ return self::URI
+ . '/journals/'
+ . $this->getInput('username-journals');
+ case 'Single Journal':
+ return self::URI
+ . '/journal/'
+ . $this->getInput('journal-id');
+ case 'Gallery':
+ return self::URI
+ . '/gallery/'
+ . $this->getInput('username-gallery');
+ case 'Scraps':
+ return self::URI
+ . '/scraps/'
+ . $this->getInput('username-scraps');
+ case 'Favorites':
+ return self::URI
+ . '/favorites/'
+ . $this->getInput('username-favorites');
+ case 'Gallery Folder':
+ return self::URI
+ . '/gallery/'
+ . $this->getInput('username-folder')
+ . '/folder/'
+ . $this->getInput('folder-id');
+ default:
+ return parent::getURI();
+ }
+ }
+
+ public function collectData()
+ {
+ switch ($this->queriedContext) {
+ case 'Search':
+ $data = [
+ 'q' => $this->getInput('q'),
+ 'perpage' => 72,
+ 'rating-general' => ($this->getInput('rating-general') === true ? 'on' : 0),
+ 'rating-mature' => ($this->getInput('rating-mature') === true ? 'on' : 0),
+ 'rating-adult' => ($this->getInput('rating-adult') === true ? 'on' : 0),
+ 'range' => $this->getInput('range'),
+ 'type-art' => ($this->getInput('type-art') === true ? 'on' : 0),
+ 'type-flash' => ($this->getInput('type-flash') === true ? 'on' : 0),
+ 'type-photo' => ($this->getInput('type-photo') === true ? 'on' : 0),
+ 'type-music' => ($this->getInput('type-music') === true ? 'on' : 0),
+ 'type-story' => ($this->getInput('type-story') === true ? 'on' : 0),
+ 'type-poetry' => ($this->getInput('type-poetry') === true ? 'on' : 0),
+ 'mode' => $this->getInput('mode')
+ ];
+ $html = $this->postFASimpleHTMLDOM($data);
+ $limit = (is_int($this->getInput('limit')) ? $this->getInput('limit') : 10);
+ $this->itemsFromSubmissionList($html, $limit);
+ break;
+ case 'Browse':
+ $data = [
+ 'cat' => $this->getInput('cat'),
+ 'atype' => $this->getInput('atype'),
+ 'species' => $this->getInput('species'),
+ 'gender' => $this->getInput('gender'),
+ 'perpage' => 72,
+ 'rating_general' => ($this->getInput('rating_general') === true ? 'on' : 0),
+ 'rating_mature' => ($this->getInput('rating_mature') === true ? 'on' : 0),
+ 'rating_adult' => ($this->getInput('rating_adult') === true ? 'on' : 0)
+ ];
+ $html = $this->postFASimpleHTMLDOM($data);
+ $limit = (is_int($this->getInput('limit-browse')) ? $this->getInput('limit-browse') : 10);
+ $this->itemsFromSubmissionList($html, $limit);
+ break;
+ case 'Journals':
+ $html = $this->getFASimpleHTMLDOM($this->getURI());
+ $limit = (is_int($this->getInput('limit')) ? $this->getInput('limit') : -1);
+ $this->itemsFromJournalList($html, $limit);
+ break;
+ case 'Single Journal':
+ $html = $this->getFASimpleHTMLDOM($this->getURI());
+ $this->itemsFromJournal($html);
+ break;
+ case 'Gallery':
+ case 'Scraps':
+ case 'Favorites':
+ case 'Gallery Folder':
+ $html = $this->getFASimpleHTMLDOM($this->getURI());
+ $limit = (is_int($this->getInput('limit')) ? $this->getInput('limit') : 10);
+ $this->itemsFromSubmissionList($html, $limit);
+ break;
+ }
+ }
+
+ private function postFASimpleHTMLDOM($data)
+ {
+ $opts = [
+ CURLOPT_CUSTOMREQUEST => 'POST',
+ CURLOPT_POSTFIELDS => http_build_query($data)
+ ];
+ $header = [
+ 'Host: ' . parse_url(self::URI, PHP_URL_HOST),
+ 'Content-Type: application/x-www-form-urlencoded',
+ 'Cookie: ' . self::FA_AUTH_COOKIE
+ ];
+
+ $html = getSimpleHTMLDOM($this->getURI(), $header, $opts);
+ $html = defaultLinkTo($html, $this->getURI());
+
+ return $html;
+ }
+
+ private function getFASimpleHTMLDOM($url, $cache = false)
+ {
+ $header = [
+ 'Cookie: ' . self::FA_AUTH_COOKIE
+ ];
+
+ if ($cache) {
+ $html = getSimpleHTMLDOMCached($url, 86400, $header); // 24 hours
+ } else {
+ $html = getSimpleHTMLDOM($url, $header);
+ }
+
+ $html = defaultLinkTo($html, $url);
+
+ return $html;
+ }
+
+ private function itemsFromJournalList($html, $limit)
+ {
+ foreach ($html->find('table[id^=jid:]') as $journal) {
+ # allows limit = -1 to mean 'unlimited'
+ if ($limit-- === 0) {
+ break;
+ }
+
+ $item = [];
+
+ $this->setReferrerPolicy($journal);
+
+ $item['uri'] = $journal->find('a', 0)->href;
+ $item['title'] = html_entity_decode($journal->find('a', 0)->plaintext);
+ $item['author'] = $this->getInput('username-journals');
+ $item['timestamp'] = strtotime(
+ $journal->find('span.popup_date', 0)->plaintext
+ );
+ $item['content'] = $journal
+ ->find('.alt1 table div.no_overflow', 0)
+ ->innertext;
+
+ $this->items[] = $item;
+ }
+ }
+
+ private function itemsFromJournal($html)
+ {
+ $this->setReferrerPolicy($html);
+ $item = [];
+
+ $item['uri'] = $this->getURI();
+
+ $title = $html->find('.journal-title-box .no_overflow', 0)->plaintext;
+ $title = html_entity_decode($title);
+ $title = trim($title, " \t\n\r\0\x0B" . chr(0xC2) . chr(0xA0));
+ $item['title'] = $title;
+
+ $item['author'] = $html->find('.journal-title-box a', 0)->plaintext;
+ $item['timestamp'] = strtotime(
+ $html->find('.journal-title-box span.popup_date', 0)->plaintext
+ );
+ $item['content'] = $html->find('.journal-body', 0)->innertext;
+
+ $this->items[] = $item;
+ }
+
+ private function itemsFromSubmissionList($html, $limit)
+ {
+ $cache = ($this->getInput('cache') === true);
+
+ foreach ($html->find('section.gallery figure') as $figure) {
+ # allows limit = -1 to mean 'unlimited'
+ if ($limit-- === 0) {
+ break;
+ }
+
+ $item = [];
+
+ $submissionURL = $figure->find('b u a', 0)->href;
+ $imgURL = 'https:' . $figure->find('b u a img', 0)->src;
+
+ $item['uri'] = $submissionURL;
+ $item['title'] = html_entity_decode(
+ $figure->find('figcaption p a[href*=/view/]', 0)->title
+ );
+ $item['author'] = $figure->find('figcaption p a[href*=/user/]', 0)->title;
+
+ if ($this->getInput('full') === true) {
+ $submissionHTML = $this->getFASimpleHTMLDOM($submissionURL, $cache);
+
+ $stats = $submissionHTML->find('.stats-container', 0);
+ $item['timestamp'] = strtotime($stats->find('.popup_date', 0)->title);
+ $item['enclosures'] = [
+ $submissionHTML->find('.actions a[href^=https://d.facdn]', 0)->href
+ ];
+ foreach ($stats->find('#keywords a') as $keyword) {
+ $item['categories'][] = $keyword->plaintext;
+ }
+
+ $previewSrc = $submissionHTML->find('#submissionImg', 0)
+ ->{'data-preview-src'};
+ if ($previewSrc) {
+ $imgURL = 'https:' . $previewSrc;
+ }
+
+ $description = $submissionHTML
+ ->find('.maintable .maintable tr td.alt1', -1);
+ $this->setReferrerPolicy($description);
+ $description = $description->innertext;
+
+ $item['content'] = <<<EOD
<a href="$submissionURL">
<img src="{$imgURL}" referrerpolicy="no-referrer" />
</a>
@@ -905,27 +927,28 @@ class FurAffinityBridge extends BridgeAbstract {
{$description}
</p>
EOD;
- } else {
- $item['content'] = <<<EOD
+ } else {
+ $item['content'] = <<<EOD
<a href="$submissionURL">
<img src="$imgURL" referrerpolicy="no-referrer" />
</a>
EOD;
- }
-
- $this->items[] = $item;
- }
- }
-
- private function setReferrerPolicy(&$html) {
- foreach($html->find('img') as $img) {
- /*
- * Note: Without the no-referrer policy their CDN sometimes denies requests.
- * We can't control this for enclosures sadly.
- * At least tt-rss adds the referrerpolicy on its own.
- * Alternatively we could not use https for images, but that's not ideal.
- */
- $img->referrerpolicy = 'no-referrer';
- }
- }
+ }
+
+ $this->items[] = $item;
+ }
+ }
+
+ private function setReferrerPolicy(&$html)
+ {
+ foreach ($html->find('img') as $img) {
+ /*
+ * Note: Without the no-referrer policy their CDN sometimes denies requests.
+ * We can't control this for enclosures sadly.
+ * At least tt-rss adds the referrerpolicy on its own.
+ * Alternatively we could not use https for images, but that's not ideal.
+ */
+ $img->referrerpolicy = 'no-referrer';
+ }
+ }
}