From b7d5672d68b0d824354249534ea47fa64803751d Mon Sep 17 00:00:00 2001 From: Johannes Schauer Date: Fri, 4 Aug 2017 22:06:01 +0200 Subject: Import rss-bridge_2017-08-03-1.debian.tar.xz [dgit import tarball rss-bridge 2017-08-03-1 rss-bridge_2017-08-03-1.debian.tar.xz] --- changelog | 5 +++++ compat | 1 + control | 20 ++++++++++++++++++++ copyright | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ rss-bridge.dirs | 1 + rss-bridge.docs | 1 + rss-bridge.install | 8 ++++++++ rss-bridge.links | 2 ++ rules | 11 +++++++++++ source/format | 1 + whitelist.txt | 16 ++++++++++++++++ 11 files changed, 118 insertions(+) create mode 100644 changelog create mode 100644 compat create mode 100644 control create mode 100644 copyright create mode 100644 rss-bridge.dirs create mode 100644 rss-bridge.docs create mode 100644 rss-bridge.install create mode 100644 rss-bridge.links create mode 100755 rules create mode 100644 source/format create mode 100644 whitelist.txt diff --git a/changelog b/changelog new file mode 100644 index 0000000..52d9b8a --- /dev/null +++ b/changelog @@ -0,0 +1,5 @@ +rss-bridge (2017-08-03-1) unstable; urgency=medium + + * Initial release. (Closes: #870687) + + -- Johannes Schauer Fri, 04 Aug 2017 22:06:01 +0200 diff --git a/compat b/compat new file mode 100644 index 0000000..f599e28 --- /dev/null +++ b/compat @@ -0,0 +1 @@ +10 diff --git a/control b/control new file mode 100644 index 0000000..30f3346 --- /dev/null +++ b/control @@ -0,0 +1,20 @@ +Source: rss-bridge +Section: web +Priority: extra +Maintainer: Johannes Schauer +Homepage: https://github.com/RSS-Bridge/rss-bridge +Vcs-Browser: https://browse.dgit.debian.org/rss-bridge.git/ +Vcs-Git: https://git.dgit.debian.org/rss-bridge +Standards-Version: 4.0.0 +Build-Depends: debhelper (>= 10) + +Package: rss-bridge +Architecture: all +Depends: ${misc:Depends}, php5-curl, php5-json, nginx | httpd +Description: web service generating ATOM feeds for websites that don't have them + Provides a PHP web service which generates ATOM feeds for facebook, twitter, + youtube, flickr, google, instagram, pinterest and more than 130 other web + services which do not provide ATOM or RSS feeds themselves. The software acts + as a proxy between the web service and an RSS reader software. It scrapes + their publicly available HTML pages to extract the necessary information. + To prevent banning, the results are cached. diff --git a/copyright b/copyright new file mode 100644 index 0000000..99c89e4 --- /dev/null +++ b/copyright @@ -0,0 +1,52 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: rss-bridge +Upstream-Contact: https://github.com/RSS-Bridge/rss-bridge/issues +Source: https://github.com/RSS-Bridge/rss-bridge + +Files: * +Copyright: 2013-2014 sebsauvage + 2014-2017 Mitsukarenai + 2016-2017 Pierre Mazière + 2015-2017 logmanoriginal +License: public-domain + This is free and unencumbered software released into the public domain. + . + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + . + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + +Files: debian/* +Copyright: 2017 Johannes Schauer +License: Expat + +Files: vendor/simplehtmldom/simple_html_dom.php +Copyright: S.C. Chen + John Schlick + Rus Carroll + Jose Solorzano + Yousuke Kumakura + Vadim Voituk + Antcs +License: Expat +Comment: https://sourceforge.net/p/simplehtmldom/feature-requests/47/ + +License: Expat + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + . + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. diff --git a/rss-bridge.dirs b/rss-bridge.dirs new file mode 100644 index 0000000..72c62be --- /dev/null +++ b/rss-bridge.dirs @@ -0,0 +1 @@ +/var/cache/rss-bridge diff --git a/rss-bridge.docs b/rss-bridge.docs new file mode 100644 index 0000000..b43bf86 --- /dev/null +++ b/rss-bridge.docs @@ -0,0 +1 @@ +README.md diff --git a/rss-bridge.install b/rss-bridge.install new file mode 100644 index 0000000..266ba42 --- /dev/null +++ b/rss-bridge.install @@ -0,0 +1,8 @@ +./bridges /usr/share/rss-bridge +./lib /usr/share/rss-bridge +./index.php /usr/share/rss-bridge +./formats /usr/share/rss-bridge +./caches /usr/share/rss-bridge +./static /usr/share/rss-bridge +./vendor /usr/share/rss-bridge +debian/whitelist.txt /etc/rss-bridge diff --git a/rss-bridge.links b/rss-bridge.links new file mode 100644 index 0000000..fce5361 --- /dev/null +++ b/rss-bridge.links @@ -0,0 +1,2 @@ +/var/cache/rss-bridge /usr/share/rss-bridge/cache +/etc/rss-bridge/whitelist.txt /usr/share/rss-bridge/whitelist.txt diff --git a/rules b/rules new file mode 100755 index 0000000..27263de --- /dev/null +++ b/rules @@ -0,0 +1,11 @@ +#!/usr/bin/make -f +%: + dh $@ + +override_dh_install: + dh_install + install -d -o www-data -g www-data $(CURDIR)/debian/rss-bridge/var/cache/rss-bridge + chmod -x $(CURDIR)/debian/rss-bridge/usr/share/rss-bridge/bridges/LeBonCoinBridge.php + +override_dh_fixperms: + dh_fixperms --exclude /var/cache/rss-bridge diff --git a/source/format b/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/whitelist.txt b/whitelist.txt new file mode 100644 index 0000000..ffb536f --- /dev/null +++ b/whitelist.txt @@ -0,0 +1,16 @@ +BandcampBridge +CryptomeBridge +DansTonChatBridge +DuckDuckGoBridge +FacebookBridge +FlickrExploreBridge +GooglePlusPostBridge +GoogleSearchBridge +IdenticaBridge +InstagramBridge +OpenClassroomsBridge +PinterestBridge +ScmbBridge +TwitterBridge +WikipediaBridge +YoutubeBridge -- cgit v1.2.3 From b005331cd910c0cc7dee2ddf82491b8248f431cf Mon Sep 17 00:00:00 2001 From: Johannes Schauer Date: Fri, 4 Aug 2017 22:06:01 +0200 Subject: Import rss-bridge_2017-08-03.orig.tar.gz [dgit import orig rss-bridge_2017-08-03.orig.tar.gz] --- .gitattributes | 22 + .gitignore | 234 ++++ .travis.yml | 19 + CHANGELOG.md | 233 ++++ CONTRIBUTING.md | 47 + README.md | 142 +++ UNLICENSE | 25 + bridges/ABCTabsBridge.php | 42 + bridges/AcrimedBridge.php | 25 + bridges/AllocineFRBridge.php | 87 ++ bridges/AmazonBridge.php | 94 ++ bridges/AnimeUltimeBridge.php | 135 +++ bridges/Arte7Bridge.php | 102 ++ bridges/AskfmBridge.php | 74 ++ bridges/BandcampBridge.php | 63 ++ bridges/BastaBridge.php | 34 + bridges/BlaguesDeMerdeBridge.php | 31 + bridges/BooruprojectBridge.php | 45 + bridges/CADBridge.php | 45 + bridges/CNETBridge.php | 93 ++ bridges/CastorusBridge.php | 118 ++ bridges/CollegeDeFranceBridge.php | 84 ++ bridges/CommonDreamsBridge.php | 26 + bridges/CopieDoubleBridge.php | 35 + bridges/CourrierInternationalBridge.php | 55 + bridges/CpasbienBridge.php | 74 ++ bridges/CryptomeBridge.php | 45 + bridges/DailymotionBridge.php | 123 +++ bridges/DanbooruBridge.php | 62 ++ bridges/DansTonChatBridge.php | 23 + bridges/DauphineLibereBridge.php | 56 + bridges/DemoBridge.php | 46 + bridges/DeveloppezDotComBridge.php | 47 + bridges/DiceBridge.php | 120 ++ bridges/DilbertBridge.php | 36 + bridges/DollbooruBridge.php | 9 + bridges/DuckDuckGoBridge.php | 42 + bridges/EZTVBridge.php | 67 ++ bridges/EliteDangerousGalnetBridge.php | 35 + bridges/ElsevierBridge.php | 75 ++ bridges/EstCeQuonMetEnProdBridge.php | 37 + bridges/EtsyBridge.php | 83 ++ bridges/FB2Bridge.php | 281 +++++ bridges/FacebookBridge.php | 305 ++++++ bridges/FeedExpanderExampleBridge.php | 62 ++ bridges/FierPandaBridge.php | 24 + bridges/FilterBridge.php | 77 ++ bridges/FlickrBridge.php | 120 ++ bridges/FootitoBridge.php | 75 ++ bridges/FourchanBridge.php | 78 ++ bridges/FuturaSciencesBridge.php | 173 +++ bridges/GBAtempBridge.php | 157 +++ bridges/GelbooruBridge.php | 22 + bridges/GiphyBridge.php | 76 ++ bridges/GithubIssueBridge.php | 192 ++++ bridges/GithubSearchBridge.php | 50 + bridges/GizmodoBridge.php | 36 + bridges/GoComicsBridge.php | 59 + bridges/GooglePlusPostBridge.php | 111 ++ bridges/GoogleSearchBridge.php | 64 ++ bridges/HDWallpapersBridge.php | 83 ++ bridges/HentaiHavenBridge.php | 37 + bridges/IdenticaBridge.php | 52 + bridges/InstagramBridge.php | 93 ++ bridges/IsoHuntBridge.php | 465 ++++++++ bridges/JapanExpoBridge.php | 100 ++ bridges/KATBridge.php | 123 +++ bridges/KernelBugTrackerBridge.php | 152 +++ bridges/KonachanBridge.php | 11 + bridges/KoreusBridge.php | 22 + bridges/KununuBridge.php | 249 +++++ bridges/LWNprevBridge.php | 265 +++++ bridges/LeBonCoinBridge.php | 190 ++++ bridges/LeMondeInformatiqueBridge.php | 44 + bridges/LegifranceJOBridge.php | 68 ++ bridges/LesJoiesDuCodeBridge.php | 45 + bridges/LichessBridge.php | 31 + bridges/LinkedInCompanyBridge.php | 37 + bridges/LolibooruBridge.php | 11 + bridges/MangareaderBridge.php | 249 +++++ bridges/MilbooruBridge.php | 11 + bridges/MixCloudBridge.php | 52 + bridges/MoebooruBridge.php | 56 + bridges/MoinMoinBridge.php | 327 ++++++ bridges/MondeDiploBridge.php | 26 + bridges/MsnMondeBridge.php | 35 + bridges/MspabooruBridge.php | 12 + bridges/NasaApodBridge.php | 44 + bridges/NeuviemeArtBridge.php | 57 + bridges/NextInpactBridge.php | 34 + bridges/NextgovBridge.php | 74 ++ bridges/NiceMatinBridge.php | 32 + bridges/NovelUpdatesBridge.php | 69 ++ bridges/OpenClassroomsBridge.php | 49 + bridges/ParuVenduImmoBridge.php | 102 ++ bridges/PickyWallpapersBridge.php | 101 ++ bridges/PinterestBridge.php | 163 +++ bridges/PlanetLibreBridge.php | 38 + bridges/RTBFBridge.php | 66 ++ bridges/RainbowSixSiegeBridge.php | 36 + bridges/ReadComicsBridge.php | 44 + bridges/Releases3DSBridge.php | 136 +++ bridges/ReporterreBridge.php | 47 + bridges/Rue89Bridge.php | 25 + bridges/Rule34Bridge.php | 12 + bridges/Rule34pahealBridge.php | 10 + bridges/SafebooruBridge.php | 12 + bridges/SakugabooruBridge.php | 11 + bridges/ScmbBridge.php | 39 + bridges/ScoopItBridge.php | 42 + bridges/SensCritiqueBridge.php | 97 ++ bridges/SexactuBridge.php | 88 ++ bridges/ShanaprojectBridge.php | 123 +++ bridges/Shimmie2Bridge.php | 39 + bridges/SoundcloudBridge.php | 64 ++ bridges/SteamBridge.php | 129 +++ bridges/StripeAPIChangeLogBridge.php | 23 + bridges/SuperbWallpapersBridge.php | 70 ++ bridges/T411Bridge.php | 96 ++ bridges/TagBoardBridge.php | 49 + bridges/TbibBridge.php | 12 + bridges/TheCodingLoveBridge.php | 46 + bridges/TheHackerNewsBridge.php | 80 ++ bridges/ThePirateBayBridge.php | 174 +++ bridges/TheTVDBBridge.php | 205 ++++ bridges/Torrent9Bridge.php | 102 ++ bridges/TwitterBridge.php | 287 +++++ bridges/UnsplashBridge.php | 77 ++ bridges/UsbekEtRicaBridge.php | 110 ++ bridges/ViadeoCompanyBridge.php | 37 + bridges/VineBridge.php | 40 + bridges/VkBridge.php | 66 ++ bridges/WallpaperStopBridge.php | 107 ++ bridges/WeLiveSecurityBridge.php | 45 + bridges/WebfailBridge.php | 149 +++ bridges/WhydBridge.php | 56 + bridges/WikiLeaksBridge.php | 129 +++ bridges/WikipediaBridge.php | 304 ++++++ bridges/WordPressBridge.php | 76 ++ bridges/WordPressPluginUpdateBridge.php | 87 ++ bridges/WorldOfTanksBridge.php | 72 ++ bridges/XbooruBridge.php | 12 + bridges/YandereBridge.php | 11 + bridges/YoutubeBridge.php | 189 ++++ bridges/ZDNetBridge.php | 302 ++++++ cache/.gitkeep | 0 caches/FileCache.php | 119 ++ formats/AtomFormat.php | 93 ++ formats/HtmlFormat.php | 100 ++ formats/JsonFormat.php | 25 + formats/MrssFormat.php | 106 ++ formats/PlaintextFormat.php | 25 + index.php | 268 +++++ lib/Bridge.php | 105 ++ lib/BridgeAbstract.php | 271 +++++ lib/BridgeInterface.php | 71 ++ lib/Cache.php | 53 + lib/CacheInterface.php | 7 + lib/Exceptions.php | 209 ++++ lib/FeedExpander.php | 208 ++++ lib/Format.php | 73 ++ lib/FormatAbstract.php | 106 ++ lib/FormatInterface.php | 11 + lib/RssBridge.php | 53 + lib/contents.php | 132 +++ lib/error.php | 28 + lib/html.php | 327 ++++++ lib/validation.php | 100 ++ phpcs.xml | 73 ++ scalingo.json | 6 + static/HtmlFormat.css | 113 ++ static/search.js | 27 + static/style.css | 280 +++++ vendor/simplehtmldom/simple_html_dom.php | 1742 ++++++++++++++++++++++++++++++ 174 files changed, 17225 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTING.md create mode 100644 README.md create mode 100644 UNLICENSE create mode 100644 bridges/ABCTabsBridge.php create mode 100644 bridges/AcrimedBridge.php create mode 100644 bridges/AllocineFRBridge.php create mode 100644 bridges/AmazonBridge.php create mode 100644 bridges/AnimeUltimeBridge.php create mode 100644 bridges/Arte7Bridge.php create mode 100644 bridges/AskfmBridge.php create mode 100644 bridges/BandcampBridge.php create mode 100644 bridges/BastaBridge.php create mode 100644 bridges/BlaguesDeMerdeBridge.php create mode 100644 bridges/BooruprojectBridge.php create mode 100644 bridges/CADBridge.php create mode 100644 bridges/CNETBridge.php create mode 100644 bridges/CastorusBridge.php create mode 100644 bridges/CollegeDeFranceBridge.php create mode 100644 bridges/CommonDreamsBridge.php create mode 100644 bridges/CopieDoubleBridge.php create mode 100644 bridges/CourrierInternationalBridge.php create mode 100644 bridges/CpasbienBridge.php create mode 100644 bridges/CryptomeBridge.php create mode 100644 bridges/DailymotionBridge.php create mode 100644 bridges/DanbooruBridge.php create mode 100644 bridges/DansTonChatBridge.php create mode 100644 bridges/DauphineLibereBridge.php create mode 100644 bridges/DemoBridge.php create mode 100644 bridges/DeveloppezDotComBridge.php create mode 100644 bridges/DiceBridge.php create mode 100644 bridges/DilbertBridge.php create mode 100644 bridges/DollbooruBridge.php create mode 100644 bridges/DuckDuckGoBridge.php create mode 100644 bridges/EZTVBridge.php create mode 100644 bridges/EliteDangerousGalnetBridge.php create mode 100644 bridges/ElsevierBridge.php create mode 100644 bridges/EstCeQuonMetEnProdBridge.php create mode 100644 bridges/EtsyBridge.php create mode 100644 bridges/FB2Bridge.php create mode 100644 bridges/FacebookBridge.php create mode 100644 bridges/FeedExpanderExampleBridge.php create mode 100644 bridges/FierPandaBridge.php create mode 100644 bridges/FilterBridge.php create mode 100644 bridges/FlickrBridge.php create mode 100644 bridges/FootitoBridge.php create mode 100644 bridges/FourchanBridge.php create mode 100644 bridges/FuturaSciencesBridge.php create mode 100644 bridges/GBAtempBridge.php create mode 100644 bridges/GelbooruBridge.php create mode 100644 bridges/GiphyBridge.php create mode 100644 bridges/GithubIssueBridge.php create mode 100644 bridges/GithubSearchBridge.php create mode 100644 bridges/GizmodoBridge.php create mode 100644 bridges/GoComicsBridge.php create mode 100644 bridges/GooglePlusPostBridge.php create mode 100644 bridges/GoogleSearchBridge.php create mode 100644 bridges/HDWallpapersBridge.php create mode 100644 bridges/HentaiHavenBridge.php create mode 100644 bridges/IdenticaBridge.php create mode 100644 bridges/InstagramBridge.php create mode 100644 bridges/IsoHuntBridge.php create mode 100644 bridges/JapanExpoBridge.php create mode 100644 bridges/KATBridge.php create mode 100644 bridges/KernelBugTrackerBridge.php create mode 100644 bridges/KonachanBridge.php create mode 100644 bridges/KoreusBridge.php create mode 100644 bridges/KununuBridge.php create mode 100644 bridges/LWNprevBridge.php create mode 100755 bridges/LeBonCoinBridge.php create mode 100644 bridges/LeMondeInformatiqueBridge.php create mode 100644 bridges/LegifranceJOBridge.php create mode 100644 bridges/LesJoiesDuCodeBridge.php create mode 100644 bridges/LichessBridge.php create mode 100644 bridges/LinkedInCompanyBridge.php create mode 100644 bridges/LolibooruBridge.php create mode 100644 bridges/MangareaderBridge.php create mode 100644 bridges/MilbooruBridge.php create mode 100644 bridges/MixCloudBridge.php create mode 100644 bridges/MoebooruBridge.php create mode 100644 bridges/MoinMoinBridge.php create mode 100644 bridges/MondeDiploBridge.php create mode 100644 bridges/MsnMondeBridge.php create mode 100644 bridges/MspabooruBridge.php create mode 100644 bridges/NasaApodBridge.php create mode 100644 bridges/NeuviemeArtBridge.php create mode 100644 bridges/NextInpactBridge.php create mode 100644 bridges/NextgovBridge.php create mode 100644 bridges/NiceMatinBridge.php create mode 100644 bridges/NovelUpdatesBridge.php create mode 100644 bridges/OpenClassroomsBridge.php create mode 100644 bridges/ParuVenduImmoBridge.php create mode 100644 bridges/PickyWallpapersBridge.php create mode 100644 bridges/PinterestBridge.php create mode 100644 bridges/PlanetLibreBridge.php create mode 100644 bridges/RTBFBridge.php create mode 100644 bridges/RainbowSixSiegeBridge.php create mode 100644 bridges/ReadComicsBridge.php create mode 100644 bridges/Releases3DSBridge.php create mode 100644 bridges/ReporterreBridge.php create mode 100644 bridges/Rue89Bridge.php create mode 100644 bridges/Rule34Bridge.php create mode 100644 bridges/Rule34pahealBridge.php create mode 100644 bridges/SafebooruBridge.php create mode 100644 bridges/SakugabooruBridge.php create mode 100644 bridges/ScmbBridge.php create mode 100644 bridges/ScoopItBridge.php create mode 100644 bridges/SensCritiqueBridge.php create mode 100644 bridges/SexactuBridge.php create mode 100644 bridges/ShanaprojectBridge.php create mode 100644 bridges/Shimmie2Bridge.php create mode 100644 bridges/SoundcloudBridge.php create mode 100644 bridges/SteamBridge.php create mode 100644 bridges/StripeAPIChangeLogBridge.php create mode 100644 bridges/SuperbWallpapersBridge.php create mode 100644 bridges/T411Bridge.php create mode 100644 bridges/TagBoardBridge.php create mode 100644 bridges/TbibBridge.php create mode 100644 bridges/TheCodingLoveBridge.php create mode 100644 bridges/TheHackerNewsBridge.php create mode 100644 bridges/ThePirateBayBridge.php create mode 100644 bridges/TheTVDBBridge.php create mode 100644 bridges/Torrent9Bridge.php create mode 100644 bridges/TwitterBridge.php create mode 100644 bridges/UnsplashBridge.php create mode 100644 bridges/UsbekEtRicaBridge.php create mode 100644 bridges/ViadeoCompanyBridge.php create mode 100644 bridges/VineBridge.php create mode 100644 bridges/VkBridge.php create mode 100644 bridges/WallpaperStopBridge.php create mode 100644 bridges/WeLiveSecurityBridge.php create mode 100644 bridges/WebfailBridge.php create mode 100644 bridges/WhydBridge.php create mode 100644 bridges/WikiLeaksBridge.php create mode 100644 bridges/WikipediaBridge.php create mode 100644 bridges/WordPressBridge.php create mode 100644 bridges/WordPressPluginUpdateBridge.php create mode 100644 bridges/WorldOfTanksBridge.php create mode 100644 bridges/XbooruBridge.php create mode 100644 bridges/YandereBridge.php create mode 100644 bridges/YoutubeBridge.php create mode 100644 bridges/ZDNetBridge.php create mode 100644 cache/.gitkeep create mode 100644 caches/FileCache.php create mode 100644 formats/AtomFormat.php create mode 100644 formats/HtmlFormat.php create mode 100644 formats/JsonFormat.php create mode 100644 formats/MrssFormat.php create mode 100644 formats/PlaintextFormat.php create mode 100644 index.php create mode 100644 lib/Bridge.php create mode 100644 lib/BridgeAbstract.php create mode 100644 lib/BridgeInterface.php create mode 100644 lib/Cache.php create mode 100644 lib/CacheInterface.php create mode 100644 lib/Exceptions.php create mode 100644 lib/FeedExpander.php create mode 100644 lib/Format.php create mode 100644 lib/FormatAbstract.php create mode 100644 lib/FormatInterface.php create mode 100644 lib/RssBridge.php create mode 100644 lib/contents.php create mode 100644 lib/error.php create mode 100644 lib/html.php create mode 100644 lib/validation.php create mode 100644 phpcs.xml create mode 100644 scalingo.json create mode 100644 static/HtmlFormat.css create mode 100644 static/search.js create mode 100644 static/style.css create mode 100644 vendor/simplehtmldom/simple_html_dom.php diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..412eeda --- /dev/null +++ b/.gitattributes @@ -0,0 +1,22 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d28d273 --- /dev/null +++ b/.gitignore @@ -0,0 +1,234 @@ +################# +## Eclipse +################# +vendor/* +data/ +*.pydevproject +.project +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.classpath +.settings/ +.loadpath + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + + +################# +## Visual Studio +################# + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results + +[Dd]ebug/ +[Rr]elease/ +x64/ +build/ +[Bb]in/ +[Oo]bj/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.log +*.scc + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +*.ncrunch* +.*crunch*.local.xml + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.Publish.xml +*.pubxml + +# NuGet Packages Directory +## TODO: If you have NuGet Package Restore enabled, uncomment the next line +#packages/ + +# Windows Azure Build Output +csx +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.[Pp]ublish.xml +*.pfx +*.publishsettings + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +App_Data/*.mdf +App_Data/*.ldf + +################# +## Other ide stuff +################# +.idea/* +[#]*[#] + +############# +## Windows detritus +############# + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Mac crap +.DS_Store + + +############# +## Python +############# + +*.py[co] + +# Packages +*.egg +*.egg-info +dist/ +build/ +eggs/ +parts/ +var/ +sdist/ +develop-eggs/ +.installed.cfg + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox + +#Translations +*.mo + +#Mr Developer +.mr.developer.cfg + +############## +## RSS-Bridge +############## +/cache +/whitelist.txt +DEBUG + +###################### +## VisualStudioCode ## +###################### +.vscode/* \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..969c1b3 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,19 @@ +language: php +php: + - '5.6' + - '7.0' + - hhvm + - nightly + +install: + - pear install PHP_CodeSniffer + +script: + - phpenv rehash + - phpcs . --standard=phpcs.xml --warning-severity=0 --extensions=php -p + +matrix: + fast_finish: true + allow_failures: + - php: hhvm + - php: nightly \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4f589ff --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,233 @@ +rss-bridge Changelog +=== + +RSS-Bridge 2017-08-03 +== + +## Important changes +RSS-Bridge now has [contribution guidelines](CONTRIBUTING.md) +[phpcs rules](phpcs.xml) follow the [contribution guidelines](CONTRIBUTING.md) + +## General changes +Added a search bar to make searching for bridges easier +Added user friendly error page for when a bridge fails +Added caching of extraInfos (name, uri) +Added an indicator to warn for bridges using HTTP instead of HTTPS +Various bug fixes and improvements + +## Modified bridges +[AllocineFRBridge] Update Faux Raccord link +[DanbooruBridge] Fix broken URI +[DuckDuckGoBridge] Disable DuckDuckGo redirects so that the links returned are correct. +[FacebookBridge] Add option to hide posts with facebook videos +[FacebookBridge] Add requester languages to HTTP header +[FacebookBridge] Handle summary posts +[FacebookBridge] Replace 'novideo' with 'media_type' +[FilterBridge] Initial implementation of basic title permit and block +[FlickrTagBridge] Fix and improve bridge by using the FlickrExploreBridge approach +[GooglePlusPostBridge] Autofix user names +[GooglePlusPostBridge] Fix bridge implementation +[GooglePlusPostBridge] Fix content loading +[InstagramBridge] Add option to filter for videos and pictures +[LWNprevBridge] full rewrite +[MangareaderBridge] Fix double forward slashes +[NasaApodBridge] Use HTTPS instead of HTTP +[PinterestBridge] Fix checkbox not working +[PinterestBridge] Fix implementation after DOM changes +[RTBFBridge] Update URI +[SexactuBridge] Fix URI and timestamp +[SexactuBridge] Use most modern version of bridge api and cached pages (#504) +[ShanaprojectBridge] Don't throw error if timestamp is missing +[TwitterBridge] Add option to hide retweets +[TwitterBridge] Avoid empty content caused by new login policy +[TwitterBridge] Fix double slashes in URI +[TwitterBridge] Fix missing spaces +[TwitterBridge] Fix title includes anchors in plaintext format +[TwitterBridge] ignore promoted tweets +[TwitterBridge] Optimize returned image sizes +[TwitterBridge] Show quotes and pictures +[WebfailBridge] Properly handle gifs (DOM changed) +[YoutubeBridge] Improve readability of feed contents +[YoutubeBridge] Improve URL handling in video descriptions + +## New bridges +AmazonBridge +DiceBridge +EtsyBridge +FB2Bridge +FilterBridge +FlickrBridge +GithubSearchBridge +GoComicsBridge +KATBridge +KernelBugTrackerBridge +MixCloudBridge +MoinMoinBridge +RainbowSixSiegeBridge +SteamBridge +TheTVDBBridge +Torrent9Bridge +UsbekEtRicaBridge +WikiLeaksBridge +WordPressPluginUpdateBridge + +Alpha 0.2 +=== + +## Important changes +* RSS-Bridge has been [UNLICENSED](UNLICENSE) +* RSS-Bridge is now a community-managed project on [GitHub](https://github.com/rss-bridge/rss-bridge) +* RSS-Bridge now has a [Wiki](https://github.com/rss-bridge/rss-bridge/wiki) +* RSS-Bridge now supports [Travis-CI](https://travis-ci.org) + +## General changes +* Added [CHANGELOG](CHANGELOG.md) (this file) +* Added [PHP Simple HTML DOM Parser](http://simplehtmldom.sourceforge.net) to [vendor](vendor/simplehtmldom/) +* Added cache purging function (cache will be force-purged after 24 hours or as defined by bridge) +* Added new format [MrssFormat](formats/MrssFormat.php) +* Added parameter `author` - for display of the feed author name - to all formats +* Added new abstraction of the BridgeInterface: + - [FeedExpander](https://github.com/RSS-Bridge/rss-bridge/wiki/Bridge-API) +* Added optional support for proxy usage on each individual bridge +* Added support for [custom bridge parameter](https://github.com/RSS-Bridge/rss-bridge/wiki/BridgeAbstract#format-specifications) (text, number, list, checkbox) +* Changed design of the welcome screen +* Changed design of HtmlFormat +* Changed behavior of debug mode: + - Enable debug mode by placing a file called "DEBUG" in the root folder + - Debug mode automatically disables cache file loading +* Changed implementation of bridges - see [Wiki](https://github.com/rss-bridge/rss-bridge/wiki) + - Changed comment-style metadata to constants + - Added support for multiple utilizations per bridge + - Changed the parameter loading algorithm to be loaded by RSS-Bridge core +* Improved checks for PHP version, configuration and extensions +* Many bug fixes + +## Modified Bridges +* FlickrExploreBridge +* GoogleSearchBridge +* TwitterBridge + +## New Bridges +* ABCTabsBridge +* AcrimedBridge +* AllocineFRBridge +* AnimeUltimeBridge +* Arte7Bridge +* AskfmBridge +* BandcampBridge +* BastaBridge +* BlaguesDeMerdeBridge +* BooruprojectBridge +* CADBridge +* CNETBridge +* CastorusBridge +* CollegeDeFranceBridge +* CommonDreamsBridge +* CopieDoubleBridge +* CourrierInternationalBridge +* CpasbienBridge +* CryptomeBridge +* DailymotionBridge +* DanbooruBridge +* DansTonChatBridge +* DauphineLibereBridge +* DemoBridge +* DeveloppezDotComBridge +* DilbertBridge +* DollbooruBridge +* DuckDuckGoBridge +* EZTVBridge +* EliteDangerousGalnetBridge +* ElsevierBridge +* EstCeQuonMetEnProdBridge +* FacebookBridge +* FierPandaBridge +* FlickrTagBridge +* FootitoBridge +* FourchanBridge +* FuturaSciencesBridge +* GBAtempBridge +* GelbooruBridge +* GiphyBridge +* GithubIssueBridge +* GizmodoBridge +* GooglePlusPostBridge +* HDWallpapersBridge +* HentaiHavenBridge +* IdenticaBridge +* InstagramBridge +* IsoHuntBridge +* JapanExpoBridge +* KonachanBridge +* KoreusBridge +* KununuBridge +* LWNprevBridge +* LeBonCoinBridge +* LegifranceJOBridge +* LeMondeInformatiqueBridge +* LesJoiesDuCodeBridge +* LichessBridge +* LinkedInCompanyBridge +* LolibooruBridge +* MangareaderBridge +* MilbooruBridge +* MoebooruBridge +* MondeDiploBridge +* MsnMondeBridge +* MspabooruBridge +* NasaApodBridge +* NeuviemeArtBridge +* NextInpactBridge +* NextgovBridge +* NiceMatinBridge +* NovelUpdatesBridge +* OpenClassroomsBridge +* ParuVenduImmoBridge +* PickyWallpapersBridge +* PinterestBridge +* PlanetLibreBridge +* RTBFBridge +* ReadComicsBridge +* Releases3DSBridge +* ReporterreBridge +* Rue89Bridge +* Rule34Bridge +* Rule34pahealBridge +* SafebooruBridge +* SakugabooruBridge +* ScmbBridge +* ScoopItBridge +* SensCritiqueBridge +* SexactuBridge +* ShanaprojectBridge +* Shimmie2Bridge +* SoundcloudBridge +* StripeAPIChangeLogBridge +* SuperbWallpapersBridge +* T411Bridge +* TagBoardBridge +* TbibBridge +* TheCodingLoveBridge +* TheHackerNewsBridge +* ThePirateBayBridge +* UnsplashBridge +* ViadeoCompanyBridge +* VineBridge +* VkBridge +* WallpaperStopBridge +* WebfailBridge +* WeLiveSecurityBridge +* WhydBridge +* WikipediaBridge +* WordPressBridge +* WorldOfTanksBridge +* XbooruBridge +* YandereBridge +* YoutubeBridge +* ZDNetBridge + +Alpha 0.1 +=== +* First tagged version. +* Includes refactoring. +* Unstable. \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..e03f926 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,47 @@ +### Pull request policy +Fix one issue per pull request. +Squash commits before opening a pull request. +Respect the coding style policy. +Name your PR like the following : + +* When correcting a single bridge, use `[BridgeName] Feature`. +* When fixing a problem in a specific file, use `[FileName] Feature`. +* When fixing a general problem, use `category : feature`. + +Note that all pull-requests should pass the unit tests before they can be merged. + +### Coding style + +Use `camelCase` for variables and methods. +Use `UPPERCASE` for constants. +Use `PascalCase` for class names. When creating a bridge, your class and PHP file should be named `MyImplementationBridge`. +Use tabs for indentation. +Add an empty line at the end of your file. + +Use `''` to encapsulate strings, including in arrays. +Prefer lines shorter than 80 chars, no line longer than 120 chars. +PHP constants should be in lower case (`true, false, null`...) + + +* Add spaces between the logical operator and your expressions (not needed for the `!` operator). +* Use `||` and `&&` instead of `or` and `and`. +* Add space between your condition and the opening bracket/closing bracket. +* Don't put a space between `if` and your bracket. +* Use `elseif` instead of `else if`. +* Add new lines in your conditions if they are containing more than one line. +* Example : + +```PHP +if($a == true && $b) { + print($a); +} else if(!$b) { + + $a = !$a; + $b = $b >> $a; + print($b); + +} else { + print($b); +} +``` + diff --git a/README.md b/README.md new file mode 100644 index 0000000..e36f22b --- /dev/null +++ b/README.md @@ -0,0 +1,142 @@ +rss-bridge +=== +[![LICENSE](https://img.shields.io/badge/license-UNLICENSE-blue.svg)](UNLICENSE) [![Build Status](https://travis-ci.org/RSS-Bridge/rss-bridge.svg?branch=master)](https://travis-ci.org/RSS-Bridge/rss-bridge) + +rss-bridge is a PHP project capable of generating ATOM feeds for websites which don't have one. + +Supported sites/pages (main) +=== + + * `FlickrExplore` : [Latest interesting images](http://www.flickr.com/explore) from Flickr + * `GoogleSearch` : Most recent results from Google Search + * `GooglePlus` : Most recent posts of user timeline + * `Twitter` : Return keyword/hashtag search or user timeline + * `Identi.ca` : Identica user timeline (Should be compatible with other Pump.io instances) + * `YouTube` : YouTube user channel, playlist or search + * `Cryptome` : Returns the most recent documents from [Cryptome.org](http://cryptome.org/) + * `DansTonChat`: Most recent quotes from [danstonchat.com](http://danstonchat.com/) + * `DuckDuckGo`: Most recent results from [DuckDuckGo.com](https://duckduckgo.com/) + * `Instagram`: Most recent photos from an Instagram user + * `OpenClassrooms`: Lastest tutorials from [fr.openclassrooms.com](http://fr.openclassrooms.com/) + * `Pinterest`: Most recent photos from user or search + * `ScmbBridge`: Newest stories from [secouchermoinsbete.fr](http://secouchermoinsbete.fr/) + * `Wikipedia`: highlighted articles from [Wikipedia](https://wikipedia.org/) in English, German, French or Esperanto + * `Bandcamp` : Returns last release from [bandcamp](https://bandcamp.com/) for a tag + * `ThePirateBay` : Returns the newest indexed torrents from [The Pirate Bay](https://thepiratebay.se/) with keywords + * `Facebook` : Returns the latest posts on a page or profile on [Facebook](https://facebook.com/) + +Plus [many other bridges](bridges/) to enable, thanks to the community + +Output format +=== +Output format can take several forms: + + * `Atom` : ATOM Feed, for use in RSS/Feed readers + * `Mrss` : MRSS Feed, for use in RSS/Feed readers + * `Json` : Json, for consumption by other applications. + * `Html` : Simple html page. + * `Plaintext` : raw text (php object, as returned by print_r) + +Screenshot +=== + +Welcome screen: + +![Screenshot](https://github.com/RSS-Bridge/rss-bridge/wiki/images/screenshot_rss-bridge_welcome.png) + +RSS-Bridge hashtag (#rss-bridge) search on Twitter, in ATOM format (as displayed by Firefox): + +![Screenshot](https://github.com/RSS-Bridge/rss-bridge/wiki/images/screenshot_twitterbridge_atom.png) + +Requirements +=== + + * PHP 5.6, e.g. `AddHandler application/x-httpd-php56 .php` in `.htaccess` + * `openssl` extension enabled in PHP config (`php.ini`) + * `allow_url_fopen=1` in `php.ini` + +Enabling/Disabling bridges +=== + +By default, the script creates `whitelist.txt` and adds the main bridges (see above). `whitelist.txt` is ignored by git, you can edit it: + * to enable extra bridges (one bridge per line) + * to disable main bridges (remove the line) + * to enable all bridges (just one wildcard `*` as file content) + +New bridges are disabled by default, so make sure to check regularly what's new and whitelist what you want! + +Deploy +=== +[![Deploy on Scalingo](https://cdn.scalingo.com/deploy/button.svg)](https://my.scalingo.com/deploy?source=https://github.com/sebsauvage/rss-bridge) + +Authors +=== +We are RSS Bridge Community, a group of developers continuing the project initiated by sebsauvage, webmaster of [sebsauvage.net](http://sebsauvage.net), author of [Shaarli](http://sebsauvage.net/wiki/doku.php?id=php:shaarli) and [ZeroBin](http://sebsauvage.net/wiki/doku.php?id=php:zerobin). + +Patch/contributors : + + * Yves ASTIER ([Draeli](https://github.com/Draeli)) : PHP optimizations, fixes, dynamic brigde/format list with all stuff behind and extend cache system. Mail : contact /at\ yves-astier.com + * [Mitsukarenai](https://github.com/Mitsukarenai) : Initial inspiration, collaborator + * [ArthurHoaro](https://github.com/ArthurHoaro) + * [BoboTiG](https://github.com/BoboTiG) + * [Astalaseven](https://github.com/Astalaseven) + * [qwertygc](https://github.com/qwertygc) + * [Djuuu](https://github.com/Djuuu) + * [Anadrark](https://github.com/Anadrark]) + * [Grummfy](https://github.com/Grummfy) + * [Polopollo](https://github.com/Polopollo) + * [16mhz](https://github.com/16mhz) + * [kranack](https://github.com/kranack) + * [logmanoriginal](https://github.com/logmanoriginal) + * [polo2ro](https://github.com/polo2ro) + * [Riduidel](https://github.com/Riduidel) + * [superbaillot.net](http://superbaillot.net/) + * [vinzv](https://github.com/vinzv) + * [teromene](https://github.com/teromene) + * [nel50n](https://github.com/nel50n) + * [nyutag](https://github.com/nyutag) + * [ORelio](https://github.com/ORelio) + * [Pitchoule](https://github.com/Pitchoule) + * [pit-fgfjiudghdf](https://github.com/pit-fgfjiudghdf) + * [aledeg](https://github.com/aledeg) + * [alexAubin](https://github.com/alexAubin) + * [cnlpete](https://github.com/cnlpete) + * [corenting](https://github.com/corenting) + * [Daiyousei](https://github.com/Daiyousei) + * [erwang](https://github.com/erwang) + * [gsurrel](https://github.com/gsurrel) + * [kraoc](https://github.com/kraoc) + * [lagaisse](https://github.com/lagaisse) + * [az5he6ch](https://github.com/az5he6ch) + * [niawag](https://github.com/niawag) + * [JeremyRand](https://github.com/JeremyRand) + * [mro](https://github.com/mro) + +Licenses +=== +Code is [Public Domain](UNLICENSE). + +Including `PHP Simple HTML DOM Parser` under the [MIT License](http://opensource.org/licenses/MIT) + + +Technical notes +=== + * There is a cache so that source services won't ban you even if you hammer the rss-bridge with requests. Each bridge can have a different duration for the cache. The `cache` subdirectory will be automatically created and cached objects older than 24 hours get purged. + * To implement a new Bridge, [follow the specifications](https://github.com/RSS-Bridge/rss-bridge/wiki/Bridge-API) and take a look at existing Bridges for examples. + * To enable debug mode (disabling cache and enabling error reporting), create an empty file named `DEBUG` in the root directory (next to `index.php`). + * For more information refer to the [Wiki](https://github.com/RSS-Bridge/rss-bridge/wiki) + +Rant +=== + +*Dear so-called "social" websites.* + +Your catchword is "share", but you don't want us to share. You want to keep us within your walled gardens. That's why you've been removing RSS links from webpages, hiding them deep on your website, or removed feeds entirely, replacing it with crippled or demented proprietary API. **FUCK YOU.** + +You're not social when you hamper sharing by removing feeds. You're happy to have customers creating content for your ecosystem, but you don't want this content out - a content you do not even own. Google Takeout is just a gimmick. We want our data to flow, we want RSS or ATOM feeds. + +We want to share with friends, using open protocols: RSS, ATOM, XMPP, whatever. Because no one wants to have *your* service with *your* applications using *your* API force-feeding them. Friends must be free to choose whatever software and service they want. + +We are rebuilding bridges you have wilfully destroyed. + +Get your shit together: Put RSS/ATOM back in. diff --git a/UNLICENSE b/UNLICENSE new file mode 100644 index 0000000..a84c395 --- /dev/null +++ b/UNLICENSE @@ -0,0 +1,25 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + diff --git a/bridges/ABCTabsBridge.php b/bridges/ABCTabsBridge.php new file mode 100644 index 0000000..2e451e2 --- /dev/null +++ b/bridges/ABCTabsBridge.php @@ -0,0 +1,42 @@ +find('table#myTable', 0)->children(1); + + foreach ($table->find('tr') as $tab) { + $item = array(); + $item['author'] = $tab->find('td', 1)->plaintext + . ' - ' + . $tab->find('td', 2)->plaintext; + + $item['title'] = $tab->find('td', 1)->plaintext + . ' - ' + . $tab->find('td', 2)->plaintext; + + $item['content'] = 'Le ' + . $tab->find('td', 0)->plaintext + . '
Par: ' + . $tab->find('td', 5)->plaintext + . '
Type: ' + . $tab->find('td', 3)->plaintext; + + $item['id'] = static::URI + . $tab->find('td', 2)->find('a', 0)->getAttribute('href'); + + $item['uri'] = static::URI + . $tab->find('td', 2)->find('a', 0)->getAttribute('href'); + + $this->items[] = $item; + } + } +} diff --git a/bridges/AcrimedBridge.php b/bridges/AcrimedBridge.php new file mode 100644 index 0000000..8b40d1d --- /dev/null +++ b/bridges/AcrimedBridge.php @@ -0,0 +1,25 @@ +collectExpandableDatas(static::URI . 'spip.php?page=backend'); + } + + protected function parseItem($newsItem){ + $item = parent::parseItem($newsItem); + + $articlePage = getSimpleHTMLDOM($newsItem->link); + $article = sanitize($articlePage->find('article.article1', 0)->innertext); + $article = defaultLinkTo($article, static::URI); + $item['content'] = $article; + + return $item; + } + +} diff --git a/bridges/AllocineFRBridge.php b/bridges/AllocineFRBridge.php new file mode 100644 index 0000000..604199b --- /dev/null +++ b/bridges/AllocineFRBridge.php @@ -0,0 +1,87 @@ + array( + 'name' => 'category', + 'type' => 'list', + 'required' => true, + 'exampleValue' => 'Faux Raccord', + 'title' => 'Select your category', + 'values' => array( + 'Faux Raccord' => 'faux-raccord', + 'Top 5' => 'top-5', + 'Tueurs en Séries' => 'tueurs-en-serie' + ) + ) + )); + + public function getURI(){ + if(!is_null($this->getInput('category'))) { + + switch($this->getInput('category')) { + case 'faux-raccord': + $uri = static::URI . 'video/programme-12284/saison-29841/'; + break; + case 'top-5': + $uri = static::URI . 'video/programme-12299/saison-29561/'; + break; + case 'tueurs-en-serie': + $uri = static::URI . 'video/programme-12286/saison-22938/'; + break; + } + + return $uri; + } + + return parent::getURI(); + } + + public function getName(){ + if(!is_null($this->getInput('category'))) { + return self::NAME . ' : ' + .array_search( + $this->getInput('category'), + self::PARAMETERS[$this->queriedContext]['category']['values'] + ); + } + + return parent::getName(); + } + + public function collectData(){ + + $html = getSimpleHTMLDOM($this->getURI()) + or returnServerError('Could not request ' . $this->getURI() . ' !'); + + $category = array_search( + $this->getInput('category'), + self::PARAMETERS[$this->queriedContext]['category']['values'] + ); + + foreach($html->find('figure.media-meta-fig') as $element) { + $item = array(); + + $title = $element->find('div.titlebar h3.title a', 0); + $content = trim($element->innertext); + $figCaption = strpos($content, $category); + + if($figCaption !== false) { + $content = str_replace('src="/', 'src="' . static::URI, $content); + $content = str_replace('href="/', 'href="' . static::URI, $content); + $content = str_replace('src=\'/', 'src=\'' . static::URI, $content); + $content = str_replace('href=\'/', 'href=\'' . static::URI, $content); + $item['content'] = $content; + $item['title'] = trim($title->innertext); + $item['uri'] = static::URI . $title->href; + $this->items[] = $item; + } + } + } + +} diff --git a/bridges/AmazonBridge.php b/bridges/AmazonBridge.php new file mode 100644 index 0000000..cbc6119 --- /dev/null +++ b/bridges/AmazonBridge.php @@ -0,0 +1,94 @@ + array( + 'name' => 'Keyword', + 'required' => true, + ), + 'sort' => array( + 'name' => 'Sort by', + 'type' => 'list', + 'required' => false, + 'values' => array( + 'Relevance' => 'relevanceblender', + 'Price: Low to High' => 'price-asc-rank', + 'Price: High to Low' => 'price-desc-rank', + 'Average Customer Review' => 'review-rank', + 'Newest Arrivals' => 'date-desc-rank', + ), + 'defaultValue' => 'relevanceblender', + ), + 'tld' => array( + 'name' => 'Country', + 'type' => 'list', + 'required' => true, + 'values' => array( + 'Australia' => 'com.au', + 'Brazil' => 'com.br', + 'Canada' => 'ca', + 'China' => 'cn', + 'France' => 'fr', + 'Germany' => 'de', + 'India' => 'in', + 'Italy' => 'it', + 'Japan' => 'co.jp', + 'Mexico' => 'com.mx', + 'Netherlands' => 'nl', + 'Spain' => 'es', + 'United Kingdom' => 'co.uk', + 'United States' => 'com', + ), + 'defaultValue' => 'com', + ), + )); + + public function getName(){ + if(!is_null($this->getInput('tld')) && !is_null($this->getInput('q'))) { + return 'Amazon.'.$this->getInput('tld').': '.$this->getInput('q'); + } + + return parent::getName(); + } + + public function collectData() { + + $uri = 'https://www.amazon.'.$this->getInput('tld').'/'; + $uri .= 's/?field-keywords='.urlencode($this->getInput('q')).'&sort='.$this->getInput('sort'); + + $html = getSimpleHTMLDOM($uri) + or returnServerError('Could not request Amazon.'); + + foreach($html->find('li.s-result-item') as $element) { + + $item = array(); + + // Title + $title = $element->find('h2', 0); + + $item['title'] = html_entity_decode($title->innertext, ENT_QUOTES); + + // Url + $uri = $title->parent()->getAttribute('href'); + $uri = substr($uri, 0, strrpos($uri, '/')); + + $item['uri'] = substr($uri, 0, strrpos($uri, '/')); + + // Content + $image = $element->find('img', 0); + $price = $element->find('span.s-price', 0); + $price = ($price) ? $price->innertext : ''; + + $item['content'] = '
'.$price; + + $this->items[] = $item; + } + } +} diff --git a/bridges/AnimeUltimeBridge.php b/bridges/AnimeUltimeBridge.php new file mode 100644 index 0000000..6c5427e --- /dev/null +++ b/bridges/AnimeUltimeBridge.php @@ -0,0 +1,135 @@ + array( + 'name' => 'Type', + 'type' => 'list', + 'values' => array( + 'Everything' => '', + 'Anime' => 'A', + 'Drama' => 'D', + 'Tokusatsu' => 'T' + ) + ) + )); + + private $filter = 'Releases'; + + public function collectData(){ + + //Add type filter if provided + $typeFilter = array_search( + $this->getInput('type'), + self::PARAMETERS[$this->queriedContext]['type']['values'] + ); + + //Build date and filters for making requests + $thismonth = date('mY') . $typeFilter; + $lastmonth = date('mY', mktime(0, 0, 0, date('n') - 1, 1, date('Y'))) . $typeFilter; + + //Process each HTML page until having 10 releases + $processedOK = 0; + foreach (array($thismonth, $lastmonth) as $requestFilter) { + + //Retrive page contents + $url = self::URI . 'history-0-1/' . $requestFilter; + $html = getSimpleHTMLDOM($url) + or returnServerError('Could not request Anime-Ultime: ' . $url); + + //Relases are sorted by day : process each day individually + foreach($html->find('div.history', 0)->find('h3') as $daySection) { + + //Retrieve day and build date information + $dateString = $daySection->plaintext; + $day = intval(substr($dateString, strpos($dateString, ' ') + 1, 2)); + $item_date = strtotime(str_pad($day, 2, '0', STR_PAD_LEFT) + . '-' + . substr($requestFilter, 0, 2) + . '-' + . substr($requestFilter, 2, 4)); + + //

day


<-- useful data in table rows + $release = $daySection->next_sibling()->next_sibling()->first_child(); + + //Process each release of that day, ignoring first table row: contains table headers + while(!is_null($release = $release->next_sibling())) { + if(count($release->find('td')) > 0) { + + //Retrieve metadata from table columns + $item_link_element = $release->find('td', 0)->find('a', 0); + $item_uri = self::URI . $item_link_element->href; + $item_name = html_entity_decode($item_link_element->plaintext); + $item_episode = html_entity_decode( + str_pad( + $release->find('td', 1)->plaintext, + 2, + '0', + STR_PAD_LEFT + ) + ); + + $item_fansub = $release->find('td', 2)->plaintext; + $item_type = $release->find('td', 4)->plaintext; + + if(!empty($item_uri)) { + + // Retrieve description from description page and + // convert relative image src info absolute image src + $html_item = getContents($item_uri) + or returnServerError('Could not request Anime-Ultime: ' . $item_uri); + $item_description = substr( + $html_item, + strpos($html_item, 'class="principal_contain" align="center">') + 41 + ); + $item_description = substr($item_description, + 0, + strpos($item_description, '
') + ); + $item_description = str_replace( + 'src="images', 'src="' . self::URI . 'images', + $item_description + ); + $item_description = str_replace("\r", '', $item_description); + $item_description = str_replace("\n", '', $item_description); + $item_description = utf8_encode($item_description); + + //Build and add final item + $item = array(); + $item['uri'] = $item_uri; + $item['title'] = $item_name . ' ' . $item_type . ' ' . $item_episode; + $item['author'] = $item_fansub; + $item['timestamp'] = $item_date; + $item['content'] = $item_description; + $this->items[] = $item; + $processedOK++; + + //Stop processing once limit is reached + if ($processedOK >= 10) + return; + } + } + } + } + } + } + + public function getName() { + if(!is_null($this->getInput('type'))) { + $typeFilter = array_search( + $this->getInput('type'), + self::PARAMETERS[$this->queriedContext]['type']['values'] + ); + + return 'Latest ' . $typeFilter . ' - Anime-Ultime Bridge'; + } + + return parent::getName(); + } + +} diff --git a/bridges/Arte7Bridge.php b/bridges/Arte7Bridge.php new file mode 100644 index 0000000..3d7ae9d --- /dev/null +++ b/bridges/Arte7Bridge.php @@ -0,0 +1,102 @@ + array( + 'catfr' => array( + 'type' => 'list', + 'name' => 'Catégorie', + 'values' => array( + 'Toutes les vidéos (français)' => 'toutes-les-videos', + 'Actu & société' => 'actu-société', + 'Séries & fiction' => 'séries-fiction', + 'Cinéma' => 'cinéma', + 'Arts & spectacles classiques' => 'arts-spectacles-classiques', + 'Culture pop' => 'culture-pop', + 'Découverte' => 'découverte', + 'Histoire' => 'histoire', + 'Junior' => 'junior' + ) + ) + ), + 'Catégorie (Allemand)' => array( + 'catde' => array( + 'type' => 'list', + 'name' => 'Catégorie', + 'values' => array( + 'Alle Videos (deutsch)' => 'alle-videos', + 'Aktuelles & Gesellschaft' => 'aktuelles-gesellschaft', + 'Fernsehfilme & Serien' => 'fernsehfilme-serien', + 'Kino' => 'kino', + 'Kunst & Kultur' => 'kunst-kultur', + 'Popkultur & Alternativ' => 'popkultur-alternativ', + 'Entdeckung' => 'entdeckung', + 'Geschichte' => 'geschichte', + 'Junior' => 'junior' + ) + ) + ) + ); + + public function collectData(){ + switch($this->queriedContext) { + case 'Catégorie (Français)': + $category = $this->getInput('catfr'); + $lang = 'fr'; + break; + case 'Catégorie (Allemand)': + $category = $this->getInput('catde'); + $lang = 'de'; + break; + } + + $url = self::URI . 'guide/' . $lang . '/plus7/' . $category; + $input = getContents($url) or die('Could not request ARTE.'); + + if(strpos($input, 'categoryVideoSet') !== false) { + $input = explode('categoryVideoSet="', $input); + $input = explode('}}', $input[1]); + $input = $input[0] . '}}'; + } else { + $input = explode('videoSet="', $input); + $input = explode('}]}', $input[1]); + $input = $input[0] . '}]}'; + } + + $input_json = json_decode(html_entity_decode($input, ENT_QUOTES), true); + + foreach($input_json['videos'] as $element) { + $item = array(); + $item['uri'] = str_replace("autoplay=1", "", $element['url']); + $item['id'] = $element['id']; + + $hack_broadcast_time = $element['rights_end']; + $hack_broadcast_time = strtok($hack_broadcast_time, 'T'); + $hack_broadcast_time = strtok('T'); + + $item['timestamp'] = strtotime($element['scheduled_on'] . 'T' . $hack_broadcast_time); + $item['title'] = $element['title']; + + if(!empty($element['subtitle'])) + $item['title'] = $element['title'] . ' | ' . $element['subtitle']; + + $item['duration'] = round((int)$element['duration'] / 60); + $item['content'] = $element['teaser'] + . '

' + . $item['duration'] + . 'min
'; + + $this->items[] = $item; + } + } + +} diff --git a/bridges/AskfmBridge.php b/bridges/AskfmBridge.php new file mode 100644 index 0000000..e227461 --- /dev/null +++ b/bridges/AskfmBridge.php @@ -0,0 +1,74 @@ + array( + 'u' => array( + 'name' => 'Username', + 'required' => true + ) + ) + ); + + public function collectData(){ + $html = getSimpleHTMLDOM($this->getURI()) + or returnServerError('Requested username can\'t be found.'); + + foreach($html->find('div.streamItem-answer') as $element) { + $item = array(); + $item['uri'] = self::URI . $element->find('a.streamItemsAge', 0)->href; + $question = trim($element->find('h1.streamItemContent-question', 0)->innertext); + + $item['title'] = trim( + htmlspecialchars_decode($element->find('h1.streamItemContent-question', 0)->plaintext, + ENT_QUOTES + ) + ); + + $answer = trim($element->find('p.streamItemContent-answer', 0)->innertext); + + // Doesn't work, DOM parser doesn't seem to like data-hint, dunno why + #$item['update'] = $element->find('a.streamitemsage',0)->data-hint; + + // This probably should be cleaned up, especially for YouTube embeds + $visual = $element->find('div.streamItemContent-visual', 0)->innertext; + //Fix tracking links, also doesn't work + foreach($element->find('a') as $link) { + if(strpos($link->href, 'l.ask.fm') !== false) { + + // Too slow + #$link->href = str_replace('#_=_', '', get_headers($link->href, 1)['Location']); + + $link->href = $link->plaintext; + } + } + + $content = '

' . $question . '

' . $answer . '

' . $visual . '

'; + // Fix relative links without breaking // scheme used by YouTube stuff + $content = preg_replace('#href="\/(?!\/)#', 'href="' . self::URI, $content); + $item['content'] = $content; + $this->items[] = $item; + } + } + + public function getName(){ + if(!is_null($this->getInput('u'))) { + return self::NAME . ' : ' . $this->getInput('u'); + } + + return parent::getName(); + } + + public function getURI(){ + if(!is_null($this->getInput('u'))) { + return self::URI . urlencode($this->getInput('u')) . '/answers/more?page=0'; + } + + return parent::getURI(); + } +} diff --git a/bridges/BandcampBridge.php b/bridges/BandcampBridge.php new file mode 100644 index 0000000..0527da0 --- /dev/null +++ b/bridges/BandcampBridge.php @@ -0,0 +1,63 @@ + array( + 'name' => 'tag', + 'type' => 'text', + 'required' => true + ) + )); + + public function collectData(){ + $html = getSimpleHTMLDOM($this->getURI()) + or returnServerError('No results for this query.'); + + foreach($html->find('li.item') as $release) { + $script = $release->find('div.art', 0)->getAttribute('onclick'); + $uri = ltrim($script, "return 'url("); + $uri = rtrim($uri, "')"); + + $item = array(); + $item['author'] = $release->find('div.itemsubtext', 0)->plaintext + . ' - ' + . $release->find('div.itemtext', 0)->plaintext; + + $item['title'] = $release->find('div.itemsubtext', 0)->plaintext + . ' - ' + . $release->find('div.itemtext', 0)->plaintext; + + $item['content'] = '
' + . $release->find('div.itemsubtext', 0)->plaintext + . ' - ' + . $release->find('div.itemtext', 0)->plaintext; + + $item['id'] = $release->find('a', 0)->getAttribute('href'); + $item['uri'] = $release->find('a', 0)->getAttribute('href'); + $this->items[] = $item; + } + } + + public function getURI(){ + if(!is_null($this->getInput('tag'))) { + return self::URI . 'tag/' . urlencode($this->getInput('tag')) . '?sort_field=date'; + } + + return parent::getURI(); + } + + public function getName(){ + if(!is_null($this->getInput('tag'))) { + return $this->getInput('tag') . ' - Bandcamp Tag'; + } + + return parent::getName(); + } +} diff --git a/bridges/BastaBridge.php b/bridges/BastaBridge.php new file mode 100644 index 0000000..17d3da7 --- /dev/null +++ b/bridges/BastaBridge.php @@ -0,0 +1,34 @@ +find('item') as $element) { + if($limit < 10) { + $item = array(); + $item['title'] = $element->find('title', 0)->innertext; + $item['uri'] = $element->find('guid', 0)->plaintext; + $item['timestamp'] = strtotime($element->find('dc:date', 0)->plaintext); + $item['content'] = replaceImageUrl(getSimpleHTMLDOM($item['uri'])->find('div.texte', 0)->innertext); + $this->items[] = $item; + $limit++; + } + } + } +} diff --git a/bridges/BlaguesDeMerdeBridge.php b/bridges/BlaguesDeMerdeBridge.php new file mode 100644 index 0000000..25c018a --- /dev/null +++ b/bridges/BlaguesDeMerdeBridge.php @@ -0,0 +1,31 @@ +find('article.joke_contener') as $element) { + $item = array(); + $temp = $element->find('a'); + + if(isset($temp[2])) { + $item['content'] = trim($element->find('div.joke_text_contener', 0)->innertext); + $uri = $temp[2]->href; + $item['uri'] = $uri; + $item['title'] = substr($uri, (strrpos($uri, "/") + 1)); + $date = $element->find('li.bdm_date', 0)->innertext; + $time = mktime(0, 0, 0, substr($date, 3, 2), substr($date, 0, 2), substr($date, 6, 4)); + $item['timestamp'] = $time; + $item['author'] = $element->find('li.bdm_pseudo', 0)->innertext; + $this->items[] = $item; + } + } + } +} diff --git a/bridges/BooruprojectBridge.php b/bridges/BooruprojectBridge.php new file mode 100644 index 0000000..6815d37 --- /dev/null +++ b/bridges/BooruprojectBridge.php @@ -0,0 +1,45 @@ + array( + 'p' => array( + 'name' => 'page', + 'type' => 'number' + ), + 't' => array( + 'name' => 'tags' + ) + ), + 'Booru subdomain (subdomain.booru.org)' => array( + 'i' => array( + 'name' => 'Subdomain', + 'required' => true + ) + ) + ); + + const PIDBYPAGE = 20; + + public function getURI(){ + if(!is_null($this->getInput('i'))) { + return 'http://' . $this->getInput('i') . '.booru.org/'; + } + + return parent::getURI(); + } + + public function getName(){ + if(!is_null($this->getInput('i'))) { + return static::NAME . ' ' . $this->getInput('i'); + } + + return parent::getName(); + } +} diff --git a/bridges/CADBridge.php b/bridges/CADBridge.php new file mode 100644 index 0000000..09e3e65 --- /dev/null +++ b/bridges/CADBridge.php @@ -0,0 +1,45 @@ +collectExpandableDatas('http://cdn2.cad-comic.com/rss.xml', 10); + } + + protected function parseItem($newsItem){ + $item = parent::parseItem($newsItem); + $item['content'] = $this->extractCADContent($item['uri']); + return $item; + } + + private function extractCADContent($url) { + $html3 = getSimpleHTMLDOMCached($url); + + // The request might fail due to missing https support or wrong URL + if($html3 == false) + return 'Daily comic not released yet'; + + $htmlpart = explode("/", $url); + + switch ($htmlpart[3]) { + case 'cad': + preg_match_all("/http:\/\/cdn2\.cad-comic\.com\/comics\/cad-\S*png/", $html3, $url2); + break; + case 'sillies': + preg_match_all("/http:\/\/cdn2\.cad-comic\.com\/comics\/sillies-\S*gif/", $html3, $url2); + break; + default: + return 'Daily comic not released yet'; + } + $img = implode($url2[0]); + $html3->clear(); + unset($html3); + if ($img == '') + return 'Daily comic not released yet'; + return ''; + } +} diff --git a/bridges/CNETBridge.php b/bridges/CNETBridge.php new file mode 100644 index 0000000..eefb705 --- /dev/null +++ b/bridges/CNETBridge.php @@ -0,0 +1,93 @@ + You may specify a +topic found in some section URLs, else all topics are selected.'; + + const PARAMETERS = array( array( + 'topic' => array( + 'name' => 'Topic name' + ) + )); + + public function collectData(){ + + function extractFromDelimiters($string, $start, $end){ + if(strpos($string, $start) !== false) { + $section_retrieved = substr($string, strpos($string, $start) + strlen($start)); + $section_retrieved = substr($section_retrieved, 0, strpos($section_retrieved, $end)); + return $section_retrieved; + } + + return false; + } + + function stripWithDelimiters($string, $start, $end){ + while(strpos($string, $start) !== false) { + $section_to_remove = substr($string, strpos($string, $start)); + $section_to_remove = substr($section_to_remove, 0, strpos($section_to_remove, $end) + strlen($end)); + $string = str_replace($section_to_remove, '', $string); + } + + return $string; + } + + function cleanArticle($article_html){ + $article_html = '

' . substr($article_html, strpos($article_html, '

') + 3); + $article_html = stripWithDelimiters($article_html, '', ''); + $article_html = stripWithDelimiters($article_html, ''); + $article_html = stripWithDelimiters($article_html, '

'); //strip thead + $html = stristr($html, ''); //remove tbody + $html = str_get_html(stristr($html, '', true)); //remove last tbody and get back as an array + foreach($html->find('tr') as $element) { + $item = array(); + $item['uri'] = $element->find('td', 2)->find('a', 0)->href; + $item['title'] = $element->find('td', 2)->find('a', 0)->plaintext; + $item['team'] = $element->find('td', 1)->innertext; + $item['timestamp'] = strtotime($element->find('td', 0)->plaintext); + $item['content'] = '' + . $this->seriesTitle + . ' - ' + . $item['title'] + . ' by ' + . $item['team'] + . '
' + . $fullhtml->find('div.seriesimg', 0)->innertext + . ''; + + $this->items[] = $item; + } + } + + public function getName(){ + if(!empty($this->seriesTitle)) { + return $this->seriesTitle . ' - ' . static::NAME; + } + + return parent::getName(); + } +} diff --git a/bridges/OpenClassroomsBridge.php b/bridges/OpenClassroomsBridge.php new file mode 100644 index 0000000..5f0daca --- /dev/null +++ b/bridges/OpenClassroomsBridge.php @@ -0,0 +1,49 @@ + array( + 'name' => 'Catégorie', + 'type' => 'list', + 'required' => true, + 'values' => array( + 'Arts & Culture' => 'arts', + 'Code' => 'code', + 'Design' => 'design', + 'Entreprise' => 'business', + 'Numérique' => 'digital', + 'Sciences' => 'sciences', + 'Sciences Humaines' => 'humainities', + 'Systèmes d\'information' => 'it', + 'Autres' => 'others' + ) + ) + )); + + public function getURI(){ + if(!is_null($this->getInput('u'))) { + return self::URI . '/courses?categories=' . $this->getInput('u') . '&title=&sort=updatedAt+desc'; + } + + return parent::getURI(); + } + + public function collectData(){ + $html = getSimpleHTMLDOM($this->getURI()) + or returnServerError('Could not request OpenClassrooms.'); + + foreach($html->find('.courseListItem') as $element) { + $item = array(); + $item['uri'] = self::URI . $element->find('a', 0)->href; + $item['title'] = $element->find('h3', 0)->plaintext; + $item['content'] = $element->find('slidingItem__descriptionContent', 0)->plaintext; + $this->items[] = $item; + } + } +} diff --git a/bridges/ParuVenduImmoBridge.php b/bridges/ParuVenduImmoBridge.php new file mode 100644 index 0000000..a2e2b33 --- /dev/null +++ b/bridges/ParuVenduImmoBridge.php @@ -0,0 +1,102 @@ + array( + 'name' => 'Minimal surface m²', + 'type' => 'number' + ), + 'maxprice' => array( + 'name' => 'Max price', + 'type' => 'number' + ), + 'pa' => array( + 'name' => 'Country code', + 'exampleValue' => 'FR' + ), + 'lo' => array( + 'name' => 'department numbers or postal codes, comma-separated' + ) + )); + + public function collectData(){ + $html = getSimpleHTMLDOM($this->getURI()) + or returnServerError('Could not request paruvendu.'); + + foreach($html->find('div.annonce a') as $element) { + + if(!$element->title) { + continue; + } + + $img = ''; + foreach($element->find('span.img img') as $img) { + if($img->original) { + $img = ''; + } + } + + $desc = $element->find('span.desc')[0]->innertext; + $desc = str_replace("voir l'annonce", '', $desc); + + $price = $element->find('span.price')[0]->innertext; + + list($href) = explode('#', $element->href); + + $item = array(); + $item['uri'] = self::URI . $href; + $item['title'] = $element->title; + $item['content'] = $img . $desc . $price; + $this->items[] = $item; + } + } + + public function getURI(){ + $appartment = '&tbApp=1&tbDup=1&tbChb=1&tbLof=1&tbAtl=1&tbPla=1'; + $maison = '&tbMai=1&tbVil=1&tbCha=1&tbPro=1&tbHot=1&tbMou=1&tbFer=1'; + $link = self::URI + . '/immobilier/annonceimmofo/liste/listeAnnonces?tt=1' + . $appartment + . $maison; + + if($this->getInput('minarea')) { + $link .= '&sur0=' . urlencode($this->getInput('minarea')); + } + + if($this->getInput('maxprice')) { + $link .= '&px1=' . urlencode($this->getInput('maxprice')); + } + + if($this->getInput('pa')) { + $link .= '&pa=' . urlencode($this->getInput('pa')); + } + + if($this->getInput('lo')) { + $link .= '&lo=' . urlencode($this->getInput('lo')); + } + return $link; + } + + public function getName(){ + if(!is_null($this->getInput('minarea'))) { + $request = ''; + $minarea = $this->getInput('minarea'); + if(!empty($minarea)) { + $request .= ' ' . $minarea . ' m2'; + } + $location = $this->getInput('lo'); + if(!empty($location)) { + $request .= ' In: ' . $location; + } + return 'Paru Vendu Immobilier' . $request; + } + + return parent::getName(); + } +} diff --git a/bridges/PickyWallpapersBridge.php b/bridges/PickyWallpapersBridge.php new file mode 100644 index 0000000..6c26df7 --- /dev/null +++ b/bridges/PickyWallpapersBridge.php @@ -0,0 +1,101 @@ + array( + 'name' => 'category', + 'required' => true + ), + 's' => array( + 'name' => 'subcategory' + ), + 'm' => array( + 'name' => 'Max number of wallpapers', + 'defaultValue' => 12, + 'type' => 'number' + ), + 'r' => array( + 'name' => 'resolution', + 'exampleValue' => '1920x1200, 1680x1050,…', + 'defaultValue' => '1920x1200', + 'pattern' => '[0-9]{3,4}x[0-9]{3,4}' + ) + )); + + public function collectData(){ + $lastpage = 1; + $num = 0; + $max = $this->getInput('m'); + $resolution = $this->getInput('r'); // Wide wallpaper default + + for($page = 1; $page <= $lastpage; $page++) { + $html = getSimpleHTMLDOM($this->getURI() . '/page-' . $page . '/') + or returnServerError('No results for this query.'); + + if($page === 1) { + preg_match('/page-(\d+)\/$/', $html->find('.pages li a', -2)->href, $matches); + $lastpage = min($matches[1], ceil($max / 12)); + } + + foreach($html->find('.items li img') as $element) { + $item = array(); + $item['uri'] = str_replace('www', 'wallpaper', self::URI) + . '/' + . $resolution + . '/' + . basename($element->src); + + $item['timestamp'] = time(); + $item['title'] = $element->alt; + $item['content'] = $item['title'] + . '
' + . $element + . ''; + + $this->items[] = $item; + + $num++; + if ($num >= $max) + break 2; + } + } + } + + public function getURI(){ + if(!is_null($this->getInput('s')) && !is_null($this->getInput('r')) && !is_null($this->getInput('c'))) { + $subcategory = $this->getInput('s'); + $link = self::URI + . $this->getInput('r') + . '/' + . $this->getInput('c') + . '/' + . $subcategory; + + return $link; + } + + return parent::getURI(); + } + + public function getName(){ + if(!is_null($this->getInput('s'))) { + $subcategory = $this->getInput('s'); + return 'PickyWallpapers - ' + . $this->getInput('c') + . ($subcategory ? ' > ' . $subcategory : '') + . ' [' + . $this->getInput('r') + . ']'; + } + + return parent::getName(); + } +} diff --git a/bridges/PinterestBridge.php b/bridges/PinterestBridge.php new file mode 100644 index 0000000..c5282ff --- /dev/null +++ b/bridges/PinterestBridge.php @@ -0,0 +1,163 @@ + array( + 'u' => array( + 'name' => 'username', + 'required' => true + ), + 'b' => array( + 'name' => 'board', + 'required' => true + ), + 'r' => array( + 'name' => 'Use custom RSS', + 'type' => 'checkbox', + 'required' => false, + 'title' => 'Uncheck to return data via custom filters (more data)' + ) + ), + 'From search' => array( + 'q' => array( + 'name' => 'Keyword', + 'required' => true + ) + ) + ); + + public function collectData(){ + switch($this->queriedContext) { + case 'By username and board': + if($this->getInput('r')) { + $html = getSimpleHTMLDOMCached($this->getURI()); + $this->getUserResults($html); + } else { + $this->collectExpandableDatas($this->getURI() . '.rss'); + } + break; + case 'From search': + default: + $html = getSimpleHTMLDOMCached($this->getURI()); + $this->getSearchResults($html); + } + } + + private function getUserResults($html){ + $json = json_decode($html->find('#jsInit1', 0)->innertext, true); + $results = $json['tree']['children'][0]['children'][0]['children'][0]['options']['props']['data']['board_feed']; + $username = $json['resourceDataCache'][0]['data']['owner']['username']; + $fullname = $json['resourceDataCache'][0]['data']['owner']['full_name']; + $avatar = $json['resourceDataCache'][0]['data']['owner']['image_small_url']; + + foreach($results as $result) { + $item = array(); + + $item['uri'] = $result['link']; + + // Some use regular titles, others provide 'advanced' infos, a few + // provide even less info. Thus we attempt multiple options. + $item['title'] = trim($result['title']); + + if($item['title'] === "") + $item['title'] = trim($result['rich_summary']['display_name']); + + if($item['title'] === "") + $item['title'] = trim($result['description']); + + $item['timestamp'] = strtotime($result['created_at']); + $item['username'] = $username; + $item['fullname'] = $fullname; + $item['avatar'] = $avatar; + $item['author'] = $item['username'] . ' (' . $item['fullname'] . ')'; + $item['content'] = '

' + . $item['username'] + . '
' + . $item['fullname'] + . '



' + . $result['description'] + . '

'; + + $item['enclosures'] = array($result['images']['orig']['url']); + + $this->items[] = $item; + } + } + + private function getSearchResults($html){ + $json = json_decode($html->find('#jsInit1', 0)->innertext, true); + $results = $json['resourceDataCache'][0]['data']['results']; + + foreach($results as $result) { + $item = array(); + + $item['uri'] = self::URI . $result['board']['url']; + + // Some use regular titles, others provide 'advanced' infos, a few + // provide even less info. Thus we attempt multiple options. + $item['title'] = trim($result['title']); + + if($item['title'] === "") + $item['title'] = trim($result['rich_summary']['display_name']); + + if($item['title'] === "") + $item['title'] = trim($result['grid_description']); + + $item['timestamp'] = strtotime($result['created_at']); + $item['username'] = $result['pinner']['username']; + $item['fullname'] = $result['pinner']['full_name']; + $item['avatar'] = $result['pinner']['image_small_url']; + $item['author'] = $item['username'] . ' (' . $item['fullname'] . ')'; + $item['content'] = '

' + . $item['username'] + . '
' + . $item['fullname'] + . '



' + . $result['description'] + . '

'; + + $item['enclosures'] = array($result['images']['orig']['url']); + + $this->items[] = $item; + } + } + + public function getURI(){ + switch($this->queriedContext) { + case 'By username and board': + $uri = self::URI . '/' . urlencode($this->getInput('u')) . '/' . urlencode($this->getInput('b'));// . '.rss'; + break; + case 'From search': + $uri = self::URI . '/search/?q=' . urlencode($this->getInput('q')); + break; + default: return parent::getURI(); + } + return $uri; + } + + public function getName(){ + switch($this->queriedContext) { + case 'By username and board': + $specific = $this->getInput('u') . ' - ' . $this->getInput('b'); + break; + case 'From search': + $specific = $this->getInput('q'); + break; + default: return parent::getName(); + } + return $specific . ' - ' . self::NAME; + } +} diff --git a/bridges/PlanetLibreBridge.php b/bridges/PlanetLibreBridge.php new file mode 100644 index 0000000..03a6024 --- /dev/null +++ b/bridges/PlanetLibreBridge.php @@ -0,0 +1,38 @@ +find('div[class="post-text"]', 0)->innertext; + return $text; + } + + public function collectData(){ + $html = getSimpleHTMLDOM(self::URI) + or returnServerError('Could not request PlanetLibre.'); + $limit = 0; + foreach($html->find('div.post') as $element) { + if($limit < 5) { + $item = array(); + $item['title'] = $element->find('h1', 0)->plaintext; + $item['uri'] = $element->find('a', 0)->href; + $item['timestamp'] = strtotime( + str_replace( + '/', + '-', + $element->find('div[class="post-date"]', 0)->plaintext + ) + ); + + $item['content'] = $this->extractContent($item['uri']); + $this->items[] = $item; + $limit++; + } + } + } +} diff --git a/bridges/RTBFBridge.php b/bridges/RTBFBridge.php new file mode 100644 index 0000000..22cdaf4 --- /dev/null +++ b/bridges/RTBFBridge.php @@ -0,0 +1,66 @@ + array( + 'name' => 'series id', + 'exampleValue' => 9500, + 'required' => true + ) + )); + + public function collectData(){ + $html = ''; + $limit = 10; + $count = 0; + + $html = getSimpleHTMLDOM($this->getURI()) + or returnServerError('Could not request RTBF.'); + + foreach($html->find('section[id!=widget-ml-avoiraussi-] .rtbf-media-grid article') as $element) { + if($count >= $limit) { + break; + } + + $item = array(); + $item['id'] = $element->getAttribute('data-id'); + $item['uri'] = self::URI . 'detail?id=' . $item['id']; + $thumbnailUriSrcSet = explode( + ',', + $element->find('figure .www-img-16by9 img', 0)->getAttribute('data-srcset') + ); + + $thumbnailUriLastSrc = end($thumbnailUriSrcSet); + $thumbnailUri = explode(' ', $thumbnailUriLastSrc)[0]; + $item['title'] = trim($element->find('h3', 0)->plaintext) + . ' - ' + . trim($element->find('h4', 0)->plaintext); + + $item['timestamp'] = strtotime($element->find('time', 0)->getAttribute('datetime')); + $item['content'] = ''; + $this->items[] = $item; + $count++; + } + } + + public function getURI(){ + if(!is_null($this->getInput('c'))) { + return self::URI . 'emissions/detail?id=' . $this->getInput('c'); + } + + return parent::getURI() . 'emissions/'; + } + + public function getName(){ + if(!is_null($this->getInput('c'))) { + return $this->getInput('c') .' - RTBF Bridge'; + } + + return parent::getName(); + } +} diff --git a/bridges/RainbowSixSiegeBridge.php b/bridges/RainbowSixSiegeBridge.php new file mode 100644 index 0000000..302bb89 --- /dev/null +++ b/bridges/RainbowSixSiegeBridge.php @@ -0,0 +1,36 @@ +find('h3 a', 0)->href; + $uri = 'https://rainbow6.ubisoft.com' . $uri; + $item['uri'] = $uri; + $item['title'] = $article->find('h3', 0)->plaintext; + $item['content'] = $article->find('img', 0)->outertext . '
' . $article->find('strong', 0)->plaintext; + $item['timestamp'] = strtotime($article->find('p.news_date', 0)->plaintext); + + $this->items[] = $item; + } + } +} diff --git a/bridges/ReadComicsBridge.php b/bridges/ReadComicsBridge.php new file mode 100644 index 0000000..33c8ed9 --- /dev/null +++ b/bridges/ReadComicsBridge.php @@ -0,0 +1,44 @@ + array( + 'name' => 'keywords, separated by semicolons', + 'exampleValue' => 'first list;second list;...', + 'required' => true + ), + )); + + public function collectData(){ + + function parseDateTimestamp($element){ + $guessedDate = $element->find('span', 0)->plaintext; + $guessedDate = strptime($guessedDate, '%m/%d/%Y'); + $timestamp = mktime(0, 0, 0, $guessedDate['tm_mon'] + 1, $guessedDate['tm_mday'], date('Y')); + + return $timestamp; + } + + $keywordsList = explode(";", $this->getInput('q')); + foreach($keywordsList as $keywords) { + $html = $this->getSimpleHTMLDOM(self::URI . 'comic/' . rawurlencode($keywords)) + or $this->returnServerError('Could not request readcomics.tv.'); + + foreach($html->find('li') as $element) { + $item = array(); + $item['uri'] = $element->find('a.ch-name', 0)->href; + $item['id'] = $item['uri']; + $item['timestamp'] = parseDateTimestamp($element); + $item['title'] = $element->find('a.ch-name', 0)->plaintext; + if(isset($item['title'])) + $this->items[] = $item; + } + } + } +} diff --git a/bridges/Releases3DSBridge.php b/bridges/Releases3DSBridge.php new file mode 100644 index 0000000..a7e1778 --- /dev/null +++ b/bridges/Releases3DSBridge.php @@ -0,0 +1,136 @@ +', $xml)) as $element) { + if($limit >= 5) { + break; + } + + if(strpos($element, '') === false) { + continue; + } + + $releasename = extractFromDelimiters($element, '', ''); + if(empty($releasename)) { + continue; + } + + $id = extractFromDelimiters($element, '', ''); + $name = extractFromDelimiters($element, '', ''); + $publisher = extractFromDelimiters($element, '', ''); + $region = extractFromDelimiters($element, '', ''); + $group = extractFromDelimiters($element, '', ''); + $imagesize = extractFromDelimiters($element, '', ''); + $serial = extractFromDelimiters($element, '', ''); + $titleid = extractFromDelimiters($element, '', ''); + $imgcrc = extractFromDelimiters($element, '', ''); + $filename = extractFromDelimiters($element, '', ''); + $trimmedsize = extractFromDelimiters($element, '', ''); + $firmware = extractFromDelimiters($element, '', ''); + $type = extractFromDelimiters($element, '', ''); + $card = extractFromDelimiters($element, '', ''); + + //Retrieve cover art and short desc from IGN? + $ignResult = false; + $ignDescription = ''; + $ignLink = ''; + $ignDate = time(); + $ignCoverArt = ''; + + $ignSearchUrl = 'http://www.ign.com/search?q=' . urlencode($name); + if($ignResult = getSimpleHTMLDOM($ignSearchUrl)) { + $ignCoverArt = $ignResult->find('div.search-item-media', 0)->find('img', 0)->src; + $ignDesc = $ignResult->find('div.search-item-description', 0)->plaintext; + $ignLink = $ignResult->find('div.search-item-sub-title', 0)->find('a', 1)->href; + $ignDate = strtotime(trim($ignResult->find('span.publish-date', 0)->plaintext)); + $ignDescription = '
' + . $ignDesc + . ' More at IGN
'; + } + + //Main section : Release description from 3DS database + $releaseDescription = '

Release Details

Release ID: ' . $id + . '
Game Name: ' . $name + . '
Publisher: ' . $publisher + . '
Region: ' . $region + . '
Group: ' . $group + . '
Image size: ' . (intval($imagesize) / 8) + . 'MB
Serial: ' . $serial + . '
Title ID: ' . $titleid + . '
Image CRC: ' . $imgcrc + . '
File Name: ' . $filename + . '
Release Name: ' . $releasename + . '
Trimmed size: ' . intval(intval($trimmedsize) / 1048576) + . 'MB
Firmware: ' . $firmware + . '
Type: ' . typeToString($type) + . '
Card: ' . cardToString($card) + . '
'; + + //Build search links section to facilitate release search using search engines + $releaseNameEncoded = urlencode(str_replace(' ', '+', $releasename)); + $searchLinkGoogle = 'https://google.com/?q=' . $releaseNameEncoded; + $searchLinkDuckDuckGo = 'https://duckduckgo.com/?q=' . $releaseNameEncoded; + $searchLinkQwant = 'https://lite.qwant.com/?q=' . $releaseNameEncoded . '&t=web'; + $releaseSearchLinks = '

Search this release

'; + + //Build and add final item with the above three sections + $item = array(); + $item['title'] = $name; + $item['author'] = $publisher; + $item['timestamp'] = $ignDate; + $item['uri'] = empty($ignLink) ? $searchLinkDuckDuckGo : $ignLink; + $item['content'] = $ignDescription . $releaseDescription . $releaseSearchLinks; + $this->items[] = $item; + $limit++; + } + } +} diff --git a/bridges/ReporterreBridge.php b/bridges/ReporterreBridge.php new file mode 100644 index 0000000..db1104c --- /dev/null +++ b/bridges/ReporterreBridge.php @@ -0,0 +1,47 @@ +find('div[style=text-align:justify]') as $e) { + $text = $e->outertext; + } + + $html2->clear(); + unset($html2); + + // Replace all relative urls with absolute ones + $text = preg_replace( + '/(href|src)(\=[\"\'])(?!http)([^"\']+)/ims', + "$1$2" . self::URI . "$3", + $text + ); + + $text = strip_tags($text, '


'); + return $text; + } + + public function collectData(){ + $html = getSimpleHTMLDOM(self::URI . 'spip.php?page=backend') + or returnServerError('Could not request Reporterre.'); + $limit = 0; + + foreach($html->find('item') as $element) { + if($limit < 5) { + $item = array(); + $item['title'] = html_entity_decode($element->find('title', 0)->plaintext); + $item['timestamp'] = strtotime($element->find('dc:date', 0)->plaintext); + $item['uri'] = $element->find('guid', 0)->innertext; + $item['content'] = html_entity_decode($this->extractContent($item['uri'])); + $this->items[] = $item; + $limit++; + } + } + } +} diff --git a/bridges/Rue89Bridge.php b/bridges/Rue89Bridge.php new file mode 100644 index 0000000..6599122 --- /dev/null +++ b/bridges/Rue89Bridge.php @@ -0,0 +1,25 @@ +collectExpandableDatas('http://api.rue89.nouvelobs.com/feed'); + } +} diff --git a/bridges/Rule34Bridge.php b/bridges/Rule34Bridge.php new file mode 100644 index 0000000..b46ec00 --- /dev/null +++ b/bridges/Rule34Bridge.php @@ -0,0 +1,12 @@ +find('article') as $article) { + $item = array(); + $item['uri'] = self::URI . $article->find('p.summary a', 0)->href; + $item['title'] = $article->find('header h1 a', 0)->innertext; + + // remove text "En savoir plus" from anecdote content + $article->find('span.read-more', 0)->outertext = ''; + $content = $article->find('p.summary a', 0)->innertext; + + // remove superfluous spaces at the end + $content = substr($content, 0, strlen($content) - 17); + + // get publication date + $str_date = $article->find('time', 0)->datetime; + list($date, $time) = explode(' ', $str_date); + list($y, $m, $d) = explode('-', $date); + list($h, $i) = explode(':', $time); + $timestamp = mktime($h, $i, 0, $m, $d, $y); + $item['timestamp'] = $timestamp; + + $item['content'] = $content; + $this->items[] = $item; + } + } +} diff --git a/bridges/ScoopItBridge.php b/bridges/ScoopItBridge.php new file mode 100644 index 0000000..997837d --- /dev/null +++ b/bridges/ScoopItBridge.php @@ -0,0 +1,42 @@ + array( + 'name' => 'keyword', + 'required' => true + ) + )); + + public function collectData(){ + $this->request = $this->getInput('u'); + $link = self::URI . 'search?q=' . urlencode($this->getInput('u')); + + $html = getSimpleHTMLDOM($link) + or returnServerError('Could not request ScoopIt. for : ' . $link); + + foreach($html->find('div.post-view') as $element) { + $item = array(); + $item['uri'] = $element->find('a', 0)->href; + $item['title'] = preg_replace( + '~[[:cntrl:]]~', + '', + $element->find('div.tCustomization_post_title', 0)->plaintext + ); + + $item['content'] = preg_replace( + '~[[:cntrl:]]~', + '', + $element->find('div.tCustomization_post_description', 0)->plaintext + ); + + $this->items[] = $item; + } + } +} diff --git a/bridges/SensCritiqueBridge.php b/bridges/SensCritiqueBridge.php new file mode 100644 index 0000000..7ac35f2 --- /dev/null +++ b/bridges/SensCritiqueBridge.php @@ -0,0 +1,97 @@ + array( + 'name' => 'Movies', + 'type' => 'checkbox' + ), + 's' => array( + 'name' => 'Series', + 'type' => 'checkbox' + ), + 'g' => array( + 'name' => 'Video Games', + 'type' => 'checkbox' + ), + 'b' => array( + 'name' => 'Books', + 'type' => 'checkbox' + ), + 'bd' => array( + 'name' => 'BD', + 'type' => 'checkbox' + ), + 'mu' => array( + 'name' => 'Music', + 'type' => 'checkbox' + ) + )); + + public function collectData(){ + $categories = array(); + foreach(self::PARAMETERS[$this->queriedContext] as $category => $properties) { + if($this->getInput($category)) { + $uri = self::URI; + switch($category) { + case 'm': $uri .= 'films/cette-semaine'; + break; + case 's': $uri .= 'series/actualite'; + break; + case 'g': $uri .= 'jeuxvideo/actualite'; + break; + case 'b': $uri .= 'livres/actualite'; + break; + case 'bd': $uri .= 'bd/actualite'; + break; + case 'mu': $uri .= 'musique/actualite'; + break; + } + $html = getSimpleHTMLDOM($uri) + or returnServerError('No results for this query.'); + $list = $html->find('ul.elpr-list', 0); + + $this->extractDataFromList($list); + } + } + } + + private function extractDataFromList($list){ + if($list === null) { + returnClientError('Cannot extract data from list'); + } + + foreach($list->find('li') as $movie) { + $item = array(); + $item['author'] = htmlspecialchars_decode($movie->find('.elco-title a', 0)->plaintext, ENT_QUOTES) + . ' ' + . $movie->find('.elco-date', 0)->plaintext; + + $item['title'] = $movie->find('.elco-title a', 0)->plaintext + . ' ' + . $movie->find('.elco-date', 0)->plaintext; + + $item['content'] = '' + . $movie->find('.elco-original-title', 0)->plaintext + . '

' + . $movie->find('.elco-baseline', 0)->plaintext + . '
' + . $movie->find('.elco-baseline', 1)->plaintext + . '

' + . $movie->find('.elco-description', 0)->plaintext + . '

' + . trim($movie->find('.erra-ratings .erra-global', 0)->plaintext) + . ' / 10'; + + $item['id'] = $this->getURI() . $movie->find('.elco-title a', 0)->href; + $item['uri'] = $this->getURI() . $movie->find('.elco-title a', 0)->href; + $this->items[] = $item; + } + } +} diff --git a/bridges/SexactuBridge.php b/bridges/SexactuBridge.php new file mode 100644 index 0000000..5bc552a --- /dev/null +++ b/bridges/SexactuBridge.php @@ -0,0 +1,88 @@ + 'href', + 'src' => 'src', + 'data-original' => 'src' + ); + + public function getURI(){ + return self::URI . '/sexactu'; + } + + public function collectData(){ + $html = getSimpleHTMLDOM($this->getURI()) + or returnServerError('Could not request ' . $this->getURI()); + + $sexactu = $html->find('.container_sexactu', 0); + $rowList = $sexactu->find('.row'); + foreach($rowList as $row) { + // only use first list as second one only contains pages numbers + + $title = $row->find('.title', 0); + if($title) { + $item = array(); + $item['author'] = self::AUTHOR; + $item['title'] = $title->plaintext; + $urlAttribute = "data-href"; + $uri = $title->$urlAttribute; + if($uri === false) + continue; + if(substr($uri, 0, 1) === 'h') { // absolute uri + $item['uri'] = $uri; + } else if(substr($uri, 0, 1) === '/') { // domain relative url + $item['uri'] = self::URI . $uri; + } else { + $item['uri'] = $this->getURI() . $uri; + } + $article = $this->loadFullArticle($item['uri']); + $item['content'] = $this->replaceUriInHtmlElement($article->find('.article_content', 0)); + + $publicationDate = $article->find('time[itemprop=datePublished]', 0); + $short_date = $publicationDate->datetime; + $item['timestamp'] = strtotime($short_date); + } else { + // Sometimes we get rubbish, ignore. + continue; + } + $this->items[] = $item; + } + } + + /** + * Loads the full article and returns the contents + * @param $uri The article URI + * @return The article content + */ + private function loadFullArticle($uri){ + $html = getSimpleHTMLDOMCached($uri); + + $content = $html->find('#article', 0); + if($content) { + return $content; + } + + return null; + } + + /** + * Replaces all relative URIs with absolute ones + * @param $element A simplehtmldom element + * @return The $element->innertext with all URIs replaced + */ + private function replaceUriInHtmlElement($element){ + $returned = $element->innertext; + foreach (self::REPLACED_ATTRIBUTES as $initial => $final) { + $returned = str_replace($initial . '="/', $final . '="' . self::URI . '/', $returned); + } + return $returned; + } +} diff --git a/bridges/ShanaprojectBridge.php b/bridges/ShanaprojectBridge.php new file mode 100644 index 0000000..e86f772 --- /dev/null +++ b/bridges/ShanaprojectBridge.php @@ -0,0 +1,123 @@ +getURI() . '/seasons'); + if(!$html) + returnServerError('Could not load \'seasons\' page!'); + + $season = $html->find('div.follows_menu/a', 1); + if(!$season) + returnServerError('Could not find \'Season Anime List\'!'); + + $html = getSimpleHTMLDOM($this->getURI() . $season->href); + if(!$html) + returnServerError( + 'Could not load \'Season Anime List\' from \'' + . $season->innertext + . '\'!' + ); + + return $html; + } + + // Extracts the anime title + private function extractAnimeTitle($anime){ + $title = $anime->find('a', 0); + if(!$title) + returnServerError('Could not find anime title!'); + return trim($title->innertext); + } + + // Extracts the anime URI + private function extractAnimeUri($anime){ + $uri = $anime->find('a', 0); + if(!$uri) + returnServerError('Could not find anime URI!'); + return $this->getURI() . $uri->href; + } + + // Extracts the anime release date (timestamp) + private function extractAnimeTimestamp($anime){ + $timestamp = $anime->find('span.header_info_block', 1); + if(!$timestamp) + return null; + return strtotime($timestamp->innertext); + } + + // Extracts the anime studio name (author) + private function extractAnimeAuthor($anime){ + $author = $anime->find('span.header_info_block', 2); + if(!$author) + return; // Sometimes the studio is unknown, so leave empty + return trim($author->innertext); + } + + // Extracts the episode information (x of y released) + private function extractAnimeEpisodeInformation($anime){ + $episode = $anime->find('div.header_info_episode', 0); + if(!$episode) + returnServerError('Could not find anime episode information!'); + return preg_replace('/\r|\n/', ' ', $episode->plaintext); + } + + // Extracts the background image + private function extractAnimeBackgroundImage($anime){ + // Getting the picture is a little bit tricky as it is part of the style. + // Luckily the style is part of the parent div :) + + if(preg_match("/url\(\/\/([^\)]+)\)/i", $anime->parent->style, $matches)) + return $matches[1]; + + returnServerError('Could not extract background image!'); + } + + // Builds an URI to search for a specific anime (subber is left empty) + private function buildAnimeSearchUri($anime){ + return $this->getURI() + . '/search/?title=' + . urlencode($this->extractAnimeTitle($anime)) + . '&subber='; + } + + // Builds the content string for a given anime + private function buildAnimeContent($anime){ + // We'll use a template string to place our contents + return '
'
+		. htmlspecialchars($this->extractAnimeTitle($anime))
+		. '

' + . $this->extractAnimeEpisodeInformation($anime) + . '


Search episodes

'; + } + + public function collectData(){ + $html = $this->loadSeasonAnimeList(); + + $animes = $html->find('div.header_display_box_info'); + if(!$animes) + returnServerError('Could not find anime headers!'); + + foreach($animes as $anime) { + $item = array(); + $item['title'] = $this->extractAnimeTitle($anime); + $item['author'] = $this->extractAnimeAuthor($anime); + $item['uri'] = $this->extractAnimeUri($anime); + $item['timestamp'] = $this->extractAnimeTimestamp($anime); + $item['content'] = $this->buildAnimeContent($anime); + $this->items[] = $item; + } + } +} diff --git a/bridges/Shimmie2Bridge.php b/bridges/Shimmie2Bridge.php new file mode 100644 index 0000000..efbcd9b --- /dev/null +++ b/bridges/Shimmie2Bridge.php @@ -0,0 +1,39 @@ +getURI() + . 'post/list/' + . $this->getInput('t') + . '/' + . $this->getInput('p'); + } + + protected function getItemFromElement($element){ + $item = array(); + $item['uri'] = $this->getURI() . $element->href; + $item['id'] = (int)preg_replace("/[^0-9]/", '', $element->getAttribute(static::IDATTRIBUTE)); + $item['timestamp'] = time(); + $thumbnailUri = $this->getURI() . $element->find('img', 0)->src; + $item['tags'] = $element->getAttribute('data-tags'); + $item['title'] = $this->getName() . ' | ' . $item['id']; + $item['content'] = '
Tags: ' + . $item['tags']; + + return $item; + } + +} diff --git a/bridges/SoundcloudBridge.php b/bridges/SoundcloudBridge.php new file mode 100644 index 0000000..92d77da --- /dev/null +++ b/bridges/SoundcloudBridge.php @@ -0,0 +1,64 @@ + array( + 'name' => 'username', + 'required' => true + ) + )); + + const CLIENT_ID = '0aca19eae3843844e4053c6d8fdb7875'; + + public function collectData(){ + + $res = json_decode(getContents( + 'https://api.soundcloud.com/resolve?url=http://www.soundcloud.com/' + . urlencode($this->getInput('u')) + . '&client_id=' + . self::CLIENT_ID + )) or returnServerError('No results for this query'); + + $tracks = json_decode(getContents( + 'https://api.soundcloud.com/users/' + . urlencode($res->id) + . '/tracks?client_id=' + . self::CLIENT_ID + )) or returnServerError('No results for this user'); + + for($i = 0; $i < 10; $i++) { + $item = array(); + $item['author'] = $tracks[$i]->user->username . ' - ' . $tracks[$i]->title; + $item['title'] = $tracks[$i]->user->username . ' - ' . $tracks[$i]->title; + $item['content'] = '
'); //strip thead + $html = stristr($html, ''); //remove tbody + $html = str_get_html(stristr($html, '', true)); //remove last tbody and get back as an array + foreach($html->find('tr') as $element) { + $item = array(); + $item['uri'] = $element->find('td', 2)->find('a', 0)->href; + $item['title'] = $element->find('td', 2)->find('a', 0)->plaintext; + $item['team'] = $element->find('td', 1)->innertext; + $item['timestamp'] = strtotime($element->find('td', 0)->plaintext); + $item['content'] = '' + . $this->seriesTitle + . ' - ' + . $item['title'] + . ' by ' + . $item['team'] + . '
' + . $fullhtml->find('div.seriesimg', 0)->innertext + . ''; + + $this->items[] = $item; + } + } + + public function getName(){ + if(!empty($this->seriesTitle)) { + return $this->seriesTitle . ' - ' . static::NAME; + } + + return parent::getName(); + } +} diff --git a/bridges/OpenClassroomsBridge.php b/bridges/OpenClassroomsBridge.php new file mode 100644 index 0000000..5f0daca --- /dev/null +++ b/bridges/OpenClassroomsBridge.php @@ -0,0 +1,49 @@ + array( + 'name' => 'Catégorie', + 'type' => 'list', + 'required' => true, + 'values' => array( + 'Arts & Culture' => 'arts', + 'Code' => 'code', + 'Design' => 'design', + 'Entreprise' => 'business', + 'Numérique' => 'digital', + 'Sciences' => 'sciences', + 'Sciences Humaines' => 'humainities', + 'Systèmes d\'information' => 'it', + 'Autres' => 'others' + ) + ) + )); + + public function getURI(){ + if(!is_null($this->getInput('u'))) { + return self::URI . '/courses?categories=' . $this->getInput('u') . '&title=&sort=updatedAt+desc'; + } + + return parent::getURI(); + } + + public function collectData(){ + $html = getSimpleHTMLDOM($this->getURI()) + or returnServerError('Could not request OpenClassrooms.'); + + foreach($html->find('.courseListItem') as $element) { + $item = array(); + $item['uri'] = self::URI . $element->find('a', 0)->href; + $item['title'] = $element->find('h3', 0)->plaintext; + $item['content'] = $element->find('slidingItem__descriptionContent', 0)->plaintext; + $this->items[] = $item; + } + } +} diff --git a/bridges/ParuVenduImmoBridge.php b/bridges/ParuVenduImmoBridge.php new file mode 100644 index 0000000..a2e2b33 --- /dev/null +++ b/bridges/ParuVenduImmoBridge.php @@ -0,0 +1,102 @@ + array( + 'name' => 'Minimal surface m²', + 'type' => 'number' + ), + 'maxprice' => array( + 'name' => 'Max price', + 'type' => 'number' + ), + 'pa' => array( + 'name' => 'Country code', + 'exampleValue' => 'FR' + ), + 'lo' => array( + 'name' => 'department numbers or postal codes, comma-separated' + ) + )); + + public function collectData(){ + $html = getSimpleHTMLDOM($this->getURI()) + or returnServerError('Could not request paruvendu.'); + + foreach($html->find('div.annonce a') as $element) { + + if(!$element->title) { + continue; + } + + $img = ''; + foreach($element->find('span.img img') as $img) { + if($img->original) { + $img = ''; + } + } + + $desc = $element->find('span.desc')[0]->innertext; + $desc = str_replace("voir l'annonce", '', $desc); + + $price = $element->find('span.price')[0]->innertext; + + list($href) = explode('#', $element->href); + + $item = array(); + $item['uri'] = self::URI . $href; + $item['title'] = $element->title; + $item['content'] = $img . $desc . $price; + $this->items[] = $item; + } + } + + public function getURI(){ + $appartment = '&tbApp=1&tbDup=1&tbChb=1&tbLof=1&tbAtl=1&tbPla=1'; + $maison = '&tbMai=1&tbVil=1&tbCha=1&tbPro=1&tbHot=1&tbMou=1&tbFer=1'; + $link = self::URI + . '/immobilier/annonceimmofo/liste/listeAnnonces?tt=1' + . $appartment + . $maison; + + if($this->getInput('minarea')) { + $link .= '&sur0=' . urlencode($this->getInput('minarea')); + } + + if($this->getInput('maxprice')) { + $link .= '&px1=' . urlencode($this->getInput('maxprice')); + } + + if($this->getInput('pa')) { + $link .= '&pa=' . urlencode($this->getInput('pa')); + } + + if($this->getInput('lo')) { + $link .= '&lo=' . urlencode($this->getInput('lo')); + } + return $link; + } + + public function getName(){ + if(!is_null($this->getInput('minarea'))) { + $request = ''; + $minarea = $this->getInput('minarea'); + if(!empty($minarea)) { + $request .= ' ' . $minarea . ' m2'; + } + $location = $this->getInput('lo'); + if(!empty($location)) { + $request .= ' In: ' . $location; + } + return 'Paru Vendu Immobilier' . $request; + } + + return parent::getName(); + } +} diff --git a/bridges/PcGamerBridge.php b/bridges/PcGamerBridge.php new file mode 100644 index 0000000..e0e55ce --- /dev/null +++ b/bridges/PcGamerBridge.php @@ -0,0 +1,23 @@ +getURI(), 300); + $stories = $html->find('div#popularcontent li.most-popular-item'); + foreach ($stories as $element) { + $item['uri'] = $element->find('a', 0)->href; + $articleHtml = getSimpleHTMLDOMCached($item['uri']); + $item['title'] = $element->find('h4 a', 0)->plaintext; + $item['timestamp'] = strtotime($articleHtml->find('meta[name=pub_date]', 0)->content); + $item['content'] = $articleHtml->find('meta[name=description]', 0)->content; + $item['author'] = $articleHtml->find('a[itemprop=author]', 0)->plaintext; + $this->items[] = $item; + } + } +} diff --git a/bridges/PickyWallpapersBridge.php b/bridges/PickyWallpapersBridge.php new file mode 100644 index 0000000..6c26df7 --- /dev/null +++ b/bridges/PickyWallpapersBridge.php @@ -0,0 +1,101 @@ + array( + 'name' => 'category', + 'required' => true + ), + 's' => array( + 'name' => 'subcategory' + ), + 'm' => array( + 'name' => 'Max number of wallpapers', + 'defaultValue' => 12, + 'type' => 'number' + ), + 'r' => array( + 'name' => 'resolution', + 'exampleValue' => '1920x1200, 1680x1050,…', + 'defaultValue' => '1920x1200', + 'pattern' => '[0-9]{3,4}x[0-9]{3,4}' + ) + )); + + public function collectData(){ + $lastpage = 1; + $num = 0; + $max = $this->getInput('m'); + $resolution = $this->getInput('r'); // Wide wallpaper default + + for($page = 1; $page <= $lastpage; $page++) { + $html = getSimpleHTMLDOM($this->getURI() . '/page-' . $page . '/') + or returnServerError('No results for this query.'); + + if($page === 1) { + preg_match('/page-(\d+)\/$/', $html->find('.pages li a', -2)->href, $matches); + $lastpage = min($matches[1], ceil($max / 12)); + } + + foreach($html->find('.items li img') as $element) { + $item = array(); + $item['uri'] = str_replace('www', 'wallpaper', self::URI) + . '/' + . $resolution + . '/' + . basename($element->src); + + $item['timestamp'] = time(); + $item['title'] = $element->alt; + $item['content'] = $item['title'] + . '
' + . $element + . ''; + + $this->items[] = $item; + + $num++; + if ($num >= $max) + break 2; + } + } + } + + public function getURI(){ + if(!is_null($this->getInput('s')) && !is_null($this->getInput('r')) && !is_null($this->getInput('c'))) { + $subcategory = $this->getInput('s'); + $link = self::URI + . $this->getInput('r') + . '/' + . $this->getInput('c') + . '/' + . $subcategory; + + return $link; + } + + return parent::getURI(); + } + + public function getName(){ + if(!is_null($this->getInput('s'))) { + $subcategory = $this->getInput('s'); + return 'PickyWallpapers - ' + . $this->getInput('c') + . ($subcategory ? ' > ' . $subcategory : '') + . ' [' + . $this->getInput('r') + . ']'; + } + + return parent::getName(); + } +} diff --git a/bridges/PinterestBridge.php b/bridges/PinterestBridge.php new file mode 100644 index 0000000..d2dd890 --- /dev/null +++ b/bridges/PinterestBridge.php @@ -0,0 +1,121 @@ + array( + 'u' => array( + 'name' => 'username', + 'required' => true + ), + 'b' => array( + 'name' => 'board', + 'required' => true + ) + ), + 'From search' => array( + 'q' => array( + 'name' => 'Keyword', + 'required' => true + ) + ) + ); + + public function collectData(){ + switch($this->queriedContext) { + case 'By username and board': + $this->collectExpandableDatas($this->getURI() . '.rss'); + $this->fixLowRes(); + break; + case 'From search': + default: + $html = getSimpleHTMLDOMCached($this->getURI()); + $this->getSearchResults($html); + } + } + + private function fixLowRes() { + + $newitems = []; + $pattern = '/https\:\/\/i\.pinimg\.com\/[a-zA-Z0-9]*x\//'; + foreach($this->items as $item) { + + $item['content'] = preg_replace($pattern, 'https://i.pinimg.com/originals/', $item['content']); + $newitems[] = $item; + } + $this->items = $newitems; + + } + + private function getSearchResults($html){ + $json = json_decode($html->find('#jsInit1', 0)->innertext, true); + $results = $json['resourceDataCache'][0]['data']['results']; + + foreach($results as $result) { + $item = array(); + + $item['uri'] = self::URI . $result['board']['url']; + + // Some use regular titles, others provide 'advanced' infos, a few + // provide even less info. Thus we attempt multiple options. + $item['title'] = trim($result['title']); + + if($item['title'] === '') + $item['title'] = trim($result['rich_summary']['display_name']); + + if($item['title'] === '') + $item['title'] = trim($result['grid_description']); + + $item['timestamp'] = strtotime($result['created_at']); + $item['username'] = $result['pinner']['username']; + $item['fullname'] = $result['pinner']['full_name']; + $item['avatar'] = $result['pinner']['image_small_url']; + $item['author'] = $item['username'] . ' (' . $item['fullname'] . ')'; + $item['content'] = '

' + . $item['username'] + . '
' + . $item['fullname'] + . '



' + . $result['description'] + . '

'; + + $item['enclosures'] = array($result['images']['orig']['url']); + + $this->items[] = $item; + } + } + + public function getURI(){ + switch($this->queriedContext) { + case 'By username and board': + $uri = self::URI . '/' . urlencode($this->getInput('u')) . '/' . urlencode($this->getInput('b'));// . '.rss'; + break; + case 'From search': + $uri = self::URI . '/search/?q=' . urlencode($this->getInput('q')); + break; + default: return parent::getURI(); + } + return $uri; + } + + public function getName(){ + switch($this->queriedContext) { + case 'By username and board': + $specific = $this->getInput('u') . ' - ' . $this->getInput('b'); + break; + case 'From search': + $specific = $this->getInput('q'); + break; + default: return parent::getName(); + } + return $specific . ' - ' . self::NAME; + } +} diff --git a/bridges/PixivBridge.php b/bridges/PixivBridge.php new file mode 100644 index 0000000..3a4cc93 --- /dev/null +++ b/bridges/PixivBridge.php @@ -0,0 +1,73 @@ + array( + 'name' => 'Tag to search', + 'exampleValue' => 'example', + 'required' => true + ), + )); + + + public function collectData(){ + + $html = getContents(static::URI.'search.php?word=' . urlencode($this->getInput('tag'))) + or returnClientError('Unable to query pixiv.net'); + $regex = '/getTimestamp(); + + $item['content'] = ""; + $this->items[] = $item; + } + } + + public function cacheImage($url, $illustId) { + + $url = str_replace('_master1200', '', $url); + $url = str_replace('c/240x240/img-master/', 'img-original/', $url); + $path = CACHE_DIR . '/pixiv_img'; + + if(!is_dir($path)) + mkdir($path, 0755, true); + + if(!is_file($path . '/' . $illustId . '.jpeg')) { + $headers = array('Referer: https://www.pixiv.net/member_illust.php?mode=medium&illust_id=' . $illustId); + $illust = getContents($url, $headers); + if(strpos($illust, '404 Not Found') !== false) { + $illust = getContents(str_replace('jpg', 'png', $url), $headers); + } + file_put_contents($path . '/' . $illustId . '.jpeg', $illust); + } + + return 'cache/pixiv_img/' . $illustId . '.jpeg'; + + } + +} diff --git a/bridges/RTBFBridge.php b/bridges/RTBFBridge.php new file mode 100644 index 0000000..22cdaf4 --- /dev/null +++ b/bridges/RTBFBridge.php @@ -0,0 +1,66 @@ + array( + 'name' => 'series id', + 'exampleValue' => 9500, + 'required' => true + ) + )); + + public function collectData(){ + $html = ''; + $limit = 10; + $count = 0; + + $html = getSimpleHTMLDOM($this->getURI()) + or returnServerError('Could not request RTBF.'); + + foreach($html->find('section[id!=widget-ml-avoiraussi-] .rtbf-media-grid article') as $element) { + if($count >= $limit) { + break; + } + + $item = array(); + $item['id'] = $element->getAttribute('data-id'); + $item['uri'] = self::URI . 'detail?id=' . $item['id']; + $thumbnailUriSrcSet = explode( + ',', + $element->find('figure .www-img-16by9 img', 0)->getAttribute('data-srcset') + ); + + $thumbnailUriLastSrc = end($thumbnailUriSrcSet); + $thumbnailUri = explode(' ', $thumbnailUriLastSrc)[0]; + $item['title'] = trim($element->find('h3', 0)->plaintext) + . ' - ' + . trim($element->find('h4', 0)->plaintext); + + $item['timestamp'] = strtotime($element->find('time', 0)->getAttribute('datetime')); + $item['content'] = ''; + $this->items[] = $item; + $count++; + } + } + + public function getURI(){ + if(!is_null($this->getInput('c'))) { + return self::URI . 'emissions/detail?id=' . $this->getInput('c'); + } + + return parent::getURI() . 'emissions/'; + } + + public function getName(){ + if(!is_null($this->getInput('c'))) { + return $this->getInput('c') .' - RTBF Bridge'; + } + + return parent::getName(); + } +} diff --git a/bridges/RadioMelodieBridge.php b/bridges/RadioMelodieBridge.php new file mode 100644 index 0000000..9b3772b --- /dev/null +++ b/bridges/RadioMelodieBridge.php @@ -0,0 +1,30 @@ +find('div[class=actuitem]'); + foreach($list as $element) { + $item = array(); + + // Get picture URL + $pictureHTML = $element->find('div[class=picture]'); + preg_match( + '/background-image:url\((.*)\);/', + $pictureHTML[0]->getAttribute('style'), + $pictures); + $pictureURL = $pictures[1]; + + $item['enclosures'] = array($pictureURL); + $item['uri'] = self::URI . $element->parent()->href; + $item['title'] = $element->find('h3', 0)->plaintext; + $item['content'] = $element->find('p', 0)->plaintext . '
'; + $this->items[] = $item; + } + } +} diff --git a/bridges/RainbowSixSiegeBridge.php b/bridges/RainbowSixSiegeBridge.php new file mode 100644 index 0000000..d362bbd --- /dev/null +++ b/bridges/RainbowSixSiegeBridge.php @@ -0,0 +1,36 @@ +find('h3 a', 0)->href; + $uri = 'https://rainbow6.ubisoft.com' . $uri; + $item['uri'] = $uri; + $item['title'] = $article->find('h3', 0)->plaintext; + $item['content'] = $article->find('img', 0)->outertext . '
' . $article->find('strong', 0)->plaintext; + $item['timestamp'] = strtotime($article->find('p.news_date', 0)->plaintext); + + $this->items[] = $item; + } + } +} diff --git a/bridges/ReadComicsBridge.php b/bridges/ReadComicsBridge.php new file mode 100644 index 0000000..739e6cc --- /dev/null +++ b/bridges/ReadComicsBridge.php @@ -0,0 +1,44 @@ + array( + 'name' => 'keywords, separated by semicolons', + 'exampleValue' => 'first list;second list;...', + 'required' => true + ), + )); + + public function collectData(){ + + function parseDateTimestamp($element){ + $guessedDate = $element->find('span', 0)->plaintext; + $guessedDate = strptime($guessedDate, '%m/%d/%Y'); + $timestamp = mktime(0, 0, 0, $guessedDate['tm_mon'] + 1, $guessedDate['tm_mday'], date('Y')); + + return $timestamp; + } + + $keywordsList = explode(';', $this->getInput('q')); + foreach($keywordsList as $keywords) { + $html = $this->getSimpleHTMLDOM(self::URI . 'comic/' . rawurlencode($keywords)) + or $this->returnServerError('Could not request readcomics.tv.'); + + foreach($html->find('li') as $element) { + $item = array(); + $item['uri'] = $element->find('a.ch-name', 0)->href; + $item['id'] = $item['uri']; + $item['timestamp'] = parseDateTimestamp($element); + $item['title'] = $element->find('a.ch-name', 0)->plaintext; + if(isset($item['title'])) + $this->items[] = $item; + } + } + } +} diff --git a/bridges/Releases3DSBridge.php b/bridges/Releases3DSBridge.php new file mode 100644 index 0000000..a7e1778 --- /dev/null +++ b/bridges/Releases3DSBridge.php @@ -0,0 +1,136 @@ +', $xml)) as $element) { + if($limit >= 5) { + break; + } + + if(strpos($element, '') === false) { + continue; + } + + $releasename = extractFromDelimiters($element, '', ''); + if(empty($releasename)) { + continue; + } + + $id = extractFromDelimiters($element, '', ''); + $name = extractFromDelimiters($element, '', ''); + $publisher = extractFromDelimiters($element, '', ''); + $region = extractFromDelimiters($element, '', ''); + $group = extractFromDelimiters($element, '', ''); + $imagesize = extractFromDelimiters($element, '', ''); + $serial = extractFromDelimiters($element, '', ''); + $titleid = extractFromDelimiters($element, '', ''); + $imgcrc = extractFromDelimiters($element, '', ''); + $filename = extractFromDelimiters($element, '', ''); + $trimmedsize = extractFromDelimiters($element, '', ''); + $firmware = extractFromDelimiters($element, '', ''); + $type = extractFromDelimiters($element, '', ''); + $card = extractFromDelimiters($element, '', ''); + + //Retrieve cover art and short desc from IGN? + $ignResult = false; + $ignDescription = ''; + $ignLink = ''; + $ignDate = time(); + $ignCoverArt = ''; + + $ignSearchUrl = 'http://www.ign.com/search?q=' . urlencode($name); + if($ignResult = getSimpleHTMLDOM($ignSearchUrl)) { + $ignCoverArt = $ignResult->find('div.search-item-media', 0)->find('img', 0)->src; + $ignDesc = $ignResult->find('div.search-item-description', 0)->plaintext; + $ignLink = $ignResult->find('div.search-item-sub-title', 0)->find('a', 1)->href; + $ignDate = strtotime(trim($ignResult->find('span.publish-date', 0)->plaintext)); + $ignDescription = '
' + . $ignDesc + . ' More at IGN
'; + } + + //Main section : Release description from 3DS database + $releaseDescription = '

Release Details

Release ID: ' . $id + . '
Game Name: ' . $name + . '
Publisher: ' . $publisher + . '
Region: ' . $region + . '
Group: ' . $group + . '
Image size: ' . (intval($imagesize) / 8) + . 'MB
Serial: ' . $serial + . '
Title ID: ' . $titleid + . '
Image CRC: ' . $imgcrc + . '
File Name: ' . $filename + . '
Release Name: ' . $releasename + . '
Trimmed size: ' . intval(intval($trimmedsize) / 1048576) + . 'MB
Firmware: ' . $firmware + . '
Type: ' . typeToString($type) + . '
Card: ' . cardToString($card) + . '
'; + + //Build search links section to facilitate release search using search engines + $releaseNameEncoded = urlencode(str_replace(' ', '+', $releasename)); + $searchLinkGoogle = 'https://google.com/?q=' . $releaseNameEncoded; + $searchLinkDuckDuckGo = 'https://duckduckgo.com/?q=' . $releaseNameEncoded; + $searchLinkQwant = 'https://lite.qwant.com/?q=' . $releaseNameEncoded . '&t=web'; + $releaseSearchLinks = '

Search this release

'; + + //Build and add final item with the above three sections + $item = array(); + $item['title'] = $name; + $item['author'] = $publisher; + $item['timestamp'] = $ignDate; + $item['uri'] = empty($ignLink) ? $searchLinkDuckDuckGo : $ignLink; + $item['content'] = $ignDescription . $releaseDescription . $releaseSearchLinks; + $this->items[] = $item; + $limit++; + } + } +} diff --git a/bridges/ReporterreBridge.php b/bridges/ReporterreBridge.php new file mode 100644 index 0000000..438c55b --- /dev/null +++ b/bridges/ReporterreBridge.php @@ -0,0 +1,47 @@ +find('div[style=text-align:justify]') as $e) { + $text = $e->outertext; + } + + $html2->clear(); + unset($html2); + + // Replace all relative urls with absolute ones + $text = preg_replace( + '/(href|src)(\=[\"\'])(?!http)([^"\']+)/ims', + '$1$2' . self::URI . '$3', + $text + ); + + $text = strip_tags($text, '


'); + return $text; + } + + public function collectData(){ + $html = getSimpleHTMLDOM(self::URI . 'spip.php?page=backend') + or returnServerError('Could not request Reporterre.'); + $limit = 0; + + foreach($html->find('item') as $element) { + if($limit < 5) { + $item = array(); + $item['title'] = html_entity_decode($element->find('title', 0)->plaintext); + $item['timestamp'] = strtotime($element->find('dc:date', 0)->plaintext); + $item['uri'] = $element->find('guid', 0)->innertext; + $item['content'] = html_entity_decode($this->extractContent($item['uri'])); + $this->items[] = $item; + $limit++; + } + } + } +} diff --git a/bridges/Rue89Bridge.php b/bridges/Rue89Bridge.php new file mode 100644 index 0000000..72f01eb --- /dev/null +++ b/bridges/Rue89Bridge.php @@ -0,0 +1,25 @@ +collectExpandableDatas('http://api.rue89.nouvelobs.com/feed'); + } +} diff --git a/bridges/Rule34Bridge.php b/bridges/Rule34Bridge.php new file mode 100644 index 0000000..b46ec00 --- /dev/null +++ b/bridges/Rule34Bridge.php @@ -0,0 +1,12 @@ +find('article') as $article) { + $item = array(); + $item['uri'] = self::URI . $article->find('p.summary a', 0)->href; + $item['title'] = $article->find('header h1 a', 0)->innertext; + + // remove text "En savoir plus" from anecdote content + $article->find('span.read-more', 0)->outertext = ''; + $content = $article->find('p.summary a', 0)->innertext; + + // remove superfluous spaces at the end + $content = substr($content, 0, strlen($content) - 17); + + // get publication date + $str_date = $article->find('time', 0)->datetime; + list($date, $time) = explode(' ', $str_date); + list($y, $m, $d) = explode('-', $date); + list($h, $i) = explode(':', $time); + $timestamp = mktime($h, $i, 0, $m, $d, $y); + $item['timestamp'] = $timestamp; + + $item['content'] = $content; + $this->items[] = $item; + } + } +} diff --git a/bridges/ScoopItBridge.php b/bridges/ScoopItBridge.php new file mode 100644 index 0000000..997837d --- /dev/null +++ b/bridges/ScoopItBridge.php @@ -0,0 +1,42 @@ + array( + 'name' => 'keyword', + 'required' => true + ) + )); + + public function collectData(){ + $this->request = $this->getInput('u'); + $link = self::URI . 'search?q=' . urlencode($this->getInput('u')); + + $html = getSimpleHTMLDOM($link) + or returnServerError('Could not request ScoopIt. for : ' . $link); + + foreach($html->find('div.post-view') as $element) { + $item = array(); + $item['uri'] = $element->find('a', 0)->href; + $item['title'] = preg_replace( + '~[[:cntrl:]]~', + '', + $element->find('div.tCustomization_post_title', 0)->plaintext + ); + + $item['content'] = preg_replace( + '~[[:cntrl:]]~', + '', + $element->find('div.tCustomization_post_description', 0)->plaintext + ); + + $this->items[] = $item; + } + } +} diff --git a/bridges/SensCritiqueBridge.php b/bridges/SensCritiqueBridge.php new file mode 100644 index 0000000..7ac35f2 --- /dev/null +++ b/bridges/SensCritiqueBridge.php @@ -0,0 +1,97 @@ + array( + 'name' => 'Movies', + 'type' => 'checkbox' + ), + 's' => array( + 'name' => 'Series', + 'type' => 'checkbox' + ), + 'g' => array( + 'name' => 'Video Games', + 'type' => 'checkbox' + ), + 'b' => array( + 'name' => 'Books', + 'type' => 'checkbox' + ), + 'bd' => array( + 'name' => 'BD', + 'type' => 'checkbox' + ), + 'mu' => array( + 'name' => 'Music', + 'type' => 'checkbox' + ) + )); + + public function collectData(){ + $categories = array(); + foreach(self::PARAMETERS[$this->queriedContext] as $category => $properties) { + if($this->getInput($category)) { + $uri = self::URI; + switch($category) { + case 'm': $uri .= 'films/cette-semaine'; + break; + case 's': $uri .= 'series/actualite'; + break; + case 'g': $uri .= 'jeuxvideo/actualite'; + break; + case 'b': $uri .= 'livres/actualite'; + break; + case 'bd': $uri .= 'bd/actualite'; + break; + case 'mu': $uri .= 'musique/actualite'; + break; + } + $html = getSimpleHTMLDOM($uri) + or returnServerError('No results for this query.'); + $list = $html->find('ul.elpr-list', 0); + + $this->extractDataFromList($list); + } + } + } + + private function extractDataFromList($list){ + if($list === null) { + returnClientError('Cannot extract data from list'); + } + + foreach($list->find('li') as $movie) { + $item = array(); + $item['author'] = htmlspecialchars_decode($movie->find('.elco-title a', 0)->plaintext, ENT_QUOTES) + . ' ' + . $movie->find('.elco-date', 0)->plaintext; + + $item['title'] = $movie->find('.elco-title a', 0)->plaintext + . ' ' + . $movie->find('.elco-date', 0)->plaintext; + + $item['content'] = '' + . $movie->find('.elco-original-title', 0)->plaintext + . '

' + . $movie->find('.elco-baseline', 0)->plaintext + . '
' + . $movie->find('.elco-baseline', 1)->plaintext + . '

' + . $movie->find('.elco-description', 0)->plaintext + . '

' + . trim($movie->find('.erra-ratings .erra-global', 0)->plaintext) + . ' / 10'; + + $item['id'] = $this->getURI() . $movie->find('.elco-title a', 0)->href; + $item['uri'] = $this->getURI() . $movie->find('.elco-title a', 0)->href; + $this->items[] = $item; + } + } +} diff --git a/bridges/SexactuBridge.php b/bridges/SexactuBridge.php new file mode 100644 index 0000000..b0a7174 --- /dev/null +++ b/bridges/SexactuBridge.php @@ -0,0 +1,88 @@ + 'href', + 'src' => 'src', + 'data-original' => 'src' + ); + + public function getURI(){ + return self::URI . '/sexactu'; + } + + public function collectData(){ + $html = getSimpleHTMLDOM($this->getURI()) + or returnServerError('Could not request ' . $this->getURI()); + + $sexactu = $html->find('.container_sexactu', 0); + $rowList = $sexactu->find('.row'); + foreach($rowList as $row) { + // only use first list as second one only contains pages numbers + + $title = $row->find('.title', 0); + if($title) { + $item = array(); + $item['author'] = self::AUTHOR; + $item['title'] = $title->plaintext; + $urlAttribute = 'data-href'; + $uri = $title->$urlAttribute; + if($uri === false) + continue; + if(substr($uri, 0, 1) === 'h') { // absolute uri + $item['uri'] = $uri; + } else if(substr($uri, 0, 1) === '/') { // domain relative url + $item['uri'] = self::URI . $uri; + } else { + $item['uri'] = $this->getURI() . $uri; + } + $article = $this->loadFullArticle($item['uri']); + $item['content'] = $this->replaceUriInHtmlElement($article->find('.article_content', 0)); + + $publicationDate = $article->find('time[itemprop=datePublished]', 0); + $short_date = $publicationDate->datetime; + $item['timestamp'] = strtotime($short_date); + } else { + // Sometimes we get rubbish, ignore. + continue; + } + $this->items[] = $item; + } + } + + /** + * Loads the full article and returns the contents + * @param $uri The article URI + * @return The article content + */ + private function loadFullArticle($uri){ + $html = getSimpleHTMLDOMCached($uri); + + $content = $html->find('#article', 0); + if($content) { + return $content; + } + + return null; + } + + /** + * Replaces all relative URIs with absolute ones + * @param $element A simplehtmldom element + * @return The $element->innertext with all URIs replaced + */ + private function replaceUriInHtmlElement($element){ + $returned = $element->innertext; + foreach (self::REPLACED_ATTRIBUTES as $initial => $final) { + $returned = str_replace($initial . '="/', $final . '="' . self::URI . '/', $returned); + } + return $returned; + } +} diff --git a/bridges/ShanaprojectBridge.php b/bridges/ShanaprojectBridge.php new file mode 100644 index 0000000..6eadcb1 --- /dev/null +++ b/bridges/ShanaprojectBridge.php @@ -0,0 +1,123 @@ +getURI() . '/seasons'); + if(!$html) + returnServerError('Could not load \'seasons\' page!'); + + $season = $html->find('div.follows_menu/a', 1); + if(!$season) + returnServerError('Could not find \'Season Anime List\'!'); + + $html = getSimpleHTMLDOM($this->getURI() . $season->href); + if(!$html) + returnServerError( + 'Could not load \'Season Anime List\' from \'' + . $season->innertext + . '\'!' + ); + + return $html; + } + + // Extracts the anime title + private function extractAnimeTitle($anime){ + $title = $anime->find('a', 0); + if(!$title) + returnServerError('Could not find anime title!'); + return trim($title->innertext); + } + + // Extracts the anime URI + private function extractAnimeUri($anime){ + $uri = $anime->find('a', 0); + if(!$uri) + returnServerError('Could not find anime URI!'); + return $this->getURI() . $uri->href; + } + + // Extracts the anime release date (timestamp) + private function extractAnimeTimestamp($anime){ + $timestamp = $anime->find('span.header_info_block', 1); + if(!$timestamp) + return null; + return strtotime($timestamp->innertext); + } + + // Extracts the anime studio name (author) + private function extractAnimeAuthor($anime){ + $author = $anime->find('span.header_info_block', 2); + if(!$author) + return; // Sometimes the studio is unknown, so leave empty + return trim($author->innertext); + } + + // Extracts the episode information (x of y released) + private function extractAnimeEpisodeInformation($anime){ + $episode = $anime->find('div.header_info_episode', 0); + if(!$episode) + returnServerError('Could not find anime episode information!'); + return preg_replace('/\r|\n/', ' ', $episode->plaintext); + } + + // Extracts the background image + private function extractAnimeBackgroundImage($anime){ + // Getting the picture is a little bit tricky as it is part of the style. + // Luckily the style is part of the parent div :) + + if(preg_match('/url\(\/\/([^\)]+)\)/i', $anime->parent->style, $matches)) + return $matches[1]; + + returnServerError('Could not extract background image!'); + } + + // Builds an URI to search for a specific anime (subber is left empty) + private function buildAnimeSearchUri($anime){ + return $this->getURI() + . '/search/?title=' + . urlencode($this->extractAnimeTitle($anime)) + . '&subber='; + } + + // Builds the content string for a given anime + private function buildAnimeContent($anime){ + // We'll use a template string to place our contents + return '
'
+		. htmlspecialchars($this->extractAnimeTitle($anime))
+		. '

' + . $this->extractAnimeEpisodeInformation($anime) + . '


Search episodes

'; + } + + public function collectData(){ + $html = $this->loadSeasonAnimeList(); + + $animes = $html->find('div.header_display_box_info'); + if(!$animes) + returnServerError('Could not find anime headers!'); + + foreach($animes as $anime) { + $item = array(); + $item['title'] = $this->extractAnimeTitle($anime); + $item['author'] = $this->extractAnimeAuthor($anime); + $item['uri'] = $this->extractAnimeUri($anime); + $item['timestamp'] = $this->extractAnimeTimestamp($anime); + $item['content'] = $this->buildAnimeContent($anime); + $this->items[] = $item; + } + } +} diff --git a/bridges/Shimmie2Bridge.php b/bridges/Shimmie2Bridge.php new file mode 100644 index 0000000..bdbc504 --- /dev/null +++ b/bridges/Shimmie2Bridge.php @@ -0,0 +1,39 @@ +getURI() + . 'post/list/' + . $this->getInput('t') + . '/' + . $this->getInput('p'); + } + + protected function getItemFromElement($element){ + $item = array(); + $item['uri'] = $this->getURI() . $element->href; + $item['id'] = (int)preg_replace('/[^0-9]/', '', $element->getAttribute(static::IDATTRIBUTE)); + $item['timestamp'] = time(); + $thumbnailUri = $this->getURI() . $element->find('img', 0)->src; + $item['tags'] = $element->getAttribute('data-tags'); + $item['title'] = $this->getName() . ' | ' . $item['id']; + $item['content'] = '
Tags: ' + . $item['tags']; + + return $item; + } + +} diff --git a/bridges/SoundcloudBridge.php b/bridges/SoundcloudBridge.php new file mode 100644 index 0000000..bfd97cb --- /dev/null +++ b/bridges/SoundcloudBridge.php @@ -0,0 +1,64 @@ + array( + 'name' => 'username', + 'required' => true + ) + )); + + const CLIENT_ID = '4jkoEFmZEDaqjwJ9Eih4ATNhcH3vMVfp'; + + public function collectData(){ + + $res = json_decode(getContents( + 'https://api.soundcloud.com/resolve?url=http://www.soundcloud.com/' + . urlencode($this->getInput('u')) + . '&client_id=' + . self::CLIENT_ID + )) or returnServerError('No results for this query'); + + $tracks = json_decode(getContents( + 'https://api.soundcloud.com/users/' + . urlencode($res->id) + . '/tracks?client_id=' + . self::CLIENT_ID + )) or returnServerError('No results for this user'); + + for($i = 0; $i < 10; $i++) { + $item = array(); + $item['author'] = $tracks[$i]->user->username . ' - ' . $tracks[$i]->title; + $item['title'] = $tracks[$i]->user->username . ' - ' . $tracks[$i]->title; + $item['content'] = '

' - . $deal->find('a[class*='. $selectorLink .']', 0)->innertext + . $deal->find('a[class*=' . $selectorLink . ']', 0)->innertext . '

' . $this->getPrice($deal) . $this->getDiscount($deal) . $this->getShipsFrom($deal) . $this->getShippingCost($deal) . $this->GetSource($deal) - . $deal->find('div[class*='. $selectorDescription .']', 0)->innertext + . $deal->find('div[class*=' . $selectorDescription . ']', 0)->innertext . '
' - . $deal->find('div[class='. $selectorHot .']', 0)->children(0)->outertext + . $deal->find('div[class*=' . $selectorHot . ']', 0) + ->find('span', 1)->outertext . '
'; - $dealDateDiv = $deal->find('div[class*='. $selectorDate .']', 0) + $dealDateDiv = $deal->find('div[class*=' . $selectorDate . ']', 0) ->find('span[class=hide--toW3]'); $itemDate = end($dealDateDiv)->plaintext; // In case of a Local deal, there is no date, but we can use @@ -327,7 +1214,7 @@ class PepperBridgeAbstract extends BridgeAbstract { { if ($deal->find( 'span[class*=thread-price]', 0) != null) { - return '
'.$this->i8n('price') .' : ' + return '
' . $this->i8n('price') . ' : ' . $deal->find( 'span[class*=thread-price]', 0 )->plaintext @@ -346,11 +1233,11 @@ class PepperBridgeAbstract extends BridgeAbstract { { if ($deal->find('span[class*=cept-shipping-price]', 0) != null) { if ($deal->find('span[class*=cept-shipping-price]', 0)->children(0) != null) { - return '
'. $this->i8n('shipping') .' : ' + return '
' . $this->i8n('shipping') . ' : ' . $deal->find('span[class*=cept-shipping-price]', 0)->children(0)->innertext . '
'; } else { - return '
'. $this->i8n('shipping') .' : ' + return '
' . $this->i8n('shipping') . ' : ' . $deal->find('span[class*=cept-shipping-price]', 0)->innertext . '
'; } @@ -366,7 +1253,7 @@ class PepperBridgeAbstract extends BridgeAbstract { private function GetSource($deal) { if ($deal->find('a[class=text--color-greyShade]', 0) != null) { - return '
'. $this->i8n('origin') .' : ' + return '
' . $this->i8n('origin') . ' : ' . $deal->find('a[class=text--color-greyShade]', 0)->outertext . '
'; } else { @@ -387,7 +1274,7 @@ class PepperBridgeAbstract extends BridgeAbstract { } else { $discount = ''; } - return '
'. $this->i8n('discount') .' : ' + return '
' . $this->i8n('discount') . ' : ' . $deal->find( 'span[class*=mute--text text--lineThrough]', 0 )->plaintext @@ -428,13 +1315,13 @@ class PepperBridgeAbstract extends BridgeAbstract { 'cept-thread-img' ) ); - if ($deal->find('img[class='. $selectorLazy .']', 0) != null) { + if ($deal->find('img[class=' . $selectorLazy . ']', 0) != null) { return json_decode( html_entity_decode( - $deal->find('img[class='. $selectorLazy .']', 0) + $deal->find('img[class=' . $selectorLazy . ']', 0) ->getAttribute('data-lazy-img')))->{'src'}; } else { - return $deal->find('img[class*='. $selectorPlain .']', 0 )->src; + return $deal->find('img[class*=' . $selectorPlain . ']', 0 )->src; } } @@ -453,9 +1340,9 @@ class PepperBridgeAbstract extends BridgeAbstract { 'text--color-greyShade' ) ); - if ($deal->find('span[class='. $selector .']', 0) != null) { + if ($deal->find('span[class=' . $selector . ']', 0) != null) { return '
' - . $deal->find('span[class='. $selector .']', 0)->children(2)->plaintext + . $deal->find('span[class=' . $selector . ']', 0)->children(2)->plaintext . '
'; } else { return ''; @@ -558,12 +1445,12 @@ class PepperBridgeAbstract extends BridgeAbstract { public function getName(){ switch($this->queriedContext) { case $this->i8n('context-keyword'): - return $this->i8n('bridge-name') . ' - '. $this->i8n('title-keyword') .' : '. $this->getInput('q'); + return $this->i8n('bridge-name') . ' - ' . $this->i8n('title-keyword') . ' : ' . $this->getInput('q'); break; case $this->i8n('context-group'): $values = $this->getParameters()[$this->i8n('context-group')]['group']['values']; $group = array_search($this->getInput('group'), $values); - return $this->i8n('bridge-name') . ' - '. $this->i8n('title-group'). ' : '. $group; + return $this->i8n('bridge-name') . ' - ' . $this->i8n('title-group') . ' : ' . $group; break; default: // Return default value return static::NAME; @@ -577,7 +1464,7 @@ class PepperBridgeAbstract extends BridgeAbstract { * the "$lang" class variable in the local class * @return various the local content needed */ - public function i8n($key) + protected function i8n($key) { if (array_key_exists($key, $this->lang)) { return $this->lang[$key]; diff --git a/bridges/DesoutterBridge.php b/bridges/DesoutterBridge.php new file mode 100644 index 0000000..94059f6 --- /dev/null +++ b/bridges/DesoutterBridge.php @@ -0,0 +1,240 @@ + array( + 'news_lang' => array( + 'name' => 'Language', + 'type' => 'list', + 'required' => true, + 'title' => 'Select your language', + 'defaultValue' => 'Corporate', + 'values' => array( + 'Corporate' + => 'https://www.desouttertools.com/about-desoutter/news-events', + 'Česko' + => 'https://www.desouttertools.cz/o-desoutter/aktuality-udalsoti', + 'Deutschland' + => 'https://www.desoutter.de/ueber-desoutter/news-events', + 'España' + => 'https://www.desouttertools.es/sobre-desoutter/noticias-eventos', + 'México' + => 'https://www.desouttertools.mx/acerca-desoutter/noticias-eventos', + 'France' + => 'https://www.desouttertools.fr/a-propos-de-desoutter/actualites-evenements', + 'Magyarország' + => 'https://www.desouttertools.hu/a-desoutter-vallalatrol/hirek-esemenyek', + 'Italia' + => 'https://www.desouttertools.it/su-desoutter/news-eventi', + '日本' + => 'https://www.desouttertools.jp/desotanituite/niyusu-ibento', + '대한민국' + => 'https://www.desouttertools.co.kr/desoteoe-daehaeseo/nyuseu-mic-ibenteu', + 'Polska' + => 'https://www.desouttertools.pl/o-desoutter/aktualnosci-wydarzenia', + 'Brasil' + => 'https://www.desouttertools.com.br/sobre-desoutter/noti%C2%ADcias-eventos', + 'Portugal' + => 'https://www.desouttertools.pt/sobre-desoutter/notIcias-eventos', + 'România' + => 'https://www.desouttertools.ro/despre-desoutter/noutati-evenimente', + 'Российская Федерация' + => 'https://www.desouttertools.com.ru/o-desoutter/novosti-mieropriiatiia', + 'Slovensko' + => 'https://www.desouttertools.sk/o-spolocnosti-desoutter/novinky-udalosti', + 'Slovenija' + => 'https://www.desouttertools.si/o-druzbi-desoutter/novice-dogodki', + 'Sverige' + => 'https://www.desouttertools.se/om-desoutter/nyheter-evenemang', + 'Türkiye' + => 'https://www.desoutter.com.tr/desoutter-hakkinda/haberler-etkinlikler', + '中国' + => 'https://www.desouttertools.com.cn/guan-yu-ma-tou/xin-wen-he-huo-dong', + ) + ), + ), + self::CATEGORY_INDUSTRY => array( + 'industry_lang' => array( + 'name' => 'Language', + 'type' => 'list', + 'required' => true, + 'title' => 'Select your language', + 'defaultValue' => 'Corporate', + 'values' => array( + 'Corporate' + => 'https://www.desouttertools.com/industry-4-0/news', + 'Česko' + => 'https://www.desouttertools.cz/prumysl-4-0/novinky', + 'Deutschland' + => 'https://www.desoutter.de/industrie-4-0/news', + 'España' + => 'https://www.desouttertools.es/industria-4-0/noticias', + 'México' + => 'https://www.desouttertools.mx/industria-4-0/noticias', + 'France' + => 'https://www.desouttertools.fr/industrie-4-0/actualites', + 'Magyarország' + => 'https://www.desouttertools.hu/industry-4-0/hirek', + 'Italia' + => 'https://www.desouttertools.it/industry-4-0/news', + '日本' + => 'https://www.desouttertools.jp/industry-4-0/news', + '대한민국' + => 'https://www.desouttertools.co.kr/industry-4-0/news', + 'Polska' + => 'https://www.desouttertools.pl/przemysl-4-0/wiadomosci', + 'Brasil' + => 'https://www.desouttertools.com.br/industria-4-0/noticias', + 'Portugal' + => 'https://www.desouttertools.pt/industria-4-0/noticias', + 'România' + => 'https://www.desouttertools.ro/industry-4-0/noutati', + 'Российская Федерация' + => 'https://www.desouttertools.com.ru/industry-4-0/news', + 'Slovensko' + => 'https://www.desouttertools.sk/priemysel-4-0/novinky', + 'Slovenija' + => 'https://www.desouttertools.si/industrija-4-0/novice', + 'Sverige' + => 'https://www.desouttertools.se/industri-4-0/nyheter', + 'Türkiye' + => 'https://www.desoutter.com.tr/endustri-4-0/haberler', + '中国' + => 'https://www.desouttertools.com.cn/industry-4-0/news', + ) + ), + ), + 'global' => array( + 'full' => array( + 'name' => 'Load full articles', + 'type' => 'checkbox', + 'required' => false, + 'title' => 'Enable to load the full article for each item' + ) + ) + ); + + private $title; + + public function getURI() { + switch($this->queriedContext) { + case self::CATEGORY_NEWS: + return $this->getInput('news_lang') ?: parent::getURI(); + case self::CATEGORY_INDUSTRY: + return $this->getInput('industry_lang') ?: parent::getURI(); + } + + return parent::getURI(); + } + + public function getName() { + return isset($this->title) ? $this->title . ' - ' . parent::getName() : parent::getName(); + } + + public function collectData() { + + // Uncomment to generate list of languages automtically (dev mode) + /* + switch($this->queriedContext) { + case self::CATEGORY_NEWS: + $this->extractNewsLanguages(); die; + case self::CATEGORY_INDUSTRY: + $this->extractIndustryLanguages(); die; + } + */ + + $html = getSimpleHTMLDOM($this->getURI()) + or returnServerError('Could not request ' . $this->getURI()); + + $html = defaultLinkTo($html, $this->getURI()); + + $this->title = html_entity_decode($html->find('title', 0)->plaintext, ENT_QUOTES); + + foreach($html->find('article') as $article) { + $item = array(); + + $item['uri'] = $article->find('[itemprop="name"]', 0)->href; + $item['title'] = $article->find('[itemprop="name"]', 0)->title; + + if($this->getInput('full')) { + $item['content'] = $this->getFullNewsArticle($item['uri']); + } else { + $item['content'] = $article->find('[itemprop="description"]', 0)->plaintext; + } + + $this->items[] = $item; + } + + } + + private function getFullNewsArticle($uri) { + $html = getSimpleHTMLDOMCached($uri) + or returnServerError('Unable to load full article!'); + + $html = defaultLinkTo($html, $this->getURI()); + + return $html->find('section.article', 0); + } + + /** + * Generates a HTML page with a PHP formatted array of languages, + * pointing to the corresponding news pages. Implementation is based + * on the 'Corporate' site. + * @return void + */ + private function extractNewsLanguages() { + $html = getSimpleHTMLDOMCached('https://www.desouttertools.com/about-desoutter/news-events') + or returnServerError('Error loading news!'); + + $html = defaultLinkTo($html, static::URI); + + $items = $html->find('ul[class="dropdown-menu"] li'); + + $list = "\t'Corporate'\n\t=> 'https://www.desouttertools.com/about-desoutter/news-events',\n"; + + foreach($items as $item) { + $lang = trim($item->plaintext); + $uri = $item->find('a', 0)->href; + + $list .= "\t'{$lang}'\n\t=> '{$uri}',\n"; + } + + echo $list; + } + + /** + * Generates a HTML page with a PHP formatted array of languages, + * pointing to the corresponding news pages. Implementation is based + * on the 'Corporate' site. + * @return void + */ + private function extractIndustryLanguages() { + $html = getSimpleHTMLDOMCached('https://www.desouttertools.com/industry-4-0/news') + or returnServerError('Error loading news!'); + + $html = defaultLinkTo($html, static::URI); + + $items = $html->find('ul[class="dropdown-menu"] li'); + + $list = "\t'Corporate'\n\t=> 'https://www.desouttertools.com/industry-4-0/news',\n"; + + foreach($items as $item) { + $lang = trim($item->plaintext); + $uri = $item->find('a', 0)->href; + + $list .= "\t'{$lang}'\n\t=> '{$uri}',\n"; + } + + echo $list; + } + +} diff --git a/bridges/DevToBridge.php b/bridges/DevToBridge.php new file mode 100644 index 0000000..2dfc033 --- /dev/null +++ b/bridges/DevToBridge.php @@ -0,0 +1,105 @@ + array( + 'tag' => array( + 'name' => 'Tag', + 'type' => 'text', + 'required' => true, + 'title' => 'Insert your tag', + 'exampleValue' => 'python' + ), + 'full' => array( + 'name' => 'Full article', + 'type' => 'checkbox', + 'required' => false, + 'title' => 'Enable to receive the full article for each item', + 'defaultValue' => false + ) + ) + ); + + public function getURI() { + switch($this->queriedContext) { + case self::CONTEXT_BY_TAG: + if($tag = $this->getInput('tag')) { + return static::URI . '/t/' . urlencode($tag); + } + break; + } + + return parent::getURI(); + } + + public function getIcon() { + return 'https://practicaldev-herokuapp-com.freetls.fastly.net/assets/ +apple-icon-5c6fa9f2bce280428589c6195b7f1924206a53b782b371cfe2d02da932c8c173.png'; + } + + public function collectData() { + + $html = getSimpleHTMLDOMCached($this->getURI()) + or returnServerError('Could not request ' . $this->getURI()); + + $html = defaultLinkTo($html, static::URI); + + $articles = $html->find('div[class="single-article"]') + or returnServerError('Could not find articles!'); + + foreach($articles as $article) { + + if($article->find('[class*="cta"]', 0)) { // Skip ads + continue; + } + + $item = array(); + + $item['uri'] = $article->find('a[id*=article-link]', 0)->href; + $item['title'] = $article->find('h3', 0)->plaintext; + + // i.e. "Charlie Harrington・Sep 21" + $item['timestamp'] = strtotime(explode('・', $article->find('h4 a', 0)->plaintext, 2)[1]); + $item['author'] = explode('・', $article->find('h4 a', 0)->plaintext, 2)[0]; + + // Profile image + $item['enclosures'] = array($article->find('img', 0)->src); + + if($this->getInput('full')) { + $fullArticle = $this->getFullArticle($item['uri']); + $item['content'] = << +

{$fullArticle}

+EOD; + } else { + $item['content'] = << +

{$item['title']}

+EOD; + } + + $item['categories'] = array_map(function($e){ return $e->plaintext; }, $article->find('div.tags span.tag')); + + $this->items[] = $item; + } + + } + + private function getFullArticle($url) { + $html = getSimpleHTMLDOMCached($url) + or returnServerError('Unable to load article from "' . $url . '"!'); + + $html = defaultLinkTo($html, static::URI); + + return $html->find('[id="article-body"]', 0); + } + +} diff --git a/bridges/DiceBridge.php b/bridges/DiceBridge.php index dc6ea15..11218df 100644 --- a/bridges/DiceBridge.php +++ b/bridges/DiceBridge.php @@ -75,6 +75,10 @@ class DiceBridge extends BridgeAbstract { ), )); + public function getIcon() { + return 'https://assets.dice.com/techpro/img/favicons/favicon.ico'; + } + public function collectData() { $uri = 'https://www.dice.com/jobs/advancedResult.html'; $uri .= '?for_one=' . urlencode($this->getInput('for_one')); diff --git a/bridges/DilbertBridge.php b/bridges/DilbertBridge.php index 959a91a..a84e5e8 100644 --- a/bridges/DilbertBridge.php +++ b/bridges/DilbertBridge.php @@ -9,8 +9,8 @@ class DilbertBridge extends BridgeAbstract { public function collectData(){ - $html = getSimpleHTMLDOM($this->getURI()) - or returnServerError('Could not request Dilbert: ' . $this->getURI()); + $html = getSimpleHTMLDOM(self::URI) + or returnServerError('Could not request Dilbert: ' . self::URI); foreach($html->find('section.comic-item') as $element) { diff --git a/bridges/DiscogsBridge.php b/bridges/DiscogsBridge.php index 9fe4f51..534ac08 100644 --- a/bridges/DiscogsBridge.php +++ b/bridges/DiscogsBridge.php @@ -81,7 +81,7 @@ class DiscogsBridge extends BridgeAbstract { . $this->getInput('username_folder') . '/collection/folders/' . $this->getInput('folderid') - .'/releases?sort=added&sort_order=desc') + . '/releases?sort=added&sort_order=desc') or returnServerError('Unable to query discogs !'); $jsonData = json_decode($data, true)['releases']; } diff --git a/bridges/DribbbleBridge.php b/bridges/DribbbleBridge.php index 07c4c6e..5058da6 100644 --- a/bridges/DribbbleBridge.php +++ b/bridges/DribbbleBridge.php @@ -7,6 +7,11 @@ class DribbbleBridge extends BridgeAbstract { const CACHE_TIMEOUT = 1800; const DESCRIPTION = 'Returns the newest popular shots from Dribbble.'; + public function getIcon() { + return 'https://cdn.dribbble.com/assets/ +favicon-63b2904a073c89b52b19aa08cebc16a154bcf83fee8ecc6439968b1e6db569c7.ico'; + } + public function collectData(){ $html = getSimpleHTMLDOM(self::URI . '/shots') or returnServerError('Error while downloading the website content'); diff --git a/bridges/ETTVBridge.php b/bridges/ETTVBridge.php index ab90bf7..c348ca0 100644 --- a/bridges/ETTVBridge.php +++ b/bridges/ETTVBridge.php @@ -94,17 +94,20 @@ class ETTVBridge extends BridgeAbstract { ) )); + protected $results_link; + public function collectData(){ - // No control on inputs, because all have defaultValue set + // No control on inputs, because all defaultValue are set $query_str = 'torrents-search.php'; - $query_str .= '?search=' . urlencode('+'.str_replace(' ', ' +', $this->getInput('query'))); + $query_str .= '?search=' . urlencode('+' . str_replace(' ', ' +', $this->getInput('query'))); $query_str .= '&cat=' . $this->getInput('cat'); - $query_str .= 'incldead&=' . $this->getInput('status'); + $query_str .= '&incldead=' . $this->getInput('status'); $query_str .= '&lang=' . $this->getInput('lang'); $query_str .= '&sort=id&order=desc'; // Get results page - $html = getSimpleHTMLDOM(self::URI . $query_str) + $this->results_link = self::URI . $query_str; + $html = getSimpleHTMLDOM($this->results_link) or returnServerError('Could not request ' . $this->getName()); // Loop on each entry @@ -125,7 +128,7 @@ class ETTVBridge extends BridgeAbstract { $item = array(); $item['author'] = $details->children(6)->children(1)->plaintext; $item['title'] = $entry->title; - $item['uri'] = $dllinks->children(0)->children(0)->children(0)->href; + $item['uri'] = $link; $item['timestamp'] = strtotime($details->children(7)->children(1)->plaintext); $item['content'] = ''; $item['content'] .= '
Name: ' . $details->children(0)->children(1)->innertext; @@ -139,4 +142,20 @@ class ETTVBridge extends BridgeAbstract { $this->items[] = $item; } } + + public function getName(){ + if($this->getInput('query')) { + return '[' . self::NAME . '] ' . $this->getInput('query'); + } + + return self::NAME; + } + + public function getURI(){ + if(isset($this->results_link) && !empty($this->results_link)) { + return $this->results_link; + } + + return self::URI; + } } diff --git a/bridges/EliteDangerousGalnetBridge.php b/bridges/EliteDangerousGalnetBridge.php index 86a1bbf..d19b360 100644 --- a/bridges/EliteDangerousGalnetBridge.php +++ b/bridges/EliteDangerousGalnetBridge.php @@ -7,6 +7,11 @@ class EliteDangerousGalnetBridge extends BridgeAbstract { const CACHE_TIMEOUT = 7200; // 2h const DESCRIPTION = 'Returns the latest page of news from Galnet'; + public function getIcon() { + return 'https://community.elitedangerous.com/sites/ +EDSITE_COMM/themes/bootstrap/bootstrap_community/favicon.ico'; + } + public function collectData(){ $html = getSimpleHTMLDOM(self::URI) or returnServerError('Error while downloading the website content'); diff --git a/bridges/ElloBridge.php b/bridges/ElloBridge.php index c0e266e..b51fed7 100644 --- a/bridges/ElloBridge.php +++ b/bridges/ElloBridge.php @@ -45,9 +45,10 @@ class ElloBridge extends BridgeAbstract { $item = array(); $item['author'] = $this->getUsername($post, $postData); $item['timestamp'] = strtotime($post->created_at); - $item['title'] = $this->findText($post->summary); + $item['title'] = strip_tags($this->findText($post->summary)); $item['content'] = $this->getPostContent($post->body); $item['enclosures'] = $this->getEnclosures($post, $postData); + $item['uri'] = self::URI . $item['author'] . '/post/' . $post->token; $content = $post->body; $this->items[] = $item; @@ -57,7 +58,7 @@ class ElloBridge extends BridgeAbstract { } - public function findText($path) { + private function findText($path) { foreach($path as $summaryElement) { @@ -71,7 +72,7 @@ class ElloBridge extends BridgeAbstract { } - public function getPostContent($path) { + private function getPostContent($path) { $content = ''; foreach($path as $summaryElement) { @@ -92,7 +93,7 @@ class ElloBridge extends BridgeAbstract { } - public function getEnclosures($post, $postData) { + private function getEnclosures($post, $postData) { $assets = []; foreach($post->links->assets as $asset) { @@ -108,7 +109,7 @@ class ElloBridge extends BridgeAbstract { } - public function getUsername($post, $postData) { + private function getUsername($post, $postData) { foreach($postData->linked->users as $user) { if($user->id == $post->links->author->id) { @@ -118,9 +119,9 @@ class ElloBridge extends BridgeAbstract { } - public function getAPIKey() { + private function getAPIKey() { $cache = Cache::create('FileCache'); - $cache->setPath(CACHE_DIR); + $cache->setPath(PATH_CACHE); $cache->setParameters(['key']); $key = $cache->loadData(); diff --git a/bridges/ElsevierBridge.php b/bridges/ElsevierBridge.php index f6ba7dd..080fe00 100644 --- a/bridges/ElsevierBridge.php +++ b/bridges/ElsevierBridge.php @@ -57,6 +57,10 @@ class ElsevierBridge extends BridgeAbstract { return ''; } + public function getIcon() { + return 'https://cdn.elsevier.io/verona/includes/favicons/favicon-32x32.png'; + } + public function collectData(){ $uri = self::URI . $this->getInput('j') . '/recent-articles/'; $html = getSimpleHTMLDOM($uri) diff --git a/bridges/EstCeQuonMetEnProdBridge.php b/bridges/EstCeQuonMetEnProdBridge.php index db9d1d5..4439d69 100644 --- a/bridges/EstCeQuonMetEnProdBridge.php +++ b/bridges/EstCeQuonMetEnProdBridge.php @@ -7,19 +7,9 @@ class EstCeQuonMetEnProdBridge extends BridgeAbstract { const CACHE_TIMEOUT = 21600; // 6h const DESCRIPTION = 'Should we put a website in production today? (French)'; - public function collectData(){ - function extractFromDelimiters($string, $start, $end){ - if(strpos($string, $start) !== false) { - $section_retrieved = substr($string, strpos($string, $start) + strlen($start)); - $section_retrieved = substr($section_retrieved, 0, strpos($section_retrieved, $end)); - return $section_retrieved; - } - - return false; - } - - $html = getSimpleHTMLDOM($this->getURI()) - or returnServerError('Could not request EstCeQuonMetEnProd: ' . $this->getURI()); + public function collectData() { + $html = getSimpleHTMLDOM(self::URI) + or returnServerError('Could not request EstCeQuonMetEnProd: ' . self::URI); $item = array(); $item['uri'] = $this->getURI() . '#' . date('Y-m-d'); @@ -28,8 +18,8 @@ class EstCeQuonMetEnProdBridge extends BridgeAbstract { $item['timestamp'] = strtotime('today midnight'); $item['content'] = str_replace( 'src="/', - 'src="' . $this->getURI(), - trim(extractFromDelimiters($html->outertext, '', '

')) + 'src="' . self::URI, + trim(extractFromDelimiters($html->outertext, '', '