Browse Source

[InstructableBridge] Add new bridge (#724)

This commit adds a new bridge for http://www.instructables.com. This bridge
currently supports fetching content by category (all categories available 200+),
using available filters (featured, recent, popular, views, contest winners).
LogMANOriginal 5 years ago
parent
commit
4facbf32e3
1 changed files with 370 additions and 0 deletions
  1. 370 0
      bridges/InstructablesBridge.php

+ 370 - 0
bridges/InstructablesBridge.php

@@ -0,0 +1,370 @@
+<?php
+/**
+* This class implements a bridge for http://www.instructables.com, supporting
+* general feeds and feeds by category. Instructables doesn't support HTTPS as
+* of now (23.06.2018), so all connections are insecure!
+*
+* 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';
+	const URI = 'http://www.instructables.com';
+	const DESCRIPTION = 'Returns general feeds and feeds by category';
+	const MAINTAINER = 'logmanoriginal';
+	const PARAMETERS = array(
+		'Category' => array(
+			'category' => array(
+				'name' => 'Category',
+				'type' => 'list',
+				'required' => true,
+				'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',
+				'required' => true,
+				'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(){
+		// Use arbitrary category to receive full list
+		$html = getSimpleHTMLDOM(self::URI . '/technology/');
+
+		foreach($html->find('.channel a') as $channel) {
+			$name = html_entity_decode(trim($channel->innertext));
+
+			// Remove unwanted entities
+			$name = str_replace("'", '', $name);
+			$name = str_replace('&#39;', '', $name);
+
+			$uri = $channel->href;
+
+			$category = explode('/', $uri)[1];
+
+			if(!isset($categories)
+			|| !array_key_exists($category, $categories)
+			|| !in_array($uri, $categories[$category]))
+				$categories[$category][$name] = $uri;
+		}
+
+		// 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>';
+	}
+}