2018-07-21 15:25:13 +02:00
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* This class implements a bridge for http://www.instructables.com, supporting
|
2019-06-27 20:16:53 +02:00
|
|
|
* general feeds and feeds by category.
|
2018-07-21 15:25:13 +02:00
|
|
|
*
|
|
|
|
* Remarks:
|
|
|
|
* - For some reason it is very important to have the category URI end with a
|
|
|
|
* slash, otherwise the site defaults to the main category (i.e. Technology)!
|
|
|
|
* If you need to update the categories list, enable the 'listCategories'
|
|
|
|
* function (see comments below) and run the bridge with format=Html (see page
|
|
|
|
* source)
|
|
|
|
*/
|
|
|
|
class InstructablesBridge extends BridgeAbstract {
|
|
|
|
const NAME = 'Instructables Bridge';
|
2019-06-27 20:16:53 +02:00
|
|
|
const URI = 'https://www.instructables.com';
|
2018-07-21 15:25:13 +02:00
|
|
|
const DESCRIPTION = 'Returns general feeds and feeds by category';
|
|
|
|
const MAINTAINER = 'logmanoriginal';
|
|
|
|
const PARAMETERS = array(
|
|
|
|
'Category' => array(
|
|
|
|
'category' => array(
|
|
|
|
'name' => 'Category',
|
|
|
|
'type' => 'list',
|
|
|
|
'values' => array(
|
|
|
|
'Play' => array(
|
|
|
|
'All' => '/play/',
|
|
|
|
'KNEX' => '/play/knex/',
|
|
|
|
'Offbeat' => '/play/offbeat/',
|
|
|
|
'Lego' => '/play/lego/',
|
|
|
|
'Airsoft' => '/play/airsoft/',
|
|
|
|
'Card Games' => '/play/card-games/',
|
|
|
|
'Guitars' => '/play/guitars/',
|
|
|
|
'Instruments' => '/play/instruments/',
|
|
|
|
'Magic Tricks' => '/play/magic-tricks/',
|
|
|
|
'Minecraft' => '/play/minecraft/',
|
|
|
|
'Music' => '/play/music/',
|
|
|
|
'Nerf' => '/play/nerf/',
|
|
|
|
'Nintendo' => '/play/nintendo/',
|
|
|
|
'Office Supplies' => '/play/office-supplies/',
|
|
|
|
'Paintball' => '/play/paintball/',
|
|
|
|
'Paper Airplanes' => '/play/paper-airplanes/',
|
|
|
|
'Party Tricks' => '/play/party-tricks/',
|
|
|
|
'PlayStation' => '/play/playstation/',
|
|
|
|
'Pranks and Humor' => '/play/pranks-and-humor/',
|
|
|
|
'Puzzles' => '/play/puzzles/',
|
|
|
|
'Siege Engines' => '/play/siege-engines/',
|
|
|
|
'Sports' => '/play/sports/',
|
|
|
|
'Table Top' => '/play/table-top/',
|
|
|
|
'Toys' => '/play/toys/',
|
|
|
|
'Video Games' => '/play/video-games/',
|
|
|
|
'Wii' => '/play/wii/',
|
|
|
|
'Xbox' => '/play/xbox/',
|
|
|
|
'Yo-Yo' => '/play/yo-yo/',
|
|
|
|
),
|
|
|
|
'Craft' => array(
|
|
|
|
'All' => '/craft/',
|
|
|
|
'Art' => '/craft/art/',
|
|
|
|
'Sewing' => '/craft/sewing/',
|
|
|
|
'Paper' => '/craft/paper/',
|
|
|
|
'Jewelry' => '/craft/jewelry/',
|
|
|
|
'Fashion' => '/craft/fashion/',
|
|
|
|
'Books & Journals' => '/craft/books-and-journals/',
|
|
|
|
'Cards' => '/craft/cards/',
|
|
|
|
'Clay' => '/craft/clay/',
|
|
|
|
'Duct Tape' => '/craft/duct-tape/',
|
|
|
|
'Embroidery' => '/craft/embroidery/',
|
|
|
|
'Felt' => '/craft/felt/',
|
|
|
|
'Fiber Arts' => '/craft/fiber-arts/',
|
|
|
|
'Gifts & Wrapping' => '/craft/gifts-and-wrapping/',
|
|
|
|
'Knitting & Crocheting' => '/craft/knitting-and-crocheting/',
|
|
|
|
'Leather' => '/craft/leather/',
|
|
|
|
'Mason Jars' => '/craft/mason-jars/',
|
|
|
|
'No-Sew' => '/craft/no-sew/',
|
|
|
|
'Parties & Weddings' => '/craft/parties-and-weddings/',
|
|
|
|
'Print Making' => '/craft/print-making/',
|
|
|
|
'Soap' => '/craft/soap/',
|
|
|
|
'Wallets' => '/craft/wallets/',
|
|
|
|
),
|
|
|
|
'Technology' => array(
|
|
|
|
'All' => '/technology/',
|
|
|
|
'Electronics' => '/technology/electronics/',
|
|
|
|
'Arduino' => '/technology/arduino/',
|
|
|
|
'Photography' => '/technology/photography/',
|
|
|
|
'Leds' => '/technology/leds/',
|
|
|
|
'Science' => '/technology/science/',
|
|
|
|
'Reuse' => '/technology/reuse/',
|
|
|
|
'Apple' => '/technology/apple/',
|
|
|
|
'Computers' => '/technology/computers/',
|
|
|
|
'3D Printing' => '/technology/3D-Printing/',
|
|
|
|
'Robots' => '/technology/robots/',
|
|
|
|
'Art' => '/technology/art/',
|
|
|
|
'Assistive Tech' => '/technology/assistive-technology/',
|
|
|
|
'Audio' => '/technology/audio/',
|
|
|
|
'Clocks' => '/technology/clocks/',
|
|
|
|
'CNC' => '/technology/cnc/',
|
|
|
|
'Digital Graphics' => '/technology/digital-graphics/',
|
|
|
|
'Gadgets' => '/technology/gadgets/',
|
|
|
|
'Kits' => '/technology/kits/',
|
|
|
|
'Laptops' => '/technology/laptops/',
|
|
|
|
'Lasers' => '/technology/lasers/',
|
|
|
|
'Linux' => '/technology/linux/',
|
|
|
|
'Microcontrollers' => '/technology/microcontrollers/',
|
|
|
|
'Microsoft' => '/technology/microsoft/',
|
|
|
|
'Mobile' => '/technology/mobile/',
|
|
|
|
'Raspberry Pi' => '/technology/raspberry-pi/',
|
|
|
|
'Remote Control' => '/technology/remote-control/',
|
|
|
|
'Sensors' => '/technology/sensors/',
|
|
|
|
'Software' => '/technology/software/',
|
|
|
|
'Soldering' => '/technology/soldering/',
|
|
|
|
'Speakers' => '/technology/speakers/',
|
|
|
|
'Steampunk' => '/technology/steampunk/',
|
|
|
|
'Tools' => '/technology/tools/',
|
|
|
|
'USB' => '/technology/usb/',
|
|
|
|
'Wearables' => '/technology/wearables/',
|
|
|
|
'Websites' => '/technology/websites/',
|
|
|
|
'Wireless' => '/technology/wireless/',
|
|
|
|
),
|
|
|
|
'Workshop' => array(
|
|
|
|
'All' => '/workshop/',
|
|
|
|
'Woodworking' => '/workshop/woodworking/',
|
|
|
|
'Tools' => '/workshop/tools/',
|
|
|
|
'Gardening' => '/workshop/gardening/',
|
|
|
|
'Cars' => '/workshop/cars/',
|
|
|
|
'Metalworking' => '/workshop/metalworking/',
|
|
|
|
'Cardboard' => '/workshop/cardboard/',
|
|
|
|
'Electric Vehicles' => '/workshop/electric-vehicles/',
|
|
|
|
'Energy' => '/workshop/energy/',
|
|
|
|
'Furniture' => '/workshop/furniture/',
|
|
|
|
'Home Improvement' => '/workshop/home-improvement/',
|
|
|
|
'Home Theater' => '/workshop/home-theater/',
|
|
|
|
'Hydroponics' => '/workshop/hydroponics/',
|
|
|
|
'Laser Cutting' => '/workshop/laser-cutting/',
|
|
|
|
'Lighting' => '/workshop/lighting/',
|
|
|
|
'Molds & Casting' => '/workshop/molds-and-casting/',
|
|
|
|
'Motorcycles' => '/workshop/motorcycles/',
|
|
|
|
'Organizing' => '/workshop/organizing/',
|
|
|
|
'Pallets' => '/workshop/pallets/',
|
|
|
|
'Repair' => '/workshop/repair/',
|
|
|
|
'Shelves' => '/workshop/shelves/',
|
|
|
|
'Solar' => '/workshop/solar/',
|
|
|
|
'Workbenches' => '/workshop/workbenches/',
|
|
|
|
),
|
|
|
|
'Home' => array(
|
|
|
|
'All' => '/home/',
|
|
|
|
'Halloween' => '/home/halloween/',
|
|
|
|
'Decorating' => '/home/decorating/',
|
|
|
|
'Organizing' => '/home/organizing/',
|
|
|
|
'Pets' => '/home/pets/',
|
|
|
|
'Life Hacks' => '/home/life-hacks/',
|
|
|
|
'Beauty' => '/home/beauty/',
|
|
|
|
'Christmas' => '/home/christmas/',
|
|
|
|
'Cleaning' => '/home/cleaning/',
|
|
|
|
'Education' => '/home/education/',
|
|
|
|
'Finances' => '/home/finances/',
|
|
|
|
'Gardening' => '/home/gardening/',
|
|
|
|
'Green' => '/home/green/',
|
|
|
|
'Health' => '/home/health/',
|
|
|
|
'Hiding Places' => '/home/hiding-places/',
|
|
|
|
'Holidays' => '/home/holidays/',
|
|
|
|
'Homesteading' => '/home/homesteading/',
|
|
|
|
'Kids' => '/home/kids/',
|
|
|
|
'Kitchen' => '/home/kitchen/',
|
|
|
|
'Life Skills' => '/home/life-skills/',
|
|
|
|
'Parenting' => '/home/parenting/',
|
|
|
|
'Pest Control' => '/home/pest-control/',
|
|
|
|
'Relationships' => '/home/relationships/',
|
|
|
|
'Reuse' => '/home/reuse/',
|
|
|
|
'Travel' => '/home/travel/',
|
|
|
|
),
|
|
|
|
'Outside' => array(
|
|
|
|
'All' => '/outside/',
|
|
|
|
'Bikes' => '/outside/bikes/',
|
|
|
|
'Survival' => '/outside/survival/',
|
|
|
|
'Backyard' => '/outside/backyard/',
|
|
|
|
'Beach' => '/outside/beach/',
|
|
|
|
'Birding' => '/outside/birding/',
|
|
|
|
'Boats' => '/outside/boats/',
|
|
|
|
'Camping' => '/outside/camping/',
|
|
|
|
'Climbing' => '/outside/climbing/',
|
|
|
|
'Fire' => '/outside/fire/',
|
|
|
|
'Fishing' => '/outside/fishing/',
|
|
|
|
'Hunting' => '/outside/hunting/',
|
|
|
|
'Kites' => '/outside/kites/',
|
|
|
|
'Knives' => '/outside/knives/',
|
|
|
|
'Knots' => '/outside/knots/',
|
|
|
|
'Paracord' => '/outside/paracord/',
|
|
|
|
'Rockets' => '/outside/rockets/',
|
|
|
|
'Skateboarding' => '/outside/skateboarding/',
|
|
|
|
'Snow' => '/outside/snow/',
|
|
|
|
'Water' => '/outside/water/',
|
|
|
|
),
|
|
|
|
'Food' => array(
|
|
|
|
'All' => '/food/',
|
|
|
|
'Dessert' => '/food/dessert/',
|
|
|
|
'Snacks & Appetizers' => '/food/snacks-and-appetizers/',
|
|
|
|
'Bacon' => '/food/bacon/',
|
|
|
|
'BBQ & Grilling' => '/food/bbq-and-grilling/',
|
|
|
|
'Beverages' => '/food/beverages/',
|
|
|
|
'Bread' => '/food/bread/',
|
|
|
|
'Breakfast' => '/food/breakfast/',
|
|
|
|
'Cake' => '/food/cake/',
|
|
|
|
'Candy' => '/food/candy/',
|
|
|
|
'Canning & Preserves' => '/food/canning-and-preserves/',
|
|
|
|
'Cocktails & Mocktails' => '/food/cocktails-and-mocktails/',
|
|
|
|
'Coffee' => '/food/coffee/',
|
|
|
|
'Cookies' => '/food/cookies/',
|
|
|
|
'Cupcakes' => '/food/cupcakes/',
|
|
|
|
'Homebrew' => '/food/homebrew/',
|
|
|
|
'Main Course' => '/food/main-course/',
|
|
|
|
'Pasta' => '/food/pasta/',
|
|
|
|
'Pie' => '/food/pie/',
|
|
|
|
'Pizza' => '/food/pizza/',
|
|
|
|
'Salad' => '/food/salad/',
|
|
|
|
'Sandwiches' => '/food/sandwiches/',
|
|
|
|
'Soups & Stews' => '/food/soups-and-stews/',
|
|
|
|
'Vegetarian & Vegan' => '/food/vegetarian-and-vegan/',
|
|
|
|
),
|
|
|
|
'Costumes' => array(
|
|
|
|
'All' => '/costumes/',
|
|
|
|
'Props' => '/costumes/props-and-accessories/',
|
|
|
|
'Animals' => '/costumes/animals/',
|
|
|
|
'Comics' => '/costumes/comics/',
|
|
|
|
'Fantasy' => '/costumes/fantasy/',
|
|
|
|
'For Kids' => '/costumes/for-kids/',
|
|
|
|
'For Pets' => '/costumes/for-pets/',
|
|
|
|
'Funny' => '/costumes/funny/',
|
|
|
|
'Games' => '/costumes/games/',
|
|
|
|
'Historic & Futuristic' => '/costumes/historic-and-futuristic/',
|
|
|
|
'Makeup' => '/costumes/makeup/',
|
|
|
|
'Masks' => '/costumes/masks/',
|
|
|
|
'Scary' => '/costumes/scary/',
|
|
|
|
'TV & Movies' => '/costumes/tv-and-movies/',
|
|
|
|
'Weapons & Armor' => '/costumes/weapons-and-armor/',
|
|
|
|
)
|
|
|
|
),
|
|
|
|
'title' => 'Select your category (required)',
|
|
|
|
'defaultValue' => 'Technology'
|
|
|
|
),
|
|
|
|
'filter' => array(
|
|
|
|
'name' => 'Filter',
|
|
|
|
'type' => 'list',
|
|
|
|
'values' => array(
|
|
|
|
'Featured' => ' ',
|
|
|
|
'Recent' => 'recent/',
|
|
|
|
'Popular' => 'popular/',
|
|
|
|
'Views' => 'views/',
|
|
|
|
'Contest Winners' => 'winners/'
|
|
|
|
),
|
|
|
|
'title' => 'Select a filter',
|
|
|
|
'defaultValue' => 'Featured'
|
|
|
|
)
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
private $uri;
|
|
|
|
|
|
|
|
public function collectData() {
|
|
|
|
// Enable the following line to get the category list (dev mode)
|
|
|
|
// $this->listCategories();
|
|
|
|
|
|
|
|
$this->uri = static::URI;
|
|
|
|
|
|
|
|
switch($this->queriedContext) {
|
|
|
|
case 'Category': $this->uri .= $this->getInput('category') . $this->getInput('filter');
|
|
|
|
}
|
|
|
|
|
|
|
|
$html = getSimpleHTMLDOM($this->uri)
|
|
|
|
or returnServerError('Error loading category ' . $this->uri);
|
|
|
|
|
|
|
|
foreach($html->find('ul.explore-covers-list li') as $cover) {
|
|
|
|
$item = array();
|
|
|
|
|
|
|
|
$item['uri'] = static::URI . $cover->find('a.cover-image', 0)->href;
|
|
|
|
$item['title'] = $cover->find('.title', 0)->innertext;
|
|
|
|
$item['author'] = $this->getCategoryAuthor($cover);
|
|
|
|
$item['content'] = '<a href='
|
|
|
|
. $item['uri']
|
|
|
|
. '><img src='
|
|
|
|
. $cover->find('a.cover-image img', 0)->src
|
|
|
|
. '></a>';
|
|
|
|
|
|
|
|
$image = str_replace('.RECTANGLE1', '.LARGE', $cover->find('a.cover-image img', 0)->src);
|
|
|
|
$item['enclosures'] = [$image];
|
|
|
|
|
|
|
|
$this->items[] = $item;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getName() {
|
|
|
|
if(!is_null($this->getInput('category'))
|
|
|
|
&& !is_null($this->getInput('filter'))) {
|
|
|
|
foreach(self::PARAMETERS[$this->queriedContext]['category']['values'] as $key => $value) {
|
|
|
|
$subcategory = array_search($this->getInput('category'), $value);
|
|
|
|
|
|
|
|
if($subcategory !== false)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
$filter = array_search(
|
|
|
|
$this->getInput('filter'),
|
|
|
|
self::PARAMETERS[$this->queriedContext]['filter']['values']
|
|
|
|
);
|
|
|
|
|
|
|
|
return $subcategory . ' (' . $filter . ') - ' . static::NAME;
|
|
|
|
}
|
|
|
|
|
|
|
|
return parent::getName();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getURI() {
|
|
|
|
if(!is_null($this->getInput('category'))
|
|
|
|
&& !is_null($this->getInput('filter'))) {
|
|
|
|
return $this->uri;
|
|
|
|
}
|
|
|
|
|
|
|
|
return parent::getURI();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a list of categories for development purposes (used to build the
|
|
|
|
* parameters list)
|
|
|
|
*/
|
|
|
|
private function listCategories(){
|
|
|
|
|
2019-06-27 20:28:23 +02:00
|
|
|
// Use home page to acquire main categories
|
|
|
|
$html = getSimpleHTMLDOM(self::URI);
|
|
|
|
$html = defaultLinkTo($html, self::URI);
|
2018-07-21 15:25:13 +02:00
|
|
|
|
2019-06-27 20:28:23 +02:00
|
|
|
foreach($html->find('.home-content-explore-link') as $category) {
|
2018-07-21 15:25:13 +02:00
|
|
|
|
2019-06-27 20:28:23 +02:00
|
|
|
// Use arbitrary category to receive full list
|
|
|
|
$html = getSimpleHTMLDOM($category->href);
|
2018-07-21 15:25:13 +02:00
|
|
|
|
2019-06-27 20:28:23 +02:00
|
|
|
foreach($html->find('.channel-thumbnail a') as $channel) {
|
|
|
|
$name = html_entity_decode(trim($channel->title));
|
2018-07-21 15:25:13 +02:00
|
|
|
|
2019-06-27 20:28:23 +02:00
|
|
|
// Remove unwanted entities
|
|
|
|
$name = str_replace("'", '', $name);
|
|
|
|
$name = str_replace(''', '', $name);
|
|
|
|
|
|
|
|
$uri = $channel->href;
|
|
|
|
|
|
|
|
$category_name = explode('/', $uri)[1];
|
|
|
|
|
|
|
|
if(!isset($categories)
|
|
|
|
|| !array_key_exists($category_name, $categories)
|
|
|
|
|| !in_array($uri, $categories[$category_name]))
|
|
|
|
$categories[$category_name][$name] = $uri;
|
|
|
|
}
|
2018-07-21 15:25:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Build PHP array manually
|
|
|
|
foreach($categories as $key => $value) {
|
|
|
|
$name = ucfirst($key);
|
|
|
|
echo "'{$name}' => array(\n";
|
|
|
|
echo "\t'All' => '/{$key}/',\n";
|
|
|
|
foreach($value as $name => $uri) {
|
|
|
|
echo "\t'{$name}' => '{$uri}',\n";
|
|
|
|
}
|
|
|
|
echo "),\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
die;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the author as anchor for a given cover.
|
|
|
|
*/
|
|
|
|
private function getCategoryAuthor($cover) {
|
|
|
|
return '<a href='
|
|
|
|
. static::URI . $cover->find('span.author a', 0)->href
|
|
|
|
. '>'
|
|
|
|
. $cover->find('span.author a', 0)->innertext
|
|
|
|
. '</a>';
|
|
|
|
}
|
|
|
|
}
|