1
0
mirror of https://github.com/RSS-Bridge/rss-bridge.git synced 2025-08-18 06:11:33 +02:00

Compare commits

..

151 Commits

Author SHA1 Message Date
Eugene Molotov
5df8bf956a Bump version to dev.2022-01-20 2022-01-20 10:17:59 +05:00
Eugene Molotov
a49767e71b [README] Update list of contributors 2022-01-20 10:17:06 +05:00
Eugene Molotov
0584fdddde README: add buttons to irc and matrix 2022-01-20 10:12:08 +05:00
Eugene Molotov
0fdd281fb2 README: take out debian and guix buttons, since they are not officially maintained 2022-01-20 09:45:53 +05:00
Eugene Molotov
0e17282f60 contrib: add somehow working emacs script to help prepare new release 2022-01-20 09:43:01 +05:00
Eugene Molotov
0f1ec4a879 contrib: save template for creating new releases 2022-01-20 09:43:01 +05:00
Eugene Molotov
e9d3d7ba67 [contrib] Place script for fetching contributors from GitHub's gist to contrib directory
No need to depend on gist.github.com
2022-01-20 09:43:01 +05:00
岳东辰
3cef35a432 [ABCNewsBridge] New bridge (#2255) 2022-01-17 10:35:01 +05:00
Eugene Molotov
2f10d2345a [TwitterBridge] Meet the new maintainer: arnd-s 2022-01-16 23:42:28 +05:00
arnd-s
02a8ae4c62 [TwitterBridge] Use Twitter API V1.1 guest/activate for requesting new guest tokens (#2414)
Instead of searching inside base html page for the guest token, this patch instead uses the Twitter API V1.1 to acquire guest tokens
2022-01-16 07:59:02 +05:00
Štěpán Škorpil
084a1bcf19 [CeskaTelevizeBridge] Follow website changes (#2420) 2022-01-16 07:54:20 +05:00
Dag
418f951dd1 [ThePirateBayBridge] Repair broken bridge
This is more like a refactor because they dont serve data in plain
html anymore. Instead, the data is available from a json api
at apibay.org

Could possibly expand this bridge because their api has more to give.

I learned about this api by grokking https://thepiratebay.org/static/main.js
and by looking at browser ajax requests.

For some unknown reason they host some static assets at
https://torrindex.net/ which is used by the bridge to render
magnet image and user status image.

Signed-off-by: Dag <me@dvikan.no>
2022-01-15 11:18:57 +05:00
Bockiii
9dcce0ba1d [EconomistBridge] Fix if no article image present (#2328) 2022-01-15 10:29:18 +05:00
Joseph
607d9297ff [DuckDuckGoBridge] Fix bridge (#2335) 2022-01-15 10:24:13 +05:00
Joseph
c65feffb61 [FirefoxAddonsBridge] Fix add-on download links (#2338) 2022-01-15 10:16:43 +05:00
MarKoeh
f259fa7f9f [ARDMediathekBridge] Switch to JSON-API (#2380)
* Switch ARDMediathekBridge to JSON-API

The html screen scraping approach of ARDMediathekBridge did not work reliably. I could not find one show for which the item list was not empty using the html screen scraping approach.

The proposed change uses the JSON-API of the WebApp. Although there is still room for improvement (feed title, better understanding of the API, more accurate mimic of the webapp's behavior, de-pagination …), it does work with this change.

Indicate that now full URLs as well as just the ID are accepted.
2022-01-10 11:47:49 +01:00
sysadminstory
368a198321 [PepperBridge] fix and discussion (#2383)
* [PepperBridge] Fix some Notice for expired items

Some expired iteams are not identical to normal items for the title and
URI, so they got a special handling to remove PHP Notice.

The "most commented" sorting option was removed and show now some forum.
So it was removed.

With DealabsBridge, MydealsBridge and HotUKDealsBridge, you can monitor
a discussion for new comments, excluding or not post without URL
2022-01-10 11:46:26 +01:00
Albirew
590fdd9f9b [HentaiHavenBridge] domain and content retrieval update (#2402) 2022-01-10 11:44:41 +01:00
jNullj
799c93a3c6 [ExplosmBridge] Rewrite to work without feedburner (#2417)
* [ExplosmBridge] Rewrite to work without feedburner

re-wrote the bridge to scrap from the new explosm site as the old method of using feedburner is not working anymore, feedburner is stuck on dec/22 when the explosm site changed.
2022-01-10 11:44:18 +01:00
Quentin de Longraye
2f957b6870 [CI] Tag docker images with the commit sha (#2418)
We do not release stable tags often. To avoid using `latest`, for instance in Kubernetes manifests, this change allows to reference a repository commit. This way it is easy to lock image to a specific commit, and try and rollback if a change to a newer commit brings regressions.
2022-01-09 14:09:28 +05:00
arnd-s
12ff697ab0 [TwitterBridge] Continue using existing guesttoken from cache, when requesting a new one failed (#2396) 2022-01-06 10:59:37 +05:00
somini
8530aa54f2 [ComboiosDePortugalBridge] Temporarily ignore certificate checks (#2403) 2022-01-05 04:44:59 +05:00
Eugene Molotov
d0ef8aa71d [CI] Add support for php 8.0 and 8.1 (#2405) 2022-01-05 04:40:30 +05:00
Eugene Molotov
59e77a9e51 [README] Clarify problems with InstagramBridge and FacebookBridge (#2401) 2022-01-03 11:15:45 +05:00
Eugene Molotov
37cb4091d4 bridges: remove redundant "or returnServerError" after getContents/getSimpleHTMLDom/getSimpleHTMLDomCached (#2398)
When fetching website contents, exceptions already raise on fetching error
2022-01-02 14:36:09 +05:00
Christian Schabesberger
fc51c6753d [NordbayernBridge] Fix banner images (#2384) 2022-01-02 03:51:59 +05:00
sysadminstory
71cd15c35d [AutoJMBridge] Rework of the script to handle the new website (#2390)
The brand - model page does not have filters now, so the actual feed
using this bridge are broken.

Website has changed. The new website offers a new search function with
many more filter. So I switched to this new search page for this bridge.
2022-01-02 02:15:13 +05:00
Eugene Molotov
df408fb8bc [CI] Temporarly disable phpunit7 until it starts working again (#2392) 2021-12-26 09:25:36 +05:00
Shikiryu
e545f43a67 [KhinsiderBridge] Add bridge (#2302) 2021-12-20 12:36:44 +05:00
dag
ec55e99934 [HackerNewsUserThreadsBridge] Repair broken bridge (#2344) 2021-12-19 14:18:57 +05:00
dag
67e33186ce [GiphyBridge] Repair broken bridge (#2347) 2021-12-18 15:26:51 +05:00
sysadminstory
a0bbbd6978 [RadioMelodieBridge] Fix to use the new website layout (#2330) 2021-12-18 15:19:58 +05:00
Joseph
2ee091665a [ASRockNewsBridge] Fix bridge (#2373) 2021-12-18 15:17:16 +05:00
triatic
35930ee4e4 [TwitterBridge] Increase guest token expiry time (#2374) 2021-12-18 14:54:18 +05:00
Aaron F
e1290aa42c [CVEDetailsBridge] Add bridge (#2332)
CVE Details is a collection of CVEs, taken from the National Vulnerability
Database (NVD) and other sources like the Exploit DB and Metasploit. The
website categorizes it by vendor and product and attach the CWE category.
There is an Atom feed available, but only logged in users can use it,
it is not reliable and contain no useful information. This bridge create a
sane feed with additional information like tags and a link to the CWE
a description of the vulnerability.
2021-12-18 09:44:05 +05:00
dag
814711e3af [ScmbBridge] Remove "read more" text only if it exists (#2368) 2021-12-16 10:06:52 +05:00
Eugene Molotov
28db707587 [OpenClassroomsBridge] sebsauvage does not maintain this bridge
Reference: https://github.com/RSS-Bridge/rss-bridge/issues/2333#issuecomment-989611085
2021-12-09 15:26:31 +05:00
dag
b48739d0ba [DerpibooruBridge] Fix parsing of title (#2346)
The previous value was an int and was not accepted
by rss-bridge as a title. This change uses the image
name instead which fixes the problem and is a better
title than the image id.
2021-12-08 17:17:28 +01:00
csisoap
b9d92150e1 [ReutersBridge] Migrate to new API (#2348)
* [ReutersBridge] Migrate to new API

- Add new API, feeds.
- Old feed name are perserved for backward compatibility.
- Remove 'Special Report' feed.
- Some feed continue to use old Wire API due to not available in new one.
- Add some new type of content, replace iframe with blockquote for twitter.
2021-12-08 17:16:40 +01:00
Bockiii
b395fe2641 [core] Implemented feature to read config from environment variables (#2100) 2021-12-03 11:15:08 +05:00
Eugene Molotov
4bc534c80f [FacebookBridge] teromene and logmanoriginal do not maintain this bridge defacto 2021-12-03 05:31:46 +05:00
Eugene Molotov
071fdef599 [core] Drop php 5.6 and php 7.0 support (#2224) 2021-12-03 04:12:16 +05:00
erik
ae6a3227b0 [FlashbackBridge] Add new bridge (#2343) 2021-12-03 03:57:21 +05:00
benzel
f469489b56 [AppleAppStoreBridge] Add Germany (#2350) 2021-12-03 03:56:06 +05:00
dag
490f556783 [TheCodingLoveBridge] Remove redundant bridge (#2342)
This is now a wordpress blog and their official feed is at https://thecodinglove.com/feed
2021-11-29 11:26:13 +05:00
somini
192f0278d2 [PanacheDigitalGamesBridge] Add bridge (#2321) 2021-11-06 23:58:30 +05:00
sysadminstory
2b634002f2 [DealabsBridge - HotUKDealsBridge - MydealsBridge] Set the Feed URL according to the parameters (#2320) 2021-11-06 23:43:44 +05:00
Christian Schabesberger
42379071e9 [NordbayernBridge] Fix banner URL (#2326)
* make banner images show for nordbayern again
* make author portrait not apear as article banner for nordbayern
2021-11-06 23:28:12 +05:00
Joseph
fd54042ef3 [BandcampBridge] Add support for labels (#2286) 2021-10-30 01:43:20 +05:00
Martin Leyrer
3764348b76 [core] Accept additional "successful" 2xx status codes (#2310) 2021-10-30 01:38:25 +05:00
dotter-ak
0ba0e2de4e [UrlebirdBridge] Fix for non ASCII characters in post URI (#2312) 2021-10-30 01:36:09 +05:00
Florent Machen
4187d8f4cf [GitHubGistBridge] fix use the css selector "contains" to find a class in the middle of the utility classes (#2306) 2021-10-30 01:32:31 +05:00
ORelio
1c6532a9d0 [NextgovBridge] Update categories, fix missing element (#2316) 2021-10-30 01:28:02 +05:00
ORelio
547829f971 [FuturaSciences] Improve content extraction (#2317)
- Fix tracking removal in URL
- Fix images broken due to new lazy loading mechanism
- Remove headline, articles do not have it anymore
- Improve article cleanup
2021-10-30 01:24:19 +05:00
ORelio
970bdd45f9 [DarkReadingBridge] Fix content extraction (#2315)
Also:
- Add article limit (main feed was broken due to too many articles)
- Add support for article thumbnail
2021-10-30 01:21:07 +05:00
Eugene Molotov
b86ed70376 [core] Backported str_starts_with, str_ends_with and str_contains from php 8 (#2318) 2021-10-30 01:06:04 +05:00
Eugene Molotov
9254d14f50 [VkBridge] Multiple fixes:
- Correct video title
- Do not add repost of deleted post to feed
2021-10-25 10:16:24 +05:00
Matt DeMoss
8f98e07979 [PcGamerBridge] Use meta tags to generate feed contents (#2271) 2021-10-19 11:53:26 +05:00
Bockiii
8d0fc54e4d [FSecureBlogBridge] Limit number of returned items (#2300) 2021-10-19 08:23:38 +05:00
csisoap
bdf15c3ce0 [UnogsBridge] Add fallback if not found any high-res image (#2301) 2021-10-19 08:22:17 +05:00
sal0max
927b08ed00 [LegoIdeasBridge] Add bridge (#2284)
* [LegoIdeasBridge] Add bridge
2021-10-14 22:52:42 +02:00
Niehztog
87b3aaa550 [XPathAbstract] Fix encoding of feed output (#2297) 2021-10-14 14:18:00 +05:00
Yaman Qalieh
c445ba6ebb [MozillaBugTrackerBridge] Fix incorrect newlines in feed title (#2298) 2021-10-14 14:13:18 +05:00
Bockiii
793c55f43d [Docker] Allow to define port via HTTP_PORT (#2285) 2021-10-09 22:02:38 +05:00
Bockiii
11be390e65 [MozillaSecurityBridge] Limit items to 20 (#2287) 2021-10-09 16:03:12 +05:00
somini
ba5baaf7c9 [JornalDeNoticiasBridge] Add bridge (#2293) 2021-10-07 16:15:21 +05:00
sysadminstory
f0ddd686e3 [ExtremeDownloadBridge] Update URL (#2290) 2021-10-05 12:52:29 +05:00
sysadminstory
f6b9864bdd [ZoneTelechargementBridge] Update website URL (#2289) 2021-10-05 12:51:26 +05:00
KamaleiZestri
64b7c54bc8 [BakaUpdatesMangaReleasesBridge] Added option to display releases based on a user list (#2276)
* [BakaUpdatesMangaReleasesBridge] Added option to display releases based on a user list
2021-10-03 22:35:31 +02:00
KamaleiZestri
d5a010adcd [PillowfortBridge] Added new bridge (#2275)
* [PillowfortBridge] Added new bridge
2021-10-03 21:59:33 +02:00
Bockiii
2a609b39bd [LinkedInBridge] Remove bridge (#2269) 2021-10-03 21:00:24 +05:00
Bockiii
dacc586dca New docker build mechanism (#2268)
* New docker build mechanism
2021-10-03 16:06:30 +02:00
csisoap
8bcf4ebfbf [NationalGeographicBridge] Rewrite bridge (#2177)
- All the option will be preserved.
- Add timestamp, author's name included with full article.
2021-10-01 18:39:36 +05:00
csisoap
cb111a3ebd [UnogsBridge] Better feed title for Country context (#2279) 2021-09-28 14:20:04 +05:00
Timendum
42e40e2823 [PicukiBridge] Fix image URLs in content (#2282)
URLs in Picuki are already absolute
2021-09-28 14:15:41 +05:00
Bockiii
1ddce120ae [EconomistBridge] Full rework (#2272) 2021-09-26 16:25:19 +05:00
Joseph
927cb17dbf [SoundcloudBridge] Add support for albums, reposts & likes (#2236) 2021-09-20 17:53:41 +05:00
D5k H3h
ccb2e64fd0 [WallpaperflareBridge] Add bridge (#2179) 2021-09-11 13:27:17 +05:00
csisoap
a26408594b [YoutubeBridge] New features, fixes and refactors (#2208)
New features:
- Add support for custom channel name.

Fixes:
- In playlist mode last uploaded videos were not in feed
- Search mode returned empty feed

And a lot of refactoring
2021-09-11 13:20:14 +05:00
Bockiii
324932642d [PokemonTVBridge] Add bridge (#2219) 2021-09-05 12:11:36 +05:00
Bockiii
455b5e09a1 [Dockerfile] Add custom config location (#2098) 2021-09-05 08:10:54 +05:00
Bockiii
bcc15228d8 [RedditBridge] Return back NSFW posts to feed (#2257)
As it was working before applying https://github.com/RSS-Bridge/rss-bridge/pull/2229
2021-09-04 18:00:02 +05:00
Eugene Molotov
68d9e2ff24 [NineGagBridge] Remove whitespace on wtf section
Don't know why github actions did not run on https://github.com/RSS-Bridge/rss-bridge/pull/2094
Certainly wtf
2021-08-25 18:16:00 +05:00
Bockiii
a5d33615f5 [RedditBridge] Add keyword search function (#2229) 2021-08-25 18:09:36 +05:00
Christian Schabesberger
8f634eb4a1 [NordBayernBridge] Fix election articles (#2253) 2021-08-25 18:08:23 +05:00
Bockiii
677e4974d1 [NineGagBridge] Updated sections and added video 2021-08-25 18:06:10 +05:00
Joseph
10c5259493 [OpenlyBridge] Add bridge (#2129) 2021-08-15 23:42:28 +05:00
Joseph
cb3c055df9 [InternetArchiveBridge] Add detectParameters (#2142) 2021-08-15 23:36:38 +05:00
Christian Schabesberger
036a3ad245 [NordbayernBridge] Add city of Erlangen (#2248) 2021-08-15 18:57:40 +05:00
Joseph
69ce8106ce [BingSearchBridge] Remove bridge (#2242)
Microsoft has removed the bing search discover functionality.

https://www.bing.com/discover/ pages now redirect to https://www.bing.com/images/trending
2021-08-13 08:54:37 +05:00
ORelio
8a30480a45 [Releases3DSBridge] Remove requests to IGN (#2246)
This part of the bridge was meant to find game info on IGN but rarely found useful results, and is harder to maintain than the rest of this bridge due to changes at IGN.
2021-08-13 08:52:57 +05:00
Joseph
f36832b66e [CodebergBridge] Add bridge (#1951) 2021-08-13 08:51:50 +05:00
t0stiman
f3f934ed8b [HardwareInfoBridge] Add bridge (#2232) 2021-08-10 23:00:32 +05:00
Joseph
7c46c64242 [FierPandaBridge] Remove bridge (#2238) 2021-08-10 22:55:24 +05:00
ORelio
bf1773ed8b [GBAtemp] Fix news extraction (#2241) 2021-08-10 22:49:10 +05:00
Joseph
4529e3699a [BridgeImplementationTest] Allow multiple contexts to have an empty parameters array (#1954) 2021-08-10 22:37:41 +05:00
sysadminstory
f5f0b77805 [RadioMelodieBridge] Replace JS Audio Player (#2233)
The Javascript Audio Player is replaced by the plain <audio> HTML tag
2021-08-04 09:04:45 +05:00
t0stiman
3637777070 [style] add dark mode (#2029) 2021-08-01 16:31:58 +05:00
Tobias Alexander Franke
c673917aca [BinanceBridge] Fix blog posts (#2226) 2021-07-29 21:55:36 +05:00
Eugene Molotov
877707f7b2 [contrib] Add directory for unorganized bin of various useful things contributed by the community around RSS-Bridge
Idea came after reading https://drewdevault.com/2020/06/06/Add-a-contrib-directory.html
2021-07-28 00:51:34 +05:00
sysadminstory
2689f5f7fa [DealabsBridge - HotUKDealsBridge - MydealsBridge] Update groups (#2083)
The bridges has been updated with the newest "groups" available on every website
2021-07-28 00:46:01 +05:00
Bockiii
bf1cb8fadc [IndiegogoBridge] New bridge (#2135) 2021-07-28 00:41:56 +05:00
Eugene Molotov
716f5ddc0e [PikabuBridge] Do not strip strikethrough tags in body content 2021-07-24 00:39:00 +05:00
Eugene Molotov
0ee549f468 [PikabuBridge] Remove whitespace from fake news marker 2021-07-24 00:39:00 +05:00
Eugene Molotov
4a1e26fd07 [WordPressBridge] aledeg is not maintainer of this bridge 2021-07-24 00:37:57 +05:00
sal0max
e14f647075 [SpottschauBridge] New bridge (#2193) 2021-07-20 13:16:43 +05:00
Marcus
34489431b4 [PicukiBridge] New bridge. Alternative to InstagramBridge (#2183) 2021-07-20 13:06:56 +05:00
dotter-ak
126cf1a7fa [Drive2ruBridge] Fixed incorrect titles and URLs in logbooks (#2215) 2021-07-20 13:03:17 +05:00
岳东辰
3050f0ae70 [ARDMediathekBridge] New bridge (#2158) 2021-07-20 12:58:50 +05:00
csisoap
cabf7a748a [ReutersBridge] Change timestamp, add new feed, add alt text to image (#2150) 2021-07-20 12:54:07 +05:00
dotter-ak
84450e7e8d [Drive2ruBridge] Bugfix (#2211) 2021-07-13 10:20:46 +05:00
Joseph
971cd9ba39 [FirefoxAddonsBridge] Remove duplicate code (#2209) 2021-07-12 22:56:51 +05:00
Nemo
9fa782105d [AmazonPriceTrackerBridge] Fixes for subscription items (#2205) 2021-07-12 22:49:29 +05:00
dotter-ak
cb7f5b057f [Drive2ruBridge] Add news, personal blogs and featured topics (#2156) 2021-07-09 00:00:16 +05:00
csisoap
a8d1acfdad [UnogsBridge] New bridge (#2198) 2021-07-07 15:25:13 +05:00
sal0max
b5d9742a21 [NikonDownloadCenterBridge] Add bridge (#2195) 2021-07-06 18:00:19 +05:00
sal0max
7dd1a7dccc [AmazonPriceTrackerBridge] Fix bridge (#2194) 2021-07-06 01:26:08 +05:00
csisoap
1f6ad000ce [RedditBridge] Add option to choose for New, Hot and Top submissions (#2189) 2021-07-02 00:41:56 +05:00
Christian Schabesberger
398e175fe0 [NordbayernBridge] Follow site updates (#2169) 2021-07-01 07:21:58 +05:00
Corentin Garcia
0de2db853f [NYTBridge] Fix article parsing (#2106)
Co-authored-by: podiki <podiki@users.noreply.github.com>
2021-06-30 15:14:25 +05:00
sysadminstory
9399ebb2c6 [RadioMelodieBridge] Add timestamp support and content fix (#2105) 2021-06-26 01:12:33 +05:00
dotter-ak
606972dd3c [UrlebirdBridge] Add bridge (#2163) 2021-06-26 00:55:13 +05:00
Bockiii
ecaae735d9 [core] Support for bridge maintainers' donation URLs (#2102) 2021-06-26 00:45:25 +05:00
岳东辰
5598fef3cf [WikipediaBridge] Update elements (#2167) 2021-06-20 15:23:29 +05:00
triatic
9c99a1a9c1 [FacebookBridge] Increase cache timeout (#2149)
Facebook aggressively throttles queries now
2021-06-12 23:15:56 +05:00
ORelio
75cc52a62c [FilterBridge] Various improvements (#2148)
- Add option for case-insensitive regex
- Allow matching item content or author in addition to item title
- Optionally attempt to convert encoding when applying matches
2021-06-07 23:11:12 +05:00
Eugene Molotov
973e49d93e [TelegramBridge] Add test cases for detectParameters 2021-06-05 09:34:16 +05:00
Eugene Molotov
c580219627 [BridgeImplementationTest] Implement feature for testing detectParameters method 2021-06-05 09:34:16 +05:00
Joseph
a18af3952d [KATBridge] Remove bridge (#2141) 2021-06-03 00:16:12 +05:00
Joseph
15907e2bbd [StoriesIGBridge] Remove bridge (#2139)
Website has been unavailable since at least October 2020

https://web.archive.org/web/20201016100555/https://storiesig.com/
2021-06-01 10:20:11 +05:00
Joseph
459adf4790 [ExplosmBridge] Fix website URI (#2140) 2021-06-01 10:18:14 +05:00
AxorPL
d38bc18232 [Formula1Bridge] Minor fixes (#2128)
- removed useless returnServerError according to your recommendation
- made use of the caption property if present
- fixed link URL
2021-05-30 23:33:15 +05:00
Jacques Heunis
eec1163fb9 [ItchioBridge] Remove reliance on in-page timestamps (#2127)
This significantly increases the possibility of missing updates (if
files are uploaded but no file names or post contents are changed) and
of showing an update when there is none (if the post text is changed
but no new files are uploaded). However with the on-page timestamps
removed I'm not sure if there is a good way to do this more accurately
so this is good as we can do at the moment.
2021-05-30 23:12:19 +05:00
Bockiii
b074abcc0d [AppleMusicBridge] Complete rebuild for new site (#2134) 2021-05-30 23:08:39 +05:00
Joseph
2ae9793f2c [CI] Update ubuntu version (16.04 => 20.04) (#2136)
Support for Ubuntu 16.04 (Xenial Xerus) runners is deprecated and will be removed on August 1, 2021
https://github.com/shivammathur/setup-php/issues/452
2021-05-30 23:04:57 +05:00
Joseph
f02d80e141 [FirefoxAddonsBridge] Fix download link extraction (#2120) 2021-05-25 00:46:07 +05:00
Yaman Qalieh
44e01a4282 [PixivBridge] Rewrite Bridge (#2111)
Also added options:
- Search for Illustrations, Manga or Novels
- Custom Post Limit
- Choose between thumbnails and full-sized image
2021-05-25 00:42:39 +05:00
Eugene Molotov
63d257d9d0 [PikabuBridge] Cut "script" element from post body (#2125)
Also correct description and remove useless returnServerError call
2021-05-24 00:50:24 +05:00
dotter-ak
37cd071453 [Drive2ruBridge] Add bridge (#2116) 2021-05-24 00:33:46 +05:00
Christian Schabesberger
17f9c44bfc [NordbayernBridge] Fix jpeg regex (#2118) 2021-05-19 13:44:17 +05:00
Christian Schabesberger
28aaf59007 [NordbayernBridge] Exclude slideshows when handling articles (#2117) 2021-05-19 13:43:10 +05:00
Corentin Garcia
e8d241e8c9 [CourrierInternationalBridge] Switch to FeedExpander (#2107) 2021-05-17 23:36:11 +05:00
t0stiman
2b793f04de [RaceDepartmentBridge] Follow site changes (#2087) 2021-05-17 23:18:51 +05:00
Eugene Molotov
655e02e3fe [VMwareSecurityBridge] Remove bridge
There is fulltext RSS feed: https://www.vmware.com/security/advisories.xml
2021-05-17 12:01:23 +05:00
Eugene Molotov
a4bd04310f [YoutubeBridge] Several fixes and switch maintainer (#2115)
- Fix incorrectly working method for querying video info
- Partially fix playlist mode.
At least it works, if playlist has more than 15 videos. But maximum 100 video items are parsed from playlist.

Reason of switching maintainer: https://github.com/RSS-Bridge/rss-bridge/issues/2113#issuecomment-841156902
2021-05-17 01:02:45 +05:00
Bockiii
e48617530d [ExplosmBridge] Add bridge (#2092) 2021-05-13 23:35:42 +05:00
Bockiii
3585575d68 [Docker] Add support for arm32/64 (#2104) 2021-05-13 22:57:10 +05:00
somini
e79a02ac2e [PresidenciaPTBridge]: Support multiple sections (#2082) 2021-05-10 00:22:10 +05:00
Bockiii
63ebf5ceec [BridgeCard] Make HTML default, remove other buttons (#2101) 2021-05-08 15:10:16 +05:00
AxorPL
378f78d6eb [Formula1Bridge] New bridge (#2085) 2021-05-06 23:16:22 +05:00
marius851000
d7ba7782f3 [DerpibooruBridge] Make it work again (#2079) 2021-04-26 23:07:42 +05:00
279 changed files with 11748 additions and 2972 deletions

61
.github/workflows/dockerbuild.yml vendored Normal file
View File

@@ -0,0 +1,61 @@
name: Build Image on Commit and Release
on:
push:
branches:
- 'master'
tags:
- '20*'
env:
DOCKERHUB_SLUG: rssbridge/rss-bridge
GHCR_SLUG: ghcr.io/rss-bridge/rss-bridge
jobs:
bake:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v2.3.4
-
name: Docker meta
id: docker_meta
uses: docker/metadata-action@v3.5.0
with:
images: |
${{ env.DOCKERHUB_SLUG }}
${{ env.GHCR_SLUG }}
tags: |
type=raw,value=latest
type=sha
type=ref,event=tag,enable=${{ startsWith(github.ref, 'refs/tags/20') }}
type=raw,value=stable,enable=${{ startsWith(github.ref, 'refs/tags/20') }}
-
name: Set up QEMU
uses: docker/setup-qemu-action@v1
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1.6.0
-
name: Login to DockerHub
uses: docker/login-action@v1.10.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
-
name: Login to GitHub Container Registry
uses: docker/login-action@v1.10.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
-
name: Build and push
uses: docker/bake-action@v1.6.0
with:
files: |
./docker-bake.hcl
${{ steps.docker_meta.outputs.bake-file }}
targets: image-all
push: true

View File

@@ -8,10 +8,10 @@ on:
jobs:
phpcs:
runs-on: ubuntu-16.04
runs-on: ubuntu-20.04
strategy:
matrix:
php-versions: ['5.6', '7.0', '7.1', '7.2', '7.3', '7.4']
php-versions: ['7.1', '7.2', '7.3', '7.4']
steps:
- uses: actions/checkout@v2
- uses: shivammathur/setup-php@v2
@@ -21,10 +21,10 @@ jobs:
- run: phpcs . --standard=phpcs.xml --warning-severity=0 --extensions=php -p
phpcompatibility:
runs-on: ubuntu-16.04
runs-on: ubuntu-20.04
strategy:
matrix:
php-versions: ['5.6', '7.4']
php-versions: ['7.1', '7.4']
steps:
- uses: actions/checkout@v2
- uses: shivammathur/setup-php@v2

View File

@@ -7,37 +7,25 @@ on:
branches: [ master ]
jobs:
phpunit6:
runs-on: ubuntu-16.04
strategy:
matrix:
php-versions: ['7.0', '7.1', '7.2']
steps:
- uses: actions/checkout@v2
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
- run: composer global require phpunit/phpunit ^6
- run: phpunit --configuration=phpunit.xml --include-path=lib/
phpunit7:
runs-on: ubuntu-16.04
strategy:
matrix:
php-versions: ['7.1', '7.2', '7.3']
steps:
- uses: actions/checkout@v2
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
- run: composer global require phpunit/phpunit ^7
- run: phpunit --configuration=phpunit.xml --include-path=lib/
# TODO: return back when fixed https://github.com/RSS-Bridge/rss-bridge/issues/2391
# phpunit7:
# runs-on: ubuntu-20.04
# strategy:
# matrix:
# php-versions: ['7.1', '7.2', '7.3']
# steps:
# - uses: actions/checkout@v2
# - uses: shivammathur/setup-php@v2
# with:
# php-version: ${{ matrix.php-versions }}
# - run: composer global require phpunit/phpunit ^7
# - run: phpunit --configuration=phpunit.xml --include-path=lib/
phpunit8:
runs-on: ubuntu-16.04
runs-on: ubuntu-20.04
strategy:
matrix:
php-versions: ['7.3', '7.4']
php-versions: ['7.3', '7.4', '8.0', '8.1']
steps:
- uses: actions/checkout@v2
- uses: shivammathur/setup-php@v2

View File

@@ -1,5 +1,9 @@
FROM php:7-apache
LABEL description="RSS-Bridge is a PHP project capable of generating RSS and Atom feeds for websites that don't have one."
LABEL repository="https://github.com/RSS-Bridge/rss-bridge"
LABEL website="https://github.com/RSS-Bridge/rss-bridge"
ENV APACHE_DOCUMENT_ROOT=/app
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" \
@@ -15,4 +19,6 @@ RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" \
&& sed -ri -e 's/(MinProtocol\s*=\s*)TLSv1\.2/\1None/' /etc/ssl/openssl.cnf \
&& sed -ri -e 's/(CipherString\s*=\s*DEFAULT)@SECLEVEL=2/\1/' /etc/ssl/openssl.cnf
COPY --chown=www-data:www-data ./ /app/
COPY --chown=www-data:www-data ./ /app/
CMD ["/app/docker-entrypoint.sh"]

View File

@@ -1,6 +1,11 @@
![RSS-Bridge](static/logo_600px.png)
===
[![LICENSE](https://img.shields.io/badge/license-UNLICENSE-blue.svg)](UNLICENSE) [![GitHub release](https://img.shields.io/github/release/rss-bridge/rss-bridge.svg?logo=github)](https://github.com/rss-bridge/rss-bridge/releases/latest) [![Debian Release](https://img.shields.io/badge/dynamic/json.svg?logo=debian&label=debian%20release&url=https%3A%2F%2Fsources.debian.org%2Fapi%2Fsrc%2Frss-bridge%2F&query=%24.versions%5B0%5D.version&colorB=blue)](https://tracker.debian.org/pkg/rss-bridge) [![Guix Release](https://img.shields.io/badge/guix%20release-unknown-blue.svg)](https://www.gnu.org/software/guix/packages/R/) [![Actions Status](https://img.shields.io/github/workflow/status/RSS-Bridge/rss-bridge/Tests/master?label=GitHub%20Actions&logo=github)](https://github.com/RSS-Bridge/rss-bridge/actions) [![Docker Build Status](https://img.shields.io/docker/cloud/build/rssbridge/rss-bridge?logo=docker)](https://hub.docker.com/r/rssbridge/rss-bridge/)
[![LICENSE](https://img.shields.io/badge/license-UNLICENSE-blue.svg)](UNLICENSE)
[![GitHub release](https://img.shields.io/github/release/rss-bridge/rss-bridge.svg?logo=github)](https://github.com/rss-bridge/rss-bridge/releases/latest)
[![irc.libera.chat](https://img.shields.io/badge/irc.libera.chat-%23rssbridge-blue.svg)](https://web.libera.chat/#rssbridge)
[![Chat on Matrix](https://matrix.to/img/matrix-badge.svg)](https://matrix.to/#/#rssbridge:libera.chat)
[![Actions Status](https://img.shields.io/github/workflow/status/RSS-Bridge/rss-bridge/Tests/master?label=GitHub%20Actions&logo=github)](https://github.com/RSS-Bridge/rss-bridge/actions)
[![Docker Build Status](https://img.shields.io/docker/cloud/build/rssbridge/rss-bridge?logo=docker)](https://hub.docker.com/r/rssbridge/rss-bridge/)
RSS-Bridge is a PHP project capable of generating RSS and Atom feeds for websites that don't have one. It can be used on webservers or as a stand-alone application in CLI mode.
@@ -13,11 +18,11 @@ Supported sites/pages (examples)
* `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/)
* `Facebook` : Returns the latest posts on a page or profile on [Facebook](https://facebook.com/)
* `Facebook` : Returns the latest posts on a page or profile on [Facebook](https://facebook.com/) (There is an [issue](https://github.com/RSS-Bridge/rss-bridge/issues/2047) for public instances)
* `FlickrExplore` : [Latest interesting images](http://www.flickr.com/explore) from Flickr
* `GoogleSearch` : Most recent results from Google Search
* `Identi.ca` : Identica user timeline (Should be compatible with other Pump.io instances)
* `Instagram`: Most recent photos from an Instagram user
* `Instagram`: Most recent photos from an Instagram user (There is an [issue](https://github.com/RSS-Bridge/rss-bridge/issues/1891) for public instances)
* `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/)
@@ -57,7 +62,7 @@ RSS-Bridge hashtag (#rss-bridge) search on Twitter, in Atom format (as displayed
Requirements
===
RSS-Bridge requires PHP 5.6 or higher with following extensions enabled:
RSS-Bridge requires PHP 7.1 or higher with following extensions enabled:
- [`openssl`](https://secure.php.net/manual/en/book.openssl.php)
- [`libxml`](https://secure.php.net/manual/en/book.libxml.php)
@@ -108,7 +113,7 @@ We are RSS-Bridge community, a group of developers continuing the project initia
**Contributors** (sorted alphabetically):
<!--
Use this script to generate the list automatically (using the GitHub API):
https://gist.github.com/LogMANOriginal/da00cd1e5f0ca31cef8e193509b17fd8
./contrib/prepare_release/fetch_contributors.php
-->
* [16mhz](https://github.com/16mhz)
@@ -131,6 +136,7 @@ https://gist.github.com/LogMANOriginal/da00cd1e5f0ca31cef8e193509b17fd8
* [b1nj](https://github.com/b1nj)
* [benasse](https://github.com/benasse)
* [Binnette](https://github.com/Binnette)
* [Bockiii](https://github.com/Bockiii)
* [captn3m0](https://github.com/captn3m0)
* [chemel](https://github.com/chemel)
* [Chouchen](https://github.com/Chouchen)
@@ -140,11 +146,12 @@ https://gist.github.com/LogMANOriginal/da00cd1e5f0ca31cef8e193509b17fd8
* [corenting](https://github.com/corenting)
* [couraudt](https://github.com/couraudt)
* [csisoap](https://github.com/csisoap)
* [cyberjacob](https://github.com/cyberjacob)
* [da2x](https://github.com/da2x)
* [dabenzel](https://github.com/dabenzel)
* [Daiyousei](https://github.com/Daiyousei)
* [dawidsowa](https://github.com/dawidsowa)
* [DevonHess](https://github.com/DevonHess)
* [dhuschde](https://github.com/dhuschde)
* [disk0x](https://github.com/disk0x)
* [DJCrashdummy](https://github.com/DJCrashdummy)
* [Djuuu](https://github.com/Djuuu)
@@ -154,17 +161,22 @@ https://gist.github.com/LogMANOriginal/da00cd1e5f0ca31cef8e193509b17fd8
* [Dreckiger-Dan](https://github.com/Dreckiger-Dan)
* [drego85](https://github.com/drego85)
* [drklee3](https://github.com/drklee3)
* [dvikan](https://github.com/dvikan)
* [em92](https://github.com/em92)
* [eMerzh](https://github.com/eMerzh)
* [EtienneM](https://github.com/EtienneM)
* [f0086](https://github.com/f0086)
* [fanch317](https://github.com/fanch317)
* [fatuuse](https://github.com/fatuuse)
* [fivefilters](https://github.com/fivefilters)
* [floviolleau](https://github.com/floviolleau)
* [fluffy-critter](https://github.com/fluffy-critter)
* [fmachen](https://github.com/fmachen)
* [Frenzie](https://github.com/Frenzie)
* [fulmeek](https://github.com/fulmeek)
* [ggiessen](https://github.com/ggiessen)
* [Ginko-Aloe](https://github.com/Ginko-Aloe)
* [girlpunk](https://github.com/girlpunk)
* [Glandos](https://github.com/Glandos)
* [gloony](https://github.com/gloony)
* [GregThib](https://github.com/GregThib)
@@ -186,11 +198,12 @@ https://gist.github.com/LogMANOriginal/da00cd1e5f0ca31cef8e193509b17fd8
* [jdigilio](https://github.com/jdigilio)
* [JeremyRand](https://github.com/JeremyRand)
* [JimDog546](https://github.com/JimDog546)
* [jNullj](https://github.com/jNullj)
* [Jocker666z](https://github.com/Jocker666z)
* [johnnygroovy](https://github.com/johnnygroovy)
* [johnpc](https://github.com/johnpc)
* [joni1993](https://github.com/joni1993)
* [joshcoales](https://github.com/joshcoales)
* [KamaleiZestri](https://github.com/KamaleiZestri)
* [klimplant](https://github.com/klimplant)
* [kolarcz](https://github.com/kolarcz)
* [kranack](https://github.com/kranack)
@@ -201,12 +214,16 @@ https://gist.github.com/LogMANOriginal/da00cd1e5f0ca31cef8e193509b17fd8
* [lalannev](https://github.com/lalannev)
* [ldidry](https://github.com/ldidry)
* [Leomaradan](https://github.com/Leomaradan)
* [leyrer](https://github.com/leyrer)
* [liamka](https://github.com/liamka)
* [Limero](https://github.com/Limero)
* [LogMANOriginal](https://github.com/LogMANOriginal)
* [lorenzos](https://github.com/lorenzos)
* [lukasklinger](https://github.com/lukasklinger)
* [m0zes](https://github.com/m0zes)
* [Mar-Koeh](https://github.com/Mar-Koeh)
* [marcus-at-localhost](https://github.com/marcus-at-localhost)
* [marius851000](https://github.com/marius851000)
* [matthewseal](https://github.com/matthewseal)
* [mcbyte-it](https://github.com/mcbyte-it)
* [mdemoss](https://github.com/mdemoss)
@@ -225,8 +242,7 @@ https://gist.github.com/LogMANOriginal/da00cd1e5f0ca31cef8e193509b17fd8
* [niawag](https://github.com/niawag)
* [Niehztog](https://github.com/Niehztog)
* [Nono-m0le](https://github.com/Nono-m0le)
* [ObsidianWitch](https://github.com/ObsidianWitch)
* [OliverParoczai](https://github.com/OliverParoczai)
* [obsiwitch](https://github.com/obsiwitch)
* [Ololbu](https://github.com/Ololbu)
* [ORelio](https://github.com/ORelio)
* [otakuf](https://github.com/otakuf)
@@ -252,13 +268,16 @@ https://gist.github.com/LogMANOriginal/da00cd1e5f0ca31cef8e193509b17fd8
* [Roliga](https://github.com/Roliga)
* [ronansalmon](https://github.com/ronansalmon)
* [rremizov](https://github.com/rremizov)
* [sal0max](https://github.com/sal0max)
* [sebsauvage](https://github.com/sebsauvage)
* [shutosg](https://github.com/shutosg)
* [simon816](https://github.com/simon816)
* [Simounet](https://github.com/Simounet)
* [somini](https://github.com/somini)
* [SpangleLabs](https://github.com/SpangleLabs)
* [squeek502](https://github.com/squeek502)
* [stjohnjohnson](https://github.com/stjohnjohnson)
* [Stopka](https://github.com/Stopka)
* [Strubbl](https://github.com/Strubbl)
* [sublimz](https://github.com/sublimz)
* [sunchaserinfo](https://github.com/sunchaserinfo)
@@ -273,6 +292,7 @@ https://gist.github.com/LogMANOriginal/da00cd1e5f0ca31cef8e193509b17fd8
* [TheRadialActive](https://github.com/TheRadialActive)
* [theScrabi](https://github.com/theScrabi)
* [thezeroalpha](https://github.com/thezeroalpha)
* [timendum](https://github.com/timendum)
* [TitiTestScalingo](https://github.com/TitiTestScalingo)
* [triatic](https://github.com/triatic)
* [VerifiedJoseph](https://github.com/VerifiedJoseph)
@@ -282,6 +302,7 @@ https://gist.github.com/LogMANOriginal/da00cd1e5f0ca31cef8e193509b17fd8
* [yamanq](https://github.com/yamanq)
* [yardenac](https://github.com/yardenac)
* [ymeister](https://github.com/ymeister)
* [yue-dongchen](https://github.com/yue-dongchen)
* [ZeNairolf](https://github.com/ZeNairolf)
Licenses

View File

@@ -151,6 +151,7 @@ class DisplayAction extends ActionAbstract {
$infos = array(
'name' => $bridge->getName(),
'uri' => $bridge->getURI(),
'donationUri' => $bridge->getDonationURI(),
'icon' => $bridge->getIcon()
);
} catch(Error $e) {

View File

@@ -39,6 +39,7 @@ class ListAction extends ActionAbstract {
$list->bridges[$bridgeName] = array(
'status' => $status,
'uri' => $bridge->getURI(),
'donationUri' => $bridge->getDonationURI(),
'name' => $bridge->getName(),
'icon' => $bridge->getIcon(),
'parameters' => $bridge->getParameters(),

45
bridges/ABCNewsBridge.php Normal file
View File

@@ -0,0 +1,45 @@
<?php
class ABCNewsBridge extends BridgeAbstract {
const NAME = 'ABC News Bridge';
const URI = 'https://www.abc.net.au';
const DESCRIPTION = 'Topics of the Australian Broadcasting Corporation';
const MAINTAINER = 'yue-dongchen';
const PARAMETERS = array(
array(
'topic' => array(
'type' => 'list',
'name' => 'Region',
'title' => 'Choose state',
'values' => array(
'ACT' => 'act',
'NSW' => 'nsw',
'NT' => 'nt',
'QLD' => 'qld',
'SA' => 'sa',
'TAS' => 'tas',
'VIC' => 'vic',
'WA' => 'wa'
),
)
)
);
public function collectData() {
$url = 'https://www.abc.net.au/news/' . $this->getInput('topic');
$html = getSimpleHTMLDOM($url)->find('.YAJzu._26IxR._2kxNB._3BZxh', 0);
$html = defaultLinkTo($html, $this->getURI());
foreach($html->find('._2H7Su') as $article) {
$item = array();
$title = $article->find('._3T9Id.fmhNa.nsZdE._2c2Zy._1tOey._3EOTW', 0);
$item['title'] = $title->plaintext;
$item['uri'] = $title->href;
$item['content'] = $article->find('.rMkro._1cBaI._3PhF6._10YQT._1yL-m', 0)->plaintext;
$item['timestamp'] = strtotime($article->find('time', 0)->datetime);
$this->items[] = $item;
}
}
}

View File

@@ -37,8 +37,7 @@ class AO3Bridge extends BridgeAbstract {
// Feed for lists of works (e.g. recent works, search results, filtered tags,
// bookmarks, series, collections).
private function collectList($url) {
$html = getSimpleHTMLDOM($url)
or returnServerError('could not request AO3');
$html = getSimpleHTMLDOM($url);
$html = defaultLinkTo($html, self::URI);
foreach($html->find('.index.group > li') as $element) {
@@ -65,8 +64,7 @@ class AO3Bridge extends BridgeAbstract {
// Feed for recent chapters of a specific work.
private function collectWork($id) {
$url = self::URI . "/works/$id/navigate";
$html = getSimpleHTMLDOM($url)
or returnServerError('could not request AO3');
$html = getSimpleHTMLDOM($url);
$html = defaultLinkTo($html, self::URI);
$this->title = $html->find('h2 a', 0)->plaintext;

View File

@@ -0,0 +1,91 @@
<?php
class ARDMediathekBridge extends BridgeAbstract {
const NAME = 'ARD-Mediathek Bridge';
const URI = 'https://www.ardmediathek.de';
const DESCRIPTION = 'Feed of any series in the ARD-Mediathek, specified by its path';
const MAINTAINER = 'yue-dongchen';
/*
* Number of Items to be requested from ARDmediathek API
* 12 has been observed on the wild
* 29 is the highest successfully tested value
* More Items could be fetched via pagination
* The JSON-field pagination holds more information on that
* @const PAGESIZE number of requested items
*/
const PAGESIZE = 29;
/*
* The URL Prefix of the (Webapp-)API
* @const APIENDPOINT https-URL of the used endpoint
*/
const APIENDPOINT = 'https://api.ardmediathek.de/page-gateway/widgets/ard/asset/';
/*
* The URL prefix of the video link
* URLs from the webapp include a slug containing titles of show, episode, and tv station.
* It seems to work without that.
* @const VIDEOLINKPREFIX https-URL prefix of video links
*/
const VIDEOLINKPREFIX = 'https://www.ardmediathek.de/video/';
/*
* The requested width of the preview image
* 432 has been observed on the wild
* The webapp seems to also compute and add the height value
* It seems to works without that.
* @const IMAGEWIDTH width in px of the preview image
*/
const IMAGEWIDTH = 432;
/*
* Placeholder that will be replace by IMAGEWIDTH in the preview image URL
* @const IMAGEWIDTHPLACEHOLDER
*/
const IMAGEWIDTHPLACEHOLDER = '{width}';
const PARAMETERS = array(
array(
'path' => array(
'name' => 'Show Link or ID',
'required' => true,
'title' => 'Link to the show page or just its alphanumeric suffix',
'defaultValue' => 'https://www.ardmediathek.de/sendung/45-min/Y3JpZDovL25kci5kZS8xMzkx/'
)
)
);
public function collectData() {
date_default_timezone_set('Europe/Berlin');
$pathComponents = explode('/', $this->getInput('path'));
if (empty($pathComponents)) {
returnClientError('Path may not be empty');
}
if (count($pathComponents) < 2) {
$showID = $pathComponents[0];
} else {
$lastKey = count($pathComponents) - 1;
$showID = $pathComponents[$lastKey];
if (strlen($showID) === 0) {
$showID = $pathComponents[$lastKey - 1];
}
}
$url = SELF::APIENDPOINT . $showID . '/?pageSize=' . SELF::PAGESIZE;
$rawJSON = getContents($url);
$processedJSON = json_decode($rawJSON);
foreach($processedJSON->teasers as $video) {
$item = array();
// there is also ->links->self->id, ->links->self->urlId, ->links->target->id, ->links->target->urlId
$item['uri'] = SELF::VIDEOLINKPREFIX . $video->id . '/';
// there is also ->mediumTitle and ->shortTitle
$item['title'] = $video->longTitle;
// in the test, aspect16x9 was the only child of images, not sure whether that is always true
$item['enclosures'] = array(
str_replace(SELF::IMAGEWIDTHPLACEHOLDER, SELF::IMAGEWIDTH, $video->images->aspect16x9->src)
);
$item['content'] = '<img src="' . $item['enclosures'][0] . '" /><p>';
$item['timestamp'] = $video->broadcastedOn;
$item['uid'] = $video->id;
$item['author'] = $video->publicationService->name;
$this->items[] = $item;
}
}
}

View File

@@ -10,8 +10,7 @@ class ASRockNewsBridge extends BridgeAbstract {
public function collectData() {
$html = getSimpleHTMLDOM(self::URI . '/news/index.asp')
or returnServerError('Could not request: ' . self::URI . '/news/index.asp');
$html = getSimpleHTMLDOM(self::URI . '/news/index.asp');
$html = defaultLinkTo($html, self::URI . '/news/');
@@ -20,17 +19,16 @@ class ASRockNewsBridge extends BridgeAbstract {
$articlePath = $a->href;
$articlePageHtml = getSimpleHTMLDOMCached($articlePath, self::CACHE_TIMEOUT)
or returnServerError('Could not request: ' . $articlePath);
$articlePageHtml = getSimpleHTMLDOMCached($articlePath, self::CACHE_TIMEOUT);
$articlePageHtml = defaultLinkTo($articlePageHtml, self::URI);
$contents = $articlePageHtml->find('div.Contents', 0);
$item['uri'] = $articlePath;
$item['title'] = $contents->find('h5', 0)->innertext;
$item['title'] = $contents->find('h3', 0)->innertext;
$contents->find('h5', 0)->outertext = '';
$contents->find('h3', 0)->outertext = '';
$item['content'] = $contents->innertext;
$item['timestamp'] = $this->extractDate($a->plaintext);

View File

@@ -42,8 +42,7 @@ class AlbionOnlineBridge extends BridgeAbstract {
// Example: https://albiononline.com/en/changelog/1/5
$url = $api . $this->getInput('language') . '/changelog/1/' . $this->getInput('postcount');
$html = getSimpleHTMLDOM($url)
or returnServerError('Unable to get changelog data from "' . $url . '"!');
$html = getSimpleHTMLDOM($url);
foreach ($html->find('li') as $data) {
$item = array();
@@ -66,8 +65,7 @@ class AlbionOnlineBridge extends BridgeAbstract {
}
private function getFullChangelog($url) {
$html = getSimpleHTMLDOMCached($url)
or returnServerError('Unable to load changelog post from "' . $url . '"!');
$html = getSimpleHTMLDOMCached($url);
$html = defaultLinkTo($html, self::URI);
return $html->find('div.small-12.columns', 1)->innertext;
}

View File

@@ -77,8 +77,7 @@ class AllocineFRBridge extends BridgeAbstract {
public function collectData(){
$html = getSimpleHTMLDOM($this->getURI())
or returnServerError('Could not request ' . $this->getURI() . ' !');
$html = getSimpleHTMLDOM($this->getURI());
$category = array_search(
$this->getInput('category'),

View File

@@ -61,8 +61,7 @@ class AmazonBridge extends BridgeAbstract {
$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.');
$html = getSimpleHTMLDOM($uri);
foreach($html->find('li.s-result-item') as $element) {

View File

@@ -1,7 +1,7 @@
<?php
class AmazonPriceTrackerBridge extends BridgeAbstract {
const MAINTAINER = 'captn3m0';
const MAINTAINER = 'captn3m0, sal0max';
const NAME = 'Amazon Price Tracker';
const URI = 'https://www.amazon.com/';
const CACHE_TIMEOUT = 3600; // 1h
@@ -40,6 +40,15 @@ class AmazonPriceTrackerBridge extends BridgeAbstract {
),
));
const PRICE_SELECTORS = array(
'#priceblock_ourprice',
'.priceBlockBuyingPriceString',
'#newBuyBoxPrice',
'#tp_price_block_total_price_ww',
'span.offer-price',
'.a-color-price',
);
protected $title;
/**
@@ -54,7 +63,7 @@ class AmazonPriceTrackerBridge extends BridgeAbstract {
*/
public function getURI() {
if (!is_null($this->getInput('asin'))) {
return $this->getDomainName() . '/dp/' . $this->getInput('asin') . '/';
return $this->getDomainName() . '/dp/' . $this->getInput('asin');
}
return parent::getURI();
}
@@ -146,14 +155,30 @@ EOT;
}
private function scrapePriceGeneric($html) {
$priceDiv = $html->find('span.offer-price', 0) ?: $html->find('.a-color-price', 0);
$priceDiv = null;
preg_match('/^\s*([A-Z]{3}|£|\$)\s?([\d.,]+)\s*$/', $priceDiv->plaintext, $matches);
foreach(self::PRICE_SELECTORS as $sel) {
$priceDiv = $html->find($sel, 0);
if ($priceDiv) {
break;
}
}
if (count($matches) === 3) {
if (!$priceDiv) {
return false;
}
$priceString = $priceDiv->plaintext;
preg_match('/[\d.,]+/', $priceString, $matches);
$price = $matches[0];
$currency = trim(str_replace($price, '', $priceString), " \t\n\r\0\x0B\xC2\xA0");
if ($price != null && $currency != null) {
return array(
'price' => $matches[2],
'currency' => $matches[1],
'price' => $price,
'currency' => $currency,
'shipping' => '0'
);
}
@@ -176,6 +201,8 @@ EOT;
'title' => $this->title,
'uri' => $this->getURI(),
'content' => "$imageTag<br/>Price: {$data['price']} {$data['currency']}",
// This is to ensure that feed readers notice the price change
'uid' => md5($data['price'])
);
if ($data['shipping'] !== '0') {

View File

@@ -142,8 +142,7 @@ class AnidexBridge extends BridgeAbstract {
$opt[CURLOPT_SSL_VERIFYPEER] = 0;
// Retrieve torrent listing from search results, which does not contain torrent description
$html = getSimpleHTMLDOM($search_url, $headers, $opt)
or returnServerError('Could not request Anidex: ' . $search_url);
$html = getSimpleHTMLDOM($search_url, $headers, $opt);
$links = $html->find('a');
$results = array();
foreach ($links as $link)

View File

@@ -39,8 +39,7 @@ class AnimeUltimeBridge extends BridgeAbstract {
//Retrive page contents
$url = self::URI . 'history-0-1/' . $requestFilter;
$html = getSimpleHTMLDOM($url)
or returnServerError('Could not request Anime-Ultime: ' . $url);
$html = getSimpleHTMLDOM($url);
//Relases are sorted by day : process each day individually
foreach($html->find('div.history', 0)->find('h3') as $daySection) {
@@ -87,8 +86,7 @@ class AnimeUltimeBridge extends BridgeAbstract {
if(!empty($item_uri)) {
// Retrieve description from description page
$html_item = getContents($item_uri)
or returnServerError('Could not request Anime-Ultime: ' . $item_uri);
$html_item = getContents($item_uri);
$item_description = substr(
$html_item,
strpos($html_item, 'class="principal_contain" align="center">') + 41

View File

@@ -35,7 +35,8 @@ class AppleAppStoreBridge extends BridgeAbstract {
'values' => array(
'US' => 'US',
'India' => 'IN',
'Canada' => 'CA'
'Canada' => 'CA',
'Germany' => 'DE',
),
'defaultValue' => 'US',
),

View File

@@ -4,67 +4,52 @@ class AppleMusicBridge extends BridgeAbstract {
const NAME = 'Apple Music';
const URI = 'https://www.apple.com';
const DESCRIPTION = 'Fetches the latest releases from an artist';
const MAINTAINER = 'Limero';
const MAINTAINER = 'bockiii';
const PARAMETERS = array(array(
'url' => array(
'name' => 'Artist URL',
'exampleValue' => 'https://itunes.apple.com/us/artist/dunderpatrullen/329796274',
'artist' => array(
'name' => 'Artist ID',
'exampleValue' => '909253',
'required' => true,
),
'imgSize' => array(
'name' => 'Image size for thumbnails (in px)',
'type' => 'number',
'defaultValue' => 512,
'limit' => array(
'name' => 'Latest X Releases (max 50)',
'defaultValue' => '10',
'required' => true,
)
),
));
const CACHE_TIMEOUT = 21600; // 6 hours
private $title;
public function collectData() {
$url = $this->getInput('url');
$html = getSimpleHTMLDOM($url)
or returnServerError('Could not request: ' . $url);
# Limit the amount of releases to 50
if ($this->getInput('limit') > 50) {
$limit = 50;
} else {
$limit = $this->getInput('limit');
}
$imgSize = $this->getInput('imgSize');
$url = 'https://itunes.apple.com/lookup?id='
. $this->getInput('artist')
. '&entity=album&limit='
. $limit .
'&sort=recent';
$html = getSimpleHTMLDOM($url);
$this->title = $html->find('title', 0)->innertext;
// Grab the json data from the page
$html = $html->find('script[id=shoebox-ember-data-store]', 0);
$html = strstr($html, '{');
$html = substr($html, 0, -9);
$json = json_decode($html);
// Loop through each object
foreach ($json->included as $obj) {
if ($obj->type === 'lockup/album') {
foreach ($json->results as $obj) {
if ($obj->wrapperType === 'collection') {
$this->items[] = array(
'title' => $obj->attributes->artistName . ' - ' . $obj->attributes->name,
'uri' => $obj->attributes->url,
'timestamp' => $obj->attributes->releaseDate,
'enclosures' => $obj->relationships->artwork->data->id,
'title' => $obj->artistName . ' - ' . $obj->collectionName,
'uri' => $obj->collectionViewUrl,
'timestamp' => $obj->releaseDate,
'enclosures' => $obj->artworkUrl100,
'content' => '<a href=' . $obj->collectionViewUrl
. '><img src="' . $obj->artworkUrl100 . '" /></a><br><br>'
. $obj->artistName . ' - ' . $obj->collectionName
. '<br>'
. $obj->copyright,
);
} elseif ($obj->type === 'image') {
$images[$obj->id] = $obj->attributes->url;
}
}
// Add the images to each item
foreach ($this->items as &$item) {
$item['enclosures'] = array(
str_replace('{w}x{h}bb.{f}', $imgSize . 'x0w.jpg', $images[$item['enclosures']]),
);
}
// Sort the order to put the latest albums first
usort($this->items, function($a, $b){
return $a['timestamp'] < $b['timestamp'];
});
}
public function getName() {
return $this->title ?: parent::getName();
}
}

View File

@@ -39,15 +39,13 @@ class ArtStationBridge extends BridgeAbstract {
);
$jsonSearchURL = self::URI . '/api/v2/search/projects.json';
$jsonSearchStr = getContents($jsonSearchURL, $header, $opts)
or returnServerError('Could not fetch JSON for search query.');
$jsonSearchStr = getContents($jsonSearchURL, $header, $opts);
return json_decode($jsonSearchStr);
}
private function fetchProject($hashID) {
$jsonProjectURL = self::URI . '/projects/' . $hashID . '.json';
$jsonProjectStr = getContents($jsonProjectURL)
or returnServerError('Could not fetch JSON for project.');
$jsonProjectStr = getContents($jsonProjectURL);
return json_decode($jsonProjectStr);
}

View File

@@ -91,8 +91,7 @@ class Arte7Bridge extends BridgeAbstract {
'Authorization: Bearer ' . self::API_TOKEN
);
$input = getContents($url, $header)
or returnServerError('Could not request ARTE.');
$input = getContents($url, $header);
$input_json = json_decode($input, true);
foreach($input_json['videos'] as $element) {

View File

@@ -36,8 +36,7 @@ class AsahiShimbunAJWBridge extends BridgeAbstract {
}
public function collectData() {
$html = getSimpleHTMLDOM($this->getSectionURI($this->getInput('section')))
or returnServerError('Could not load content');
$html = getSimpleHTMLDOM($this->getSectionURI($this->getInput('section')));
foreach($html->find('#MainInner li a') as $element) {
if ($element->parent()->class == 'HeadlineTopImage-S') {

View File

@@ -16,8 +16,7 @@ class AskfmBridge extends BridgeAbstract {
);
public function collectData(){
$html = getSimpleHTMLDOM($this->getURI())
or returnServerError('Requested username can\'t be found.');
$html = getSimpleHTMLDOM($this->getURI());
$html = defaultLinkTo($html, self::URI);

View File

@@ -29,8 +29,7 @@ class AtmoNouvelleAquitaineBridge extends BridgeAbstract {
public function collectData() {
$uri = self::URI . '/monair/commune/' . $this->getInput('cities');
$html = getSimpleHTMLDOM($uri)
or returnServerError('Could not request ' . $uri);
$html = getSimpleHTMLDOM($uri);
$this->dom = $html->find('#block-system-main .city-prevision-map', 0);

View File

@@ -16,8 +16,7 @@ class AtmoOccitanieBridge extends BridgeAbstract {
public function collectData() {
$uri = self::URI . $this->getInput('city');
$html = getSimpleHTMLDOM($uri)
or returnServerError('Could not request ' . $uri);
$html = getSimpleHTMLDOM($uri);
$generalMessage = $html->find('.landing-ville .city-banner .iqa-avertissement', 0)->innertext;
$recommendationsDom = $html->find('.landing-ville .recommandations', 0);

View File

@@ -7,51 +7,14 @@ class AutoJMBridge extends BridgeAbstract {
const DESCRIPTION = 'Suivre les offres de véhicules proposés par AutoJM en fonction des critères de filtrages';
const MAINTAINER = 'sysadminstory';
const PARAMETERS = array(
'Afficher les offres de véhicules disponible en fonction des critères du site AutoJM' => array(
'Afficher les offres de véhicules disponible sur la recheche AutoJM' => array(
'url' => array(
'name' => 'URL du modèle',
'name' => 'URL de la page de recherche',
'type' => 'text',
'required' => true,
'title' => 'URL d\'une recherche avec filtre de véhicules sans le http://www.autojm.fr/',
'exampleValue' => 'achat-voitures-neuves-peugeot-nouvelle-308-5p'
'exampleValue' => 'recherche?brands[]=peugeot&ranges[]=peugeot-nouvelle-308-2021-5p'
),
'energy' => array(
'name' => 'Carburant',
'type' => 'list',
'values' => array(
'-' => '',
'Diesel' => 1,
'Essence' => 3,
'Hybride' => 5
),
'title' => 'Carburant'
),
'transmission' => array(
'name' => 'Transmission',
'type' => 'list',
'values' => array(
'-' => '',
'Automatique' => 1,
'Manuelle' => 2
),
'title' => 'Transmission'
),
'priceMin' => array(
'name' => 'Prix minimum',
'type' => 'number',
'required' => false,
'title' => 'Prix minimum du véhicule',
'exampleValue' => '10000',
'defaultValue' => '0'
),
'priceMax' => array(
'name' => 'Prix maximum',
'type' => 'number',
'required' => false,
'title' => 'Prix maximum du véhicule',
'exampleValue' => '15000',
'defaultValue' => '150000'
)
)
);
const CACHE_TIMEOUT = 3600;
@@ -62,10 +25,8 @@ class AutoJMBridge extends BridgeAbstract {
public function getName() {
switch($this->queriedContext) {
case 'Afficher les offres de véhicules disponible en fonction des critères du site AutoJM':
$html = getSimpleHTMLDOMCached(self::URI . $this->getInput('url'), 86400);
$name = html_entity_decode($html->find('title', 0)->plaintext);
return $name;
case 'Afficher les offres de véhicules disponible sur la recheche AutoJM':
return 'AutoJM | Recherche de véhicules';
break;
default:
return parent::getName();
@@ -75,13 +36,8 @@ class AutoJMBridge extends BridgeAbstract {
public function collectData() {
$model_url = self::URI . $this->getInput('url');
// Build the GET data
$get_data = 'form[energy]=' . $this->getInput('energy') .
'&form[transmission]=' . $this->getInput('transmission') .
'&form[priceMin]=' . $this->getInput('priceMin') .
'&form[priceMin]=' . $this->getInput('priceMin');
// Get the number of result for this search
$search_url = self::URI . $this->getInput('url') . '&open=energy&onlyFilters=false';
// Set the header 'X-Requested-With' like the website does it
$header = array(
@@ -89,57 +45,91 @@ class AutoJMBridge extends BridgeAbstract {
);
// Get the JSON content of the form
$json = getContents($model_url . '?' . $get_data, $header)
or returnServerError('Could not request AutoJM.');
$json = getContents($search_url, $header);
// Extract the HTML content from the JSON result
$data = json_decode($json);
$html = str_get_html($data->results);
// Go through every car of the model
$list = $html->find('div[class=car-card]');
foreach ($list as $car) {
$nb_results = $data->nbResults;
$total_pages = ceil($nb_results / 15);
// Get the Finish name if this car is the first of a new finish
$prev_tag = $car->prev_sibling();
if($prev_tag->tag == 'div' && $prev_tag->class == 'results-title') {
$finish_name = $prev_tag->plaintext;
// Limit the number of page to analyse to 10
for($page = 1; $page <= $total_pages && $page <= 10; $page++) {
// Get the result the next page
$html = $this->getResults($page);
// Go through every car of the search
$list = $html->find('div[class*=card-car card-car--listing]');
foreach ($list as $car) {
// Get the info about the car offer
$image = $car->find('div[class=card-car__header__img]', 0)->find('img', 0)->src;
// Decode HTML attribute JSON data
$car_data = json_decode(html_entity_decode($car->{'data-layer'}));
$car_model = $car->{'data-title'} . ' ' . $car->{'data-suptitle'};
$availability = $car->find('div[class=card-car__modalites]', 0)->find('div[class=col]', 0)->plaintext;
$warranty = $car->find('div[data-type=WarrantyCard]', 0)->plaintext;
$discount_html = $car->find('div[class=subtext vehicle_reference_element]', 0);
// Check if there is any discount info displayed
if ($discount_html != null) {
$reference_price_value = $discount_html->find('span[data-cfg=vehicle__reference_price]', 0)->plaintext;
$discount_percent_value = $discount_html->find('span[data-cfg=vehicle__discount_percent]', 0)->plaintext;
$reference_price = '<li>Prix de référence : <s>' . $reference_price_value . '</s></li>';
$discount_percent = '<li>Réduction : ' . $discount_percent_value . ' %</li>';
} else {
$reference_price = '';
$discount_percent = '';
}
$price = $car_data->price;
$kilometer = $car->find('span[data-cfg=vehicle__kilometer]', 0)->plaintext;
$energy = $car->find('span[data-cfg=vehicle__energy__label]', 0)->plaintext;
$power = $car->find('span[data-cfg=vehicle__tax_horse_power]', 0)->plaintext;
$seats = $car->find('span[data-cfg=vehicle__seats]', 0)->plaintext;
$doors = $car->find('span[data-cfg=vehicle__door__label]', 0)->plaintext;
$transmission = $car->find('span[data-cfg=vehicle__transmission]', 0)->plaintext;
$loa_html = $car->find('span[data-cfg=vehicle__loa]', 0);
// Check if any LOA price is displayed
if($loa_html != null) {
$loa_value = $car->find('span[data-cfg=vehicle__loa]', 0)->plaintext;
$loa = '<li>LOA : à partir de ' . $loa_value . ' / mois </li>';
} else {
$loa = '';
}
// Construct the new item
$item = array();
$item['title'] = $car_model;
$item['content'] = '<p><img style="vertical-align:middle ; padding: 10px" src="' . $image . '" />'
. $car_model . '</p>';
$item['content'] .= '<ul><li>Disponibilité : ' . $availability . '</li>';
$item['content'] .= '<li>Prix : ' . $price . ' €</li>';
$item['content'] .= $reference_price;
$item['content'] .= $loa;
$item['content'] .= $discount_percent;
$item['content'] .= '<li>Garantie : ' . $warranty . '</li>';
$item['content'] .= '<li>Kilométrage : ' . $kilometer . ' km</li>';
$item['content'] .= '<li>Energie : ' . $energy . '</li>';
$item['content'] .= '<li>Puissance: ' . $power . ' CV Fiscaux</li>';
$item['content'] .= '<li>Nombre de Places : ' . $seats . ' place(s)</li>';
$item['content'] .= '<li>Nombre de portes : ' . $doors . '</li>';
$item['content'] .= '<li>Boite de vitesse : ' . $transmission . '</li></ul>';
$item['uri'] = $car_data->{'uri'};
$item['uid'] = hash('md5', $item['content']);
$this->items[] = $item;
}
// Get the info about the car offer
$image = $car->find('div[class=car-card__visual]', 0)->find('img', 0)->src;
$serie = $car->find('div[class=car-card__title]', 0)->plaintext;
$url = $car->find('a', 0)->href;
// Check if the car model is in stock or available only on order
if($car->find('span[class*=tag--dispo]', 0) != null) {
$availability = 'En Stock';
} else {
$availability = 'Sur commande';
}
$discount_html = $car->find('span[class=promo]', 0);
// Check if there is any discount dsiplayed
if ($discount_html != null) {
$discount = $discount_html->plaintext;
} else {
$discount = 'inconnue';
}
$price = $car->find('span[class=price]', 0)->plaintext;
// Construct the new item
$item = array();
$item['title'] = $finish_name . ' ' . $serie;
$item['content'] = '<p><img style="vertical-align:middle ; padding: 10px" src="' . $image . '" />'
. $finish_name . ' ' . $serie . '</p>';
$item['content'] .= '<ul><li>Disponibilité : ' . $availability . '</li>';
$item['content'] .= '<li>Série : ' . $serie . '</li>';
$item['content'] .= '<li>Remise : ' . $discount . '</li>';
$item['content'] .= '<li>Prix : ' . $price . '</li></ul>';
// Add a fictionnal anchor to the RSS element URL, based on the item content ;
// As the URL could be identical even if the price change, some RSS reader will not show those offers as new items
$item['uri'] = $url . '#' . md5($item['content']);
$this->items[] = $item;
}
}
private function getResults(int $page)
{
$user_input = $this->getInput('url');
$search_data = preg_replace('#(recherche|recherche/[0-9]{1,10})\?#', 'recherche/' . $page . '?', $user_input);
$search_url = self::URI . $search_data . '&open=energy&onlyFilters=false';
// Get the HTML content of the page
$html = getSimpleHTMLDOMCached($search_url);
return $html;
}
}

View File

@@ -18,8 +18,7 @@ class AwwwardsBridge extends BridgeAbstract {
private function fetchSites() {
Debug::log('Fetching all sites');
$sites = getSimpleHTMLDOM(self::SITESURI)
or returnServerError('Could not fetch JSON for sites.');
$sites = getSimpleHTMLDOM(self::SITESURI);
Debug::log('Parsing all JSON data');
foreach($sites->find('li[data-model]') as $site) {

View File

@@ -220,8 +220,7 @@ class BadDragonBridge extends BridgeAbstract {
public function collectData() {
switch($this->queriedContext) {
case 'Sales':
$sales = json_decode(getContents(self::URI . 'api/sales'))
or returnServerError('Failed to query BD API');
$sales = json_decode(getContents(self::URI . 'api/sales'));
foreach($sales as $sale) {
$item = array();
@@ -274,12 +273,10 @@ class BadDragonBridge extends BridgeAbstract {
}
break;
case 'Clearance':
$toyData = json_decode(getContents($this->inputToURL(true)))
or returnServerError('Failed to query BD API');
$toyData = json_decode(getContents($this->inputToURL(true)));
$productList = json_decode(getContents(self::URI
. 'api/inventory-toy/product-list'))
or returnServerError('Failed to query BD API');
. 'api/inventory-toy/product-list'));
foreach($toyData->toys as $toy) {
$item = array();

View File

@@ -3,23 +3,80 @@ class BakaUpdatesMangaReleasesBridge extends BridgeAbstract {
const NAME = 'Baka Updates Manga Releases';
const URI = 'https://www.mangaupdates.com/';
const DESCRIPTION = 'Get the latest series releases';
const MAINTAINER = 'fulmeek';
const PARAMETERS = array(array(
'series_id' => array(
'name' => 'Series ID',
'type' => 'number',
'required' => true,
'exampleValue' => '12345'
const MAINTAINER = 'fulmeek, KamaleiZestri';
const PARAMETERS = array(
'By series' => array(
'series_id' => array(
'name' => 'Series ID',
'type' => 'number',
'required' => true,
'exampleValue' => '12345'
)
),
'By list' => array(
'list_id' => array(
'name' => 'List ID and Type',
'type' => 'text',
'required' => true,
'exampleValue' => '123456&list=read'
)
)
));
);
const LIMIT_COLS = 5;
const LIMIT_ITEMS = 10;
const RELEASES_URL = 'https://www.mangaupdates.com/releases.html';
private $feedName = '';
public function collectData() {
$html = getSimpleHTMLDOM($this->getURI())
or returnServerError('Series not found');
if($this -> queriedContext == 'By series')
$this -> collectDataBySeries();
else //queriedContext == 'By list'
$this -> collectDataByList();
}
public function getURI(){
if($this -> queriedContext == 'By series') {
$series_id = $this->getInput('series_id');
if (!empty($series_id)) {
return self::URI . 'releases.html?search=' . $series_id . '&stype=series';
}
} else //queriedContext == 'By list'
return self::RELEASES_URL;
return self::URI;
}
public function getName(){
if(!empty($this->feedName)) {
return $this->feedName . ' - ' . self::NAME;
}
return parent::getName();
}
private function getSanitizedHash($string) {
return hash('sha1', preg_replace('/[^a-zA-Z0-9\-\.]/', '', ucwords(strtolower($string))));
}
private function filterText($text) {
return rtrim($text, '* ');
}
private function filterHTML($text) {
return $this->filterText(html_entity_decode($text));
}
private function findID($manga) {
// sometimes new series are on the release list that have no ID. just drop them.
if(@$this -> filterHTML($manga -> find('a', 0) -> href) != null) {
preg_match('/id=([0-9]*)/', $this -> filterHTML($manga -> find('a', 0) -> href), $match);
return $match[1];
} else
return 0;
}
private function collectDataBySeries() {
$html = getSimpleHTMLDOM($this->getURI());
// content is an unstructured pile of divs, ugly to parse
$cols = $html->find('div#main_content div.row > div.text');
@@ -68,36 +125,62 @@ class BakaUpdatesMangaReleasesBridge extends BridgeAbstract {
$item['title'] = implode(' ', $title);
$item['uri'] = $this->getURI();
$item['uid'] = $this->getSanitizedHash($item['title']);
$item['uid'] = $this->getSanitizedHash($item['title'] . $item['author']);
$this->items[] = $item;
}
}
public function getURI(){
$series_id = $this->getInput('series_id');
if (!empty($series_id)) {
return self::URI . 'releases.html?search=' . $series_id . '&stype=series';
private function collectDataByList() {
$this -> feedName = 'Releases';
$list = array();
$releasesHTML = getSimpleHTMLDOM(self::RELEASES_URL);
$list_id = $this -> getInput('list_id');
$listHTML = getSimpleHTMLDOM('https://www.mangaupdates.com/mylist.html?id=' . $list_id);
//get ids of the manga that the user follows,
$parts = $listHTML -> find('table#ptable tr > td.pl');
foreach($parts as $part) {
$list[] = $this -> findID($part);
}
return self::URI;
}
public function getName(){
if(!empty($this->feedName)) {
return $this->feedName . ' - ' . self::NAME;
//similar to above, but the divs are in groups of 3.
$cols = $releasesHTML -> find('div#main_content div.row > div.pbreak');
$rows = array_slice(array_chunk($cols, 3), 0);
foreach($rows as $cols) {
//check if current manga is in user's list.
$id = $this -> findId($cols[0]);
if(!array_search($id, $list)) continue;
$item = array();
$title = array();
$item['content'] = '';
$objTitle = $cols[0];
if ($objTitle) {
$title[] = $this->filterHTML($objTitle->plaintext);
$item['content'] .= '<p>Series: ' . $this->filterHTML($objTitle -> innertext) . '</p>';
}
$objVolChap = $cols[1];
if ($objVolChap && !empty($objVolChap->plaintext))
$title[] = $this -> filterHTML($objVolChap -> innertext);
$objAuthor = $cols[2];
if ($objAuthor && !empty($objAuthor->plaintext)) {
$item['author'] = $this->filterHTML($objAuthor -> plaintext);
$item['content'] .= '<p>Groups: ' . $this->filterHTML($objAuthor -> innertext) . '</p>';
}
$item['title'] = implode(' ', $title);
$item['uri'] = self::URI . 'releases.html?search=' . $id . '&stype=series';
$item['uid'] = $this->getSanitizedHash($item['title'] . $item['author']);
$this->items[] = $item;
}
return parent::getName();
}
private function getSanitizedHash($string) {
return hash('sha1', preg_replace('/[^a-zA-Z0-9\-\.]/', '', ucwords(strtolower($string))));
}
private function filterText($text) {
return rtrim($text, '* ');
}
private function filterHTML($text) {
return $this->filterText(html_entity_decode($text));
}
}

View File

@@ -38,6 +38,30 @@ class BandcampBridge extends BridgeAbstract {
'defaultValue' => 5
)
),
'By label' => array(
'label' => array(
'name' => 'label',
'type' => 'text',
'title' => 'label name as seen in the label page URL',
'required' => true
),
'type' => array(
'name' => 'Articles are',
'type' => 'list',
'values' => array(
'Releases' => 'releases',
'Releases, new one when track list changes' => 'changes',
'Individual tracks' => 'tracks'
),
'defaultValue' => 'changes'
),
'limit' => array(
'name' => 'limit',
'type' => 'number',
'title' => 'Number of releases to return',
'defaultValue' => 5
)
),
'By album' => array(
'band' => array(
'name' => 'band',
@@ -86,8 +110,7 @@ class BandcampBridge extends BridgeAbstract {
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => $data
);
$content = getContents($url, $header, $opts)
or returnServerError('Could not complete request to: ' . $url);
$content = getContents($url, $header, $opts);
$json = json_decode($content);
@@ -122,6 +145,7 @@ class BandcampBridge extends BridgeAbstract {
}
break;
case 'By band':
case 'By label':
case 'By album':
$html = getSimpleHTMLDOMCached($this->getURI(), 86400);
@@ -139,6 +163,7 @@ class BandcampBridge extends BridgeAbstract {
$tralbums = array();
switch($this->queriedContext) {
case 'By band':
case 'By label':
$query_data = array(
'band_id' => $band_id
);
@@ -274,8 +299,7 @@ class BandcampBridge extends BridgeAbstract {
private function apiGet($endpoint, $query_data) {
$url = self::URI . 'api/' . $endpoint . '?' . http_build_query($query_data);
$data = json_decode(getContents($url))
or returnServerError('API request to "' . $url . '" failed.');
$data = json_decode(getContents($url));
return $data;
}
@@ -289,6 +313,13 @@ class BandcampBridge extends BridgeAbstract {
. '?sort_field=date';
}
break;
case 'By label':
if(!is_null($this->getInput('label'))) {
return 'https://'
. $this->getInput('label')
. '.bandcamp.com/music';
}
break;
case 'By band':
if(!is_null($this->getInput('band'))) {
return 'https://'
@@ -323,6 +354,13 @@ class BandcampBridge extends BridgeAbstract {
return $this->getInput('band') . ' - Bandcamp Band';
}
break;
case 'By label':
if(isset($this->feedName)) {
return $this->feedName . ' - Bandcamp Label';
} elseif(!is_null($this->getInput('label'))) {
return $this->getInput('label') . ' - Bandcamp Label';
}
break;
case 'By album':
if(isset($this->feedName)) {
return $this->feedName . ' - Bandcamp Album';

View File

@@ -8,8 +8,7 @@ class BastaBridge extends BridgeAbstract {
const DESCRIPTION = 'Returns the newest articles.';
public function collectData(){
$html = getSimpleHTMLDOM(self::URI . 'spip.php?page=backend')
or returnServerError('Could not request Bastamag.');
$html = getSimpleHTMLDOM(self::URI . 'spip.php?page=backend');
$limit = 0;

View File

@@ -35,28 +35,23 @@ class BinanceBridge extends BridgeAbstract {
}
protected function collectBlogData() {
$html = getSimpleHTMLDOM($this->getURI())
or returnServerError('Could not fetch Binance blog data.');
$html = getSimpleHTMLDOM($this->getURI());
foreach($html->find('div[direction="row"]') as $element) {
$appData = $html->find('script[id="__APP_DATA"]');
$appDataJson = json_decode($appData[0]->innertext);
$date = $element->find('div[direction="column"]', 0);
$day = $date->find('div', 0)->innertext;
$month = $date->find('div', 1)->innertext;
$extractedDate = $day . ' ' . $month;
foreach($appDataJson->pageData->redux->blogList->blogList as $element) {
$abstract = $element->find('div[direction="column"]', 1);
$a = $abstract->find('a', 0);
$uri = self::URI . $a->href;
$title = $a->innertext;
$full = getSimpleHTMLDOMCached($uri);
$content = $full->find('div.desc', 1);
$date = $element->postTime;
$abstract = $element->brief;
$uri = self::URI . '/' . $element->lang . '/blog/' . $element->idStr;
$title = $element->title;
$content = $element->content;
$item = array();
$item['title'] = $title;
$item['uri'] = $uri;
$item['timestamp'] = strtotime($extractedDate);
$item['timestamp'] = substr($date, 0, -3);
$item['author'] = 'Binance';
$item['content'] = $content;
@@ -68,8 +63,7 @@ class BinanceBridge extends BridgeAbstract {
}
protected function collectAnnouncementData() {
$html = getSimpleHTMLDOM($this->getURI())
or returnServerError('Could not fetch Zendesk announcement data.');
$html = getSimpleHTMLDOM($this->getURI());
foreach($html->find('a.article-list-link') as $a) {
$title = $a->innertext;

View File

@@ -1,119 +0,0 @@
<?php
class BingSearchBridge extends BridgeAbstract
{
const NAME = 'Bing search';
const URI = 'https://www.bing.com/';
const DESCRIPTION = 'Return images from bing search discover';
const MAINTAINER = 'DnAp';
const PARAMETERS = array(
'Image Discover' => array(
'category' => array(
'name' => 'Categories',
'type' => 'list',
'values' => self::IMAGE_DISCOVER_CATEGORIES
),
'image_size' => array(
'name' => 'Image size',
'type' => 'list',
'values' => array(
'Small' => 'turl',
'Full size' => 'imgurl'
)
)
)
);
const IMAGE_DISCOVER_CATEGORIES = array(
'Abstract' => 'abstract',
'Animals' => 'animals',
'Anime' => 'anime',
'Architecture' => 'architecture',
'Arts and Crafts' => 'arts-and-crafts',
'Beauty' => 'beauty',
'Cars and Motorcycles' => 'cars-and-motorcycles',
'Cats' => 'cats',
'Celebrities' => 'celebrities',
'Comics' => 'comics',
'DIY' => 'diy',
'Dogs' => 'dogs',
'Fitness' => 'fitness',
'Food and Drink' => 'food-and-drink',
'Funny' => 'funny',
'Gadgets' => 'gadgets',
'Gardening' => 'gardening',
'Geeky' => 'geeky',
'Hairstyles' => 'hairstyles',
'Home Decor' => 'home-decor',
'Marine Life' => 'marine-life',
'Men\'s Fashion' => 'men%27s-fashion',
'Nature' => 'nature',
'Outdoors' => 'outdoors',
'Parenting' => 'parenting',
'Phone Wallpapers' => 'phone-wallpapers',
'Photography' => 'photography',
'Quotes' => 'quotes',
'Recipes' => 'recipes',
'Snow' => 'snow',
'Tattoos' => 'tattoos',
'Travel' => 'travel',
'Video Games' => 'video-games',
'Weddings' => 'weddings',
'Women\'s Fashion' => 'women%27s-fashion',
);
public function getIcon()
{
return 'https://www.bing.com/sa/simg/bing_p_rr_teal_min.ico';
}
public function collectData()
{
$this->items = $this->imageDiscover($this->getInput('category'));
}
public function getName()
{
if ($this->getInput('category')) {
if (self::IMAGE_DISCOVER_CATEGORIES[$this->getInput('categories')] !== null) {
$category = self::IMAGE_DISCOVER_CATEGORIES[$this->getInput('categories')];
} else {
$category = 'Unknown';
}
return 'Best ' . $category . ' - Bing Image Discover';
}
return parent::getName();
}
private function imageDiscover($category)
{
$html = getSimpleHTMLDOM(self::URI . '/discover/' . $category)
or returnServerError('Could not request ' . self::NAME);
$sizeKey = $this->getInput('image_size');
$items = array();
foreach ($html->find('a.iusc') as $element) {
$data = json_decode(htmlspecialchars_decode($element->getAttribute('m')), true);
$item = array();
$item['title'] = basename(rtrim($data['imgurl'], '/'));
$item['uri'] = $data['imgurl'];
$item['content'] = '<a href="' . $data['imgurl'] . '">
<img src="' . $data[$sizeKey] . '" alt="' . $item['title'] . '"></a>
<p>Source: <a href="' . $this->curUrl($data['surl']) . '"> </a></p>';
$item['enclosures'] = $data['imgurl'];
$items[] = $item;
}
return $items;
}
private function curUrl($url)
{
if (strlen($url) <= 80) {
return $url;
}
return substr($url, 0, 80) . '...';
}
}

View File

@@ -13,8 +13,7 @@ class BlaguesDeMerdeBridge extends BridgeAbstract {
public function collectData(){
$html = getSimpleHTMLDOM(self::URI)
or returnServerError('Could not request BDM.');
$html = getSimpleHTMLDOM(self::URI);
foreach($html->find('div.blague') as $element) {

View File

@@ -44,8 +44,7 @@ class BrutBridge extends BridgeAbstract {
public function collectData() {
$html = getSimpleHTMLDOM($this->getURI())
or returnServerError('Could not request: ' . $this->getURI());
$html = getSimpleHTMLDOM($this->getURI());
$results = $html->find('div.results', 0);
@@ -54,8 +53,7 @@ class BrutBridge extends BridgeAbstract {
$videoPath = self::URI . $li->children(0)->href;
$videoPageHtml = getSimpleHTMLDOMCached($videoPath, 3600)
or returnServerError('Could not request: ' . $videoPath);
$videoPageHtml = getSimpleHTMLDOMCached($videoPath, 3600);
$this->videoImage = $videoPageHtml->find('meta[name="twitter:image"]', 0)->content;

View File

@@ -186,8 +186,7 @@ class BukowskisBridge extends BridgeAbstract
if ($sort_order)
$url = $url . '/sort/' . $sort_order;
$html = getSimpleHTMLDOM($url)
or returnServerError('Could not request: ' . $url);
$html = getSimpleHTMLDOM($url);
$this->title = htmlspecialchars_decode($html->find('title', 0)->innertext);

View File

@@ -41,8 +41,7 @@ class BundesbankBridge extends BridgeAbstract {
public function collectData() {
$html = getSimpleHTMLDOM($this->getURI())
or returnServerError('No response for ' . $this->getURI());
$html = getSimpleHTMLDOM($this->getURI());
$html = defaultLinkTo($html, $this->getURI());

View File

@@ -53,8 +53,7 @@ class CNETBridge extends BridgeAbstract {
// Retrieve webpage
$pageUrl = self::URI . (empty($topic) ? 'news/' : $topic . '/');
$html = getSimpleHTMLDOM($pageUrl)
or returnServerError('Could not request CNET: ' . $pageUrl);
$html = getSimpleHTMLDOM($pageUrl);
// Process articles
foreach($html->find('div.assetBody, div.riverPost') as $element) {

View File

@@ -0,0 +1,140 @@
<?php
// CVE Details is a collection of CVEs, taken from the National Vulnerability
// Database (NVD) and other sources like the Exploit DB and Metasploit. The
// website categorizes it by vendor and product and attach the CWE category.
// There is a Atom feed available, but only logged in users can use it,
// it is not reliable and contain no useful information. This bridge create a
// sane feed with additional information like tags and a link to the CWE
// a description of the vulnerability.
class CVEDetailsBridge extends BridgeAbstract {
const MAINTAINER = 'Aaron Fischer';
const NAME = 'CVE Details';
const CACHE_TIMEOUT = 60 * 60 * 6; // 6 hours
const DESCRIPTION = 'Report new CVE vulnerabilities for a given vendor (and product)';
const URI = 'https://www.cvedetails.com';
const PARAMETERS = array(array(
// The Vendor ID can be taken from the URL
'vendor_id' => array(
'name' => 'Vendor ID',
'type' => 'number',
'required' => true,
'exampleValue' => 74, // PHP
),
// The optional Product ID can be taken from the URL as well
'product_id' => array(
'name' => 'Product ID',
'type' => 'number',
'required' => false,
'exampleValue' => 128, // PHP
),
));
private $html = null;
private $vendor = '';
private $product = '';
// Return the URL to query.
// Because of the optional product ID, we need to attach it if it is
// set. The search result page has the exact same structure (with and
// without the product ID).
private function _buildURL() {
$url = self::URI . '/vulnerability-list/vendor_id-' . $this->getInput('vendor_id');
if ($this->getInput('product_id') !== '') {
$url .= '/product_id-' . $this->getInput('product_id');
}
// Sadly, there is no way (prove me wrong please) to sort the search
// result by publish date. So the nearest alternative is the CVE
// number, which should be mostly accurate.
$url .= '?order=1'; // Order by CVE number DESC
return $url;
}
// Make the actual request to cvedetails.com and stores the response
// (HTML) for later use and extract vendor and product from it.
private function _fetchContent() {
$html = getSimpleHTMLDOM($this->_buildURL());
$this->html = defaultLinkTo($html, self::URI);
$vendor = $html->find('#contentdiv > h1 > a', 0);
if ($vendor == null) {
returnServerError('Invalid Vendor ID ' .
$this->getInput('vendor_id') .
' or Product ID ' .
$this->getInput('product_id'));
}
$this->vendor = $vendor->innertext;
$product = $html->find('#contentdiv > h1 > a', 1);
if ($product != null) {
$this->product = $product->innertext;
}
}
// Build the name of the feed.
public function getName() {
if ($this->getInput('vendor_id') == '') {
return self::NAME;
}
if ($this->html == null) {
$this->_fetchContent();
}
$name = 'CVE Vulnerabilities for ' . $this->vendor;
if ($this->product != '') {
$name .= '/' . $this->product;
}
return $name;
}
// Pull the data from the HTML response and fill the items..
public function collectData() {
if ($this->html == null) {
$this->_fetchContent();
}
foreach ($this->html->find('#vulnslisttable .srrowns') as $i => $tr) {
// There are some optional vulnerability types, which will be
// added to the categories as well as the CWE number -- which is
// always given.
$categories = array($this->vendor);
$enclosures = array();
$cwe = $tr->find('td', 2)->find('a', 0);
if ($cwe != null) {
$cwe = $cwe->innertext;
$categories[] = 'CWE-' . $cwe;
$enclosures[] = 'https://cwe.mitre.org/data/definitions/' . $cwe . '.html';
}
$c = $tr->find('td', 4)->innertext;
if (trim($c) != '') {
$categories[] = $c;
}
if ($this->product != '') {
$categories[] = $this->product;
}
// The CVE number itself
$title = $tr->find('td', 1)->find('a', 0)->innertext;
$this->items[] = array(
'uri' => $tr->find('td', 1)->find('a', 0)->href,
'title' => $title,
'timestamp' => $tr->find('td', 5)->innertext,
'content' => $tr->next_sibling()->innertext,
'categories' => $categories,
'enclosures' => $enclosures,
'uid' => $tr->find('td', 1)->find('a', 0)->innertext,
);
// We only want to fetch the latest 10 CVEs
if (count($this->items) >= 10) {
break;
}
}
}
}

View File

@@ -13,7 +13,7 @@ class CeskaTelevizeBridge extends BridgeAbstract {
'url' => array(
'name' => 'url to the show',
'required' => true,
'exampleValue' => 'https://www.ceskatelevize.cz/porady/1097181328-udalosti/dily/'
'exampleValue' => 'https://www.ceskatelevize.cz/porady/1097181328-udalosti/'
)
)
);
@@ -38,7 +38,7 @@ class CeskaTelevizeBridge extends BridgeAbstract {
public function collectData() {
$url = $this->getInput('url');
$validUrl = '/^(https:\/\/www\.ceskatelevize\.cz\/porady\/\d+-[a-z0-9-]+\/)(dily\/((nove|vysilani)\/)?)?$/';
$validUrl = '/^(https:\/\/www\.ceskatelevize\.cz\/porady\/\d+-[a-z0-9-]+\/)(bonus\/)?$/';
if (!preg_match($validUrl, $url, $match)) {
returnServerError('Invalid url');
}
@@ -46,8 +46,7 @@ class CeskaTelevizeBridge extends BridgeAbstract {
$category = isset($match[4]) ? $match[4] : 'nove';
$fixedUrl = "{$match[1]}dily/{$category}/";
$html = getSimpleHTMLDOM($fixedUrl)
or returnServerError('Could not request Česká televize');
$html = getSimpleHTMLDOM($fixedUrl);
$this->feedUri = $fixedUrl;
$this->feedName = str_replace('Přehled dílů — ', '', $this->fixChars($html->find('title', 0)->plaintext));
@@ -55,17 +54,17 @@ class CeskaTelevizeBridge extends BridgeAbstract {
$this->feedName .= " ({$category})";
}
foreach ($html->find('.episodes-broadcast-content a.episode_list_item') as $element) {
$itemTitle = $element->find('.episode_list_item-title', 0);
$itemContent = $element->find('.episode_list_item-desc', 0);
$itemDate = $element->find('.episode_list_item-date', 0);
foreach ($html->find('#episodeListSection a[data-testid=next-link]') as $element) {
$itemTitle = $element->find('h3', 0);
$itemContent = $element->find('div[class^=content-]', 0);
$itemDate = $element->find('div[class^=playTime-] span', 0);
$itemThumbnail = $element->find('img', 0);
$itemUri = self::URI . $element->getAttribute('href');
$item = array(
'title' => $this->fixChars($itemTitle->plaintext),
'uri' => $itemUri,
'content' => '<img src="https:' . $itemThumbnail->getAttribute('src') . '" /><br />'
'content' => '<img src="' . $itemThumbnail->getAttribute('src') . '" /><br />'
. $this->fixChars($itemContent->plaintext),
'timestamp' => $this->getUploadTimeFromString($itemDate->plaintext)
);

387
bridges/CodebergBridge.php Normal file
View File

@@ -0,0 +1,387 @@
<?php
class CodebergBridge extends BridgeAbstract {
const NAME = 'Codeberg Bridge';
const URI = 'https://codeberg.org/';
const DESCRIPTION = 'Returns commits, issues, pull requests or releases for a repository.';
const MAINTAINER = 'VerifiedJoseph';
const PARAMETERS = array(
'Commits' => array(
'branch' => array(
'name' => 'branch',
'type' => 'text',
'exampleValue' => 'main',
'required' => false,
'title' => 'Optional, main branch is used by default.',
),
),
'Issues' => array(),
'Issue Comments' => array(
'issueId' => array(
'name' => 'Issue ID',
'type' => 'text',
'required' => true,
)
),
'Pull Requests' => array(),
'Releases' => array(),
'global' => array(
'username' => array(
'name' => 'Username',
'type' => 'text',
'exampleValue' => 'username',
'title' => 'Username of account that the repository belongs to.',
'required' => true,
),
'repo' => array(
'name' => 'Repository',
'type' => 'text',
'exampleValue' => 'repo',
'required' => true,
)
)
);
const CACHE_TIMEOUT = 1800;
const TEST_DETECT_PARAMETERS = array(
'https://codeberg.org/Codeberg/Community/issues/507' => array(
'context' => 'Issue Comments', 'username' => 'Codeberg', 'repo' => 'Community', 'issueId' => '507'
),
'https://codeberg.org/Codeberg/Community/issues' => array(
'context' => 'Issues', 'username' => 'Codeberg', 'repo' => 'Community'
),
'https://codeberg.org/Codeberg/Community/pulls' => array(
'context' => 'Pull Requests', 'username' => 'Codeberg', 'repo' => 'Community'
),
'https://codeberg.org/Codeberg/Community/releases' => array(
'context' => 'Releases', 'username' => 'Codeberg', 'repo' => 'Community'
),
'https://codeberg.org/Codeberg/Community/commits/branch/master' => array(
'context' => 'Commits', 'username' => 'Codeberg', 'repo' => 'Community', 'branch' => 'master'
),
'https://codeberg.org/Codeberg/Community/commits' => array(
'context' => 'Commits', 'username' => 'Codeberg', 'repo' => 'Community'
)
);
private $defaultBranch = 'main';
private $issueTitle = '';
private $urlRegex = '/codeberg\.org\/([\w]+)\/([\w]+)(?:\/commits\/branch\/([\w]+))?/';
private $issuesUrlRegex = '/codeberg\.org\/([\w]+)\/([\w]+)\/issues/';
private $pullsUrlRegex = '/codeberg\.org\/([\w]+)\/([\w]+)\/pulls/';
private $releasesUrlRegex = '/codeberg\.org\/([\w]+)\/([\w]+)\/releases/';
private $issueCommentsUrlRegex = '/codeberg\.org\/([\w]+)\/([\w]+)\/issues\/([0-9]+)/';
public function detectParameters($url) {
$params = array();
// Issue Comments
if(preg_match($this->issueCommentsUrlRegex, $url, $matches)) {
$params['context'] = 'Issue Comments';
$params['username'] = $matches[1];
$params['repo'] = $matches[2];
$params['issueId'] = $matches[3];
return $params;
}
// Issues
if(preg_match($this->issuesUrlRegex, $url, $matches)) {
$params['context'] = 'Issues';
$params['username'] = $matches[1];
$params['repo'] = $matches[2];
return $params;
}
// Pull Requests
if(preg_match($this->pullsUrlRegex, $url, $matches)) {
$params['context'] = 'Pull Requests';
$params['username'] = $matches[1];
$params['repo'] = $matches[2];
return $params;
}
// Releases
if(preg_match($this->releasesUrlRegex, $url, $matches)) {
$params['context'] = 'Releases';
$params['username'] = $matches[1];
$params['repo'] = $matches[2];
return $params;
}
// Commits
if(preg_match($this->urlRegex, $url, $matches)) {
$params['context'] = 'Commits';
$params['username'] = $matches[1];
$params['repo'] = $matches[2];
if (isset($matches[3])) {
$params['branch'] = $matches[3];
}
return $params;
}
return null;
}
public function collectData() {
$html = getSimpleHTMLDOM($this->getURI());
$html = defaultLinkTo($html, $this->getURI());
switch($this->queriedContext) {
case 'Commits':
$this->extractCommits($html);
break;
case 'Issues':
$this->extractIssues($html);
break;
case 'Issue Comments':
$this->extractIssueComments($html);
break;
case 'Pull Requests':
$this->extractPulls($html);
break;
case 'Releases':
$this->extractReleases($html);
break;
default:
returnClientError('Invalid context: ' . $this->queriedContext);
}
}
public function getName() {
switch($this->queriedContext) {
case 'Commits':
if ($this->getBranch() === $this->defaultBranch) {
return $this->getRepo() . ' Commits';
}
return $this->getRepo() . ' Commits (' . $this->getBranch() . ' branch) - ' . self::NAME;
case 'Issues':
return $this->getRepo() . ' Issues - ' . self::NAME;
case 'Issue Comments':
return $this->issueTitle . ' - Issue Comments - ' . self::NAME;
case 'Pull Requests':
return $this->getRepo() . ' Pull Requests - ' . self::NAME;
case 'Releases':
return $this->getRepo() . ' Releases - ' . self::NAME;
default:
return parent::getName();
}
}
public function getURI() {
switch($this->queriedContext) {
case 'Commits':
return self::URI . $this->getRepo() . '/commits/branch/' . $this->getBranch();
case 'Issues':
return self::URI . $this->getRepo() . '/issues/';
case 'Issue Comments':
return self::URI . $this->getRepo() . '/issues/' . $this->getInput('issueId');
case 'Pull Requests':
return self::URI . $this->getRepo() . '/pulls';
case 'Releases':
return self::URI . $this->getRepo() . '/releases';
default:
return parent::getURI();
}
}
private function getBranch() {
if ($this->getInput('branch')) {
return $this->getInput('branch');
}
return $this->defaultBranch;
}
private function getRepo() {
return $this->getInput('username') . '/' . $this->getInput('repo');
}
private function extractCommits($html) {
$table = $html->find('table#commits-table', 0);
$tbody = $table->find('tbody.commit-list', 0);
foreach ($tbody->find('tr') as $tr) {
$item = array();
$message = $tr->find('td.message', 0);
$item['title'] = $message->find('span.message-wrapper', 0)->plaintext;
$item['uri'] = $tr->find('td.sha', 0)->find('a', 0)->href;
$item['author'] = $tr->find('td.author', 0)->plaintext;
$item['timestamp'] = $tr->find('td', 3)->find('span', 0)->title;
if ($message->find('pre.commit-body', 0)) {
$message->find('pre.commit-body', 0)->style = '';
$item['content'] = $message->find('pre.commit-body', 0);
} else {
$item['content'] = '<blockquote>' . $item['title'] . '</blockquote>';
}
$this->items[] = $item;
}
}
private function extractIssues($html) {
$div = $html->find('div.repository', 0);
foreach ($div->find('li.item') as $li) {
$item = array();
$number = $li->find('div', 0)->plaintext;
$item['title'] = $li->find('a.title', 0)->plaintext . ' (' . $number . ')';
$item['uri'] = $li->find('a.title', 0)->href;
$item['timestamp'] = $li->find('p.desc', 0)->find('span', 0)->title;
$item['author'] = $li->find('p.desc', 0)->find('a', 0)->plaintext;
// Fetch issue page
$issuePage = getSimpleHTMLDOMCached($item['uri'], 3600);
$issuePage = defaultLinkTo($issuePage, self::URI);
$item['content'] = $issuePage->find('ui.timeline', 0)->find('div.render-content.markdown', 0);
foreach ($li->find('a.ui.label') as $label) {
$item['categories'][] = $label->plaintext;
}
$this->items[] = $item;
}
}
private function extractIssueComments($html) {
$this->issueTitle = $html->find('span#issue-title', 0)->plaintext
. ' (' . $html->find('span.index', 0)->plaintext . ')';
foreach ($html->find('ui.timeline > div.timeline-item.comment') as $div) {
$item = array();
if ($div->class === 'timeline-item comment merge box') {
continue;
}
$item['title'] = $this->ellipsisTitle($div->find('div.render-content.markdown', 0)->plaintext);
$item['uri'] = $div->find('span.text.grey', 0)->find('a', 1)->href;
$item['content'] = $div->find('div.render-content.markdown', 0);
if ($div->find('div.dropzone-attachments', 0)) {
$item['content'] .= $div->find('div.dropzone-attachments', 0);
}
$item['author'] = $div->find('a.author', 0)->innertext;
$item['timestamp'] = $div->find('span.time-since', 0)->title;
$this->items[] = $item;
}
}
private function extractPulls($html) {
$div = $html->find('div.repository', 0);
foreach ($div->find('li.item') as $li) {
$item = array();
$number = $li->find('div', 0)->plaintext;
$item['title'] = $li->find('a.title', 0)->plaintext . ' (' . $number . ')';
$item['uri'] = $li->find('a.title', 0)->href;
$item['timestamp'] = $li->find('p.desc', 0)->find('span', 0)->title;
$item['author'] = $li->find('p.desc', 0)->find('a', 0)->plaintext;
// Fetch pull request page
$pullRequestPage = getSimpleHTMLDOMCached($item['uri'], 3600);
$pullRequestPage = defaultLinkTo($pullRequestPage, self::URI);
$item['content'] = $pullRequestPage->find('ui.timeline', 0)->find('div.render-content.markdown', 0);
foreach ($li->find('a.ui.label') as $label) {
$item['categories'][] = $label->plaintext;
}
$this->items[] = $item;
}
}
private function extractReleases($html) {
$ul = $html->find('ul#release-list', 0);
foreach ($ul->find('li.ui.grid') as $li) {
$item = array();
if ($li->find('h3', 0)) { // Release
$item['title'] = $li->find('h3', 0)->plaintext;
$item['uri'] = $li->find('h3', 0)->find('a', 0)->href;
$tag = $li->find('span.tag', 0)->find('a', 0);
$commit = $li->find('span.commit', 0);
$downloads = $this->extractDownloads($li->find('div.download', 0));
$item['content'] = $li->find('div.markdown', 0);
$item['content'] .= <<<HTML
<strong>Tag</strong>
<p>{$tag}</p>
<strong>Commit</strong>
<p>{$commit}</p>
{$downloads}
HTML;
$item['timestamp'] = $li->find('span.time', 0)->find('span', 0)->title;
$item['author'] = $li->find('span.author', 0)->find('a', 0)->plaintext;
}
if ($li->find('h4', 0)) { // Tag
$item['title'] = $li->find('h4', 0)->plaintext;
$item['uri'] = $li->find('h4', 0)->find('a', 0)->href;
$item['content'] = <<<HTML
<strong>Commit</strong>
<p>{$li->find('div.download', 0)->find('a', 0)}</p>
HTML;
$item['content'] .= $this->extractDownloads($li->find('div.download', 0), true);
$item['timestamp'] = $li->find('span.time', 0)->find('span', 0)->title;
}
$this->items[] = $item;
}
}
private function extractDownloads($html, $skipFirst = false) {
$downloads = '';
foreach ($html->find('a') as $index => $a) {
if ($skipFirst === true && $index === 0) {
continue;
}
$downloads .= <<<HTML
{$a}<br>
HTML;
}
return <<<EOD
<strong>Downloads</strong>
<p>{$downloads}</p>
EOD;
}
private function ellipsisTitle($text) {
$length = 100;
if (strlen($text) > $length) {
$text = explode('<br>', wordwrap($text, $length, '<br>'));
return $text[0] . '...';
}
return $text;
}
}

View File

@@ -34,8 +34,7 @@ class CollegeDeFranceBridge extends BridgeAbstract {
* </li>
*/
$html = getSimpleHTMLDOM(self::URI
. 'components/search-audiovideo.jsp?fulltext=&siteid=1156951719600&lang=FR&type=all')
or returnServerError('Could not request CollegeDeFrance.');
. 'components/search-audiovideo.jsp?fulltext=&siteid=1156951719600&lang=FR&type=all');
foreach($html->find('a[data-target]') as $element) {
$item = array();

View File

@@ -7,8 +7,11 @@ class ComboiosDePortugalBridge extends BridgeAbstract {
const MAINTAINER = 'somini';
public function collectData() {
$html = getSimpleHTMLDOM($this->getURI() . '/consultar-horarios/avisos')
or returnServerError('Could not load content');
# Do not verify SSL certificate (the server doesn't send the intermediate)
# https://github.com/RSS-Bridge/rss-bridge/issues/2397
$html = getSimpleHTMLDOM($this->getURI() . '/consultar-horarios/avisos', array(), array(
CURLOPT_SSL_VERIFYPEER => 0,
));
foreach($html->find('.warnings-table a') as $element) {
$item = array();

View File

@@ -15,20 +15,17 @@ class ComicsKingdomBridge extends BridgeAbstract {
));
public function collectData(){
$html = getSimpleHTMLDOM($this->getURI(), array(), array(), true, false)
or returnServerError('Could not request Comics Kingdom: ' . $this->getURI());
$html = getSimpleHTMLDOM($this->getURI(), array(), array(), true, false);
// Get author from first page
$author = $html->find('div.author p', 0)->plaintext
or returnServerError('Comics Kingdom comic does not exist: ' . $this->getURI());;
$author = $html->find('div.author p', 0);;
// Get current date/link
$link = $html->find('meta[property=og:url]', 0)->content;
for($i = 0; $i < 5; $i++) {
$item = array();
$page = getSimpleHTMLDOM($link)
or returnServerError('Could not request Comics Kingdom: ' . $link);
$page = getSimpleHTMLDOM($link);
$imagelink = $page->find('meta[property=og:image]', 0)->content;
$prevSlug = $page->find('slider-arrow[:is-left-arrow=true]', 0);

View File

@@ -26,8 +26,7 @@ class ContainerLinuxReleasesBridge extends BridgeAbstract {
);
private function getReleaseFeed($jsonUrl) {
$json = getContents($jsonUrl)
or returnServerError('Could not request Core OS Website.');
$json = getContents($jsonUrl);
return json_decode($json, true);
}

View File

@@ -8,8 +8,7 @@ class CopieDoubleBridge extends BridgeAbstract {
const DESCRIPTION = 'CopieDouble';
public function collectData(){
$html = getSimpleHTMLDOM(self::URI)
or returnServerError('Could not request CopieDouble.');
$html = getSimpleHTMLDOM(self::URI);
$table = $html->find('table table', 2);

View File

@@ -1,55 +1,27 @@
<?php
class CourrierInternationalBridge extends BridgeAbstract {
class CourrierInternationalBridge extends FeedExpander {
const MAINTAINER = 'teromene';
const NAME = 'Courrier International Bridge';
const URI = 'https://www.courrierinternational.com/';
const CACHE_TIMEOUT = 300; // 5 min
const DESCRIPTION = 'Courrier International bridge';
const DESCRIPTION = 'Returns the newest articles';
public function collectData(){
$html = getSimpleHTMLDOM(self::URI)
or returnServerError('Error.');
$this->collectExpandableDatas(static::URI . 'feed/all/rss.xml', 20);
}
$element = $html->find('article');
$article_count = 1;
protected function parseItem($feedItem){
$item = parent::parseItem($feedItem);
foreach($element as $article) {
$item = array();
$item['uri'] = $article->parent->getAttribute('href');
if(strpos($item['uri'], 'http') === false) {
$item['uri'] = self::URI . $item['uri'];
}
$page = getSimpleHTMLDOMCached($item['uri']);
$content = $page->find('.article-text', 0);
if(!$content) {
$content = $page->find('.depeche-text', 0);
}
$item['content'] = sanitize($content);
$item['title'] = strip_tags($article->find('.title', 0));
$dateTime = date_parse($page->find('time', 0));
$item['timestamp'] = mktime(
$dateTime['hour'],
$dateTime['minute'],
$dateTime['second'],
$dateTime['month'],
$dateTime['day'],
$dateTime['year']
);
$this->items[] = $item;
$article_count ++;
if($article_count > 5)
break;
$articlePage = getSimpleHTMLDOMCached($feedItem->link);
$content = $articlePage->find('.article-text', 0);
if(!$content) {
$content = $articlePage->find('.depeche-text', 0);
}
$item['content'] = sanitize($content);
return $item;
}
}

View File

@@ -17,8 +17,7 @@ class CryptomeBridge extends BridgeAbstract {
));
public function collectData(){
$html = getSimpleHTMLDOM(self::URI)
or returnServerError('Could not request Cryptome.');
$html = getSimpleHTMLDOM(self::URI);
$number = $this->getInput('n');

View File

@@ -19,8 +19,7 @@ class CuriousCatBridge extends BridgeAbstract {
$url = self::URI . '/api/v2/profile?username=' . urlencode($this->getInput('username'));
$apiJson = getContents($url)
or returnServerError('Could not request: ' . $url);
$apiJson = getContents($url);
$apiData = json_decode($apiJson, true);

View File

@@ -46,8 +46,7 @@ class DailymotionBridge extends BridgeAbstract {
if ($this->queriedContext === 'By username' || $this->queriedContext === 'By playlist id') {
$apiJson = getContents($this->getApiUrl())
or returnServerError('Could not request: ' . $this->getApiUrl());
$apiJson = getContents($this->getApiUrl());
$apiData = json_decode($apiJson, true);
@@ -72,8 +71,7 @@ class DailymotionBridge extends BridgeAbstract {
if ($this->queriedContext === 'From search results') {
$html = getSimpleHTMLDOM($this->getURI())
or returnServerError('Could not request Dailymotion.');
$html = getSimpleHTMLDOM($this->getURI());
foreach($html->find('div.media a.preview_link') as $element) {
$item = array();
@@ -180,8 +178,7 @@ class DailymotionBridge extends BridgeAbstract {
$url = self::URI . 'playlist/' . $id;
$html = getSimpleHTMLDOM($url)
or returnServerError('Could not request: ' . $url);
$html = getSimpleHTMLDOM($url);
$title = $html->find('meta[property=og:title]', 0)->getAttribute('content');
return $title;

View File

@@ -57,8 +57,7 @@ class DanbooruBridge extends BridgeAbstract {
}
public function collectData(){
$content = getContents($this->getFullURI())
or returnServerError('Could not request ' . $this->getName());
$content = getContents($this->getFullURI());
$html = Fix_Simple_Html_Dom::str_get_html($content);

View File

@@ -9,8 +9,7 @@ class DansTonChatBridge extends BridgeAbstract {
public function collectData(){
$html = getSimpleHTMLDOM(self::URI . 'latest.html')
or returnServerError('Could not request DansTonChat.');
$html = getSimpleHTMLDOM(self::URI . 'latest.html');
foreach($html->find('div.item') as $element) {
$item = array();

View File

@@ -48,22 +48,24 @@ class DarkReadingBridge extends FeedExpander {
if ($feed_id != '000') {
$feed_url .= '?f_n=' . $feed_id . '&f_ln=' . $feed_name;
}
$this->collectExpandableDatas($feed_url);
$this->collectExpandableDatas($feed_url, 20);
}
protected function parseItem($newsItem){
$item = parent::parseItem($newsItem);
if (empty($item['content']))
return null; //ignore dummy articles
$article = getSimpleHTMLDOMCached($item['uri'])
or returnServerError('Could not request Dark Reading: ' . $item['uri']);
$article = getSimpleHTMLDOMCached($item['uri']);
$item['content'] = $this->extractArticleContent($article);
$item['enclosures'] = array(); //remove author profile picture
$image = $article->find('meta[property="og:image"]', 0);
if (is_object($image)) {
$image = $image->content;
$item['enclosures'] = array($image);
}
return $item;
}
private function extractArticleContent($article){
$content = $article->find('div#article-main', 0)->innertext;
$content = $article->find('div.article-content', 0)->innertext;
foreach (array(
'<div class="divsplitter',
@@ -74,8 +76,6 @@ class DarkReadingBridge extends FeedExpander {
$content = stripRecursiveHTMLSection($content, 'div', $div_start);
}
$content = stripWithDelimiters($content, '<h1 ', '</h1>');
return $content;
}
}

View File

@@ -9,8 +9,7 @@ class DaveRamseyBlogBridge extends BridgeAbstract {
public function collectData()
{
$html = getSimpleHTMLDOM(self::URI)
or returnServerError('Could not request daveramsey.com.');
$html = getSimpleHTMLDOM(self::URI);
foreach ($html->find('.Post') as $element) {
$this->items[] = array(

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
class DerpibooruBridge extends BridgeAbstract {
const NAME = 'Derpibooru Bridge';
const URI = 'https://derpibooru.org/';
const DESCRIPTION = 'Returns newest posts from a Derpibooru search';
const DESCRIPTION = 'Returns newest images from a Derpibooru search';
const CACHE_TIMEOUT = 300; // 5min
const MAINTAINER = 'Roliga';
@@ -73,40 +73,42 @@ class DerpibooruBridge extends BridgeAbstract {
public function collectData(){
$queryJson = json_decode(getContents(
self::URI
. 'search.json?filter_id='
. 'api/v1/json/search/images?filter_id='
. urlencode($this->getInput('f'))
. '&q='
. urlencode($this->getInput('q'))
)) or returnServerError('Failed to query Derpibooru');
));
foreach($queryJson->search as $post) {
foreach($queryJson->images as $post) {
$item = array();
$postUri = self::URI . $post->id;
$item['uri'] = $postUri;
$item['title'] = $post->id;
$item['title'] = $post->name;
$item['timestamp'] = strtotime($post->created_at);
$item['author'] = $post->uploader;
$item['enclosures'] = array('https:' . $post->image);
$item['categories'] = explode(', ', $post->tags);
$item['enclosures'] = array($post->view_url);
$item['categories'] = $post->tags;
$item['content'] = '<p><a href="' // image preview
. $postUri
. '"><img src="https:'
. '"><img src="'
. $post->representations->medium
. '"></a></p><p>' // description
. $post->description
. '</p><p><b>Size:</b> ' // image size
. $post->width
. 'x'
. $post->height
. '<br><b>Source:</b> <a href="' // source link
. $post->height;
// source link
if ($post->source_url != null) {
$item['content'] .= '<br><b>Source:</b> <a href="'
. $post->source_url
. '">'
. $post->source_url
. '</a></p>';
};
$this->items[] = $item;
}
}

View File

@@ -155,8 +155,7 @@ class DesoutterBridge extends BridgeAbstract {
}
*/
$html = getSimpleHTMLDOM($this->getURI())
or returnServerError('Could not request ' . $this->getURI());
$html = getSimpleHTMLDOM($this->getURI());
$html = defaultLinkTo($html, $this->getURI());
@@ -184,8 +183,7 @@ class DesoutterBridge extends BridgeAbstract {
}
private function getFullNewsArticle($uri) {
$html = getSimpleHTMLDOMCached($uri)
or returnServerError('Unable to load full article!');
$html = getSimpleHTMLDOMCached($uri);
$html = defaultLinkTo($html, $this->getURI());
@@ -199,8 +197,7 @@ class DesoutterBridge extends BridgeAbstract {
* @return void
*/
private function extractNewsLanguages() {
$html = getSimpleHTMLDOMCached('https://www.desouttertools.com/about-desoutter/news-events')
or returnServerError('Error loading news!');
$html = getSimpleHTMLDOMCached('https://www.desouttertools.com/about-desoutter/news-events');
$html = defaultLinkTo($html, static::URI);
@@ -225,8 +222,7 @@ class DesoutterBridge extends BridgeAbstract {
* @return void
*/
private function extractIndustryLanguages() {
$html = getSimpleHTMLDOMCached('https://www.desouttertools.com/industry-4-0/news')
or returnServerError('Error loading news!');
$html = getSimpleHTMLDOMCached('https://www.desouttertools.com/industry-4-0/news');
$html = defaultLinkTo($html, static::URI);

View File

@@ -45,8 +45,7 @@ apple-icon-5c6fa9f2bce280428589c6195b7f1924206a53b782b371cfe2d02da932c8c173.png'
}
public function collectData() {
$html = getSimpleHTMLDOMCached($this->getURI())
or returnServerError('Could not request ' . $this->getURI());
$html = getSimpleHTMLDOMCached($this->getURI());
$html = defaultLinkTo($html, static::URI);
@@ -95,8 +94,7 @@ EOD;
}
private function getFullArticle($url) {
$html = getSimpleHTMLDOMCached($url)
or returnServerError('Unable to load article from "' . $url . '"!');
$html = getSimpleHTMLDOMCached($url);
$html = defaultLinkTo($html, static::URI);

View File

@@ -56,8 +56,7 @@ class DiarioDeNoticiasBridge extends BridgeAbstract {
public function collectData() {
$archives = self::getURI();
$html = getSimpleHTMLDOMCached($archives)
or returnServerError('Could not load content');
$html = getSimpleHTMLDOMCached($archives);
foreach($html->find('article') as $element) {
$item = array();

View File

@@ -27,8 +27,7 @@ class DiarioDoAlentejoBridge extends BridgeAbstract {
public function collectData(){
/* This is slow as molasses (>30s!), keep the cache timeout high to avoid killing the host */
$html = getSimpleHTMLDOMCached($this->getURI() . '/pt/noticias-listagem.aspx')
or returnServerError('Could not load content');
$html = getSimpleHTMLDOMCached($this->getURI() . '/pt/noticias-listagem.aspx');
foreach($html->find('.list_news .item') as $element) {
$item = array();

View File

@@ -97,8 +97,7 @@ class DiceBridge extends BridgeAbstract {
$uri .= '&telecommute=true';
}
$html = getSimpleHTMLDOM($uri)
or returnServerError('Could not request Dice.');
$html = getSimpleHTMLDOM($uri);
foreach($html->find('div.complete-serp-result-div') as $element) {
$item = array();
// Title

View File

@@ -9,8 +9,7 @@ class DilbertBridge extends BridgeAbstract {
public function collectData(){
$html = getSimpleHTMLDOM(self::URI)
or returnServerError('Could not request Dilbert: ' . self::URI);
$html = getSimpleHTMLDOM(self::URI);
foreach($html->find('section.comic-item') as $element) {

View File

@@ -44,13 +44,11 @@ class DiscogsBridge extends BridgeAbstract {
if(!empty($this->getInput('artistid'))) {
$data = getContents('https://api.discogs.com/artists/'
. $this->getInput('artistid')
. '/releases?sort=year&sort_order=desc')
or returnServerError('Unable to query discogs !');
. '/releases?sort=year&sort_order=desc');
} elseif(!empty($this->getInput('labelid'))) {
$data = getContents('https://api.discogs.com/labels/'
. $this->getInput('labelid')
. '/releases?sort=year&sort_order=desc')
or returnServerError('Unable to query discogs !');
. '/releases?sort=year&sort_order=desc');
}
$jsonData = json_decode($data, true);
@@ -76,8 +74,7 @@ class DiscogsBridge extends BridgeAbstract {
if(!empty($this->getInput('username_wantlist'))) {
$data = getContents('https://api.discogs.com/users/'
. $this->getInput('username_wantlist')
. '/wants?sort=added&sort_order=desc')
or returnServerError('Unable to query discogs !');
. '/wants?sort=added&sort_order=desc');
$jsonData = json_decode($data, true)['wants'];
} elseif(!empty($this->getInput('username_folder'))) {
@@ -85,8 +82,7 @@ class DiscogsBridge extends BridgeAbstract {
. $this->getInput('username_folder')
. '/collection/folders/'
. $this->getInput('folderid')
. '/releases?sort=added&sort_order=desc')
or returnServerError('Unable to query discogs !');
. '/releases?sort=added&sort_order=desc');
$jsonData = json_decode($data, true)['releases'];
}
foreach($jsonData as $element) {

View File

@@ -57,8 +57,7 @@ class DockerHubBridge extends BridgeAbstract {
}
public function collectData() {
$json = getContents($this->getApiUrl())
or returnServerError('Could not request: ' . $this->getURI());
$json = getContents($this->getApiUrl());
$data = json_decode($json, false);

View File

@@ -40,8 +40,7 @@ class DonnonsBridge extends BridgeAbstract {
private function collectDataByPage($page) {
$uri = $this->getPageURI($page);
$html = getSimpleHTMLDOM($uri)
or returnServerError('No results for this query.');
$html = getSimpleHTMLDOM($uri);
$searchDiv = $html->find('div[id=search]', 0);

View File

@@ -13,8 +13,7 @@ favicon-63b2904a073c89b52b19aa08cebc16a154bcf83fee8ecc6439968b1e6db569c7.ico';
}
public function collectData(){
$html = getSimpleHTMLDOM(self::URI)
or returnServerError('Error while downloading the website content');
$html = getSimpleHTMLDOM(self::URI);
$json = $this->loadEmbeddedJsonData($html);

205
bridges/Drive2ruBridge.php Normal file
View File

@@ -0,0 +1,205 @@
<?php
class Drive2ruBridge extends BridgeAbstract {
const MAINTAINER = 'dotter-ak';
const NAME = 'Drive2.ru';
const URI = 'https://drive2.ru/';
const DESCRIPTION = 'Лента новостей и тестдрайвов, бортжурналов по выбранной марке или модели
(также работает с фильтром по категориям), блогов пользователей и публикаций по темам.';
const PARAMETERS = array(
'Новости и тест-драйвы' => array(),
'Бортжурналы (По модели или марке)' => array(
'url' => array(
'name' => 'Ссылка на страницу с бортжурналом',
'type' => 'text',
'required' => true,
'title' => 'Например: https://www.drive2.ru/experience/suzuki/g4895/',
'exampleValue' => 'https://www.drive2.ru/experience/suzuki/g4895/'
),
),
'Личные блоги' => array(
'username' => array(
'name' => 'Никнейм пользователя на сайте',
'type' => 'text',
'required' => true,
'title' => 'Например: Mickey',
'exampleValue' => 'Mickey'
)
),
'Публикации по темам (Стоит почитать)' => array(
'topic' => array(
'name' => 'Темы',
'type' => 'list',
'values' => array(
'Автозвук' => '16',
'Автомобильный дизайн' => '10',
'Автоспорт' => '11',
'Автошоу, музеи, выставки' => '12',
'Безопасность' => '18',
'Беспилотные автомобили' => '15',
'Видеосюжеты' => '20',
'Вне дорог' => '21',
'Встречи' => '22',
'Выбор и покупка машины' => '23',
'Гаджеты' => '30',
'Гибридные машины' => '32',
'Грузовики, автобусы, спецтехника' => '31',
'Доработка интерьера' => '35',
'Законодательство' => '40',
'История автомобилестроения' => '50',
'Мототехника' => '60',
'Новые модели и концепты' => '85',
'Обучение вождению' => '70',
'Путешествия' => '80',
'Ремонт и обслуживание' => '90',
'Реставрация ретро-авто' => '91',
'Сделай сам' => '104',
'Смешное' => '103',
'Спорткары' => '102',
'Стайлинг' => '101',
'Тест-драйвы' => '110',
'Тюнинг' => '111',
'Фотосессии' => '120',
'Шины и диски' => '140',
'Электрика' => '130',
'Электромобили' => '131'
),
'defaultValue' => '16',
)
),
'global' => array(
'full_articles' => array(
'name' => 'Загружать в ленту полный текст',
'type' => 'checkbox'
)
)
);
private $title;
private function getUserContent($url) {
$html = getSimpleHTMLDOM($url);
$this->title = $html->find('title', 0)->innertext;
$articles = $html->find('div.js-entity');
foreach ($articles as $article) {
$item = array();
$item['title'] = $article->find('a.c-link--text', 0)->plaintext;
$item['uri'] = urljoin(self::URI, $article->find('a.c-link--text', 0)->href);
if($this->getInput('full_articles')) {
$item['content'] = $this->addCommentsLink(
$this->adjustContent(getSimpleHTMLDomCached($item['uri'])->find('div.c-post__body', 0))->innertext,
$item['uri']
);
} else {
$item['content'] = $this->addReadMoreLink($article->find('div.c-post-preview__lead', 0), $item['uri']);
}
$item['author'] = $article->find('a.c-username--wrap', 0)->plaintext;
if (!is_null($article->find('img', 1))) $item['enclosures'][] = $article->find('img', 1)->src;
$this->items[] = $item;
}
}
private function getLogbooksContent($url) {
$html = getSimpleHTMLDOM($url);
$this->title = $html->find('title', 0)->innertext;
$articles = $html->find('div.js-entity');
foreach ($articles as $article) {
$item = array();
$item['title'] = $article->find('a.c-link--text', 1)->plaintext;
$item['uri'] = urljoin(self::URI, $article->find('a.c-link--text', 1)->href);
if($this->getInput('full_articles')) {
$item['content'] = $this->addCommentsLink(
$this->adjustContent(getSimpleHTMLDomCached($item['uri'])->find('div.c-post__body', 0))->innertext,
$item['uri']
);
} else {
$item['content'] = $this->addReadMoreLink($article->find('div.c-post-preview__lead', 0), $item['uri']);
}
$item['author'] = $article->find('a.c-username--wrap', 0)->plaintext;
if (!is_null($article->find('img', 1))) $item['enclosures'][] = $article->find('img', 1)->src;
$this->items[] = $item;
}
}
private function getNews() {
$html = getSimpleHTMLDOM('https://www.drive2.ru/editorial/');
$this->title = $html->find('title', 0)->innertext;
$articles = $html->find('div.c-article-card');
foreach ($articles as $article) {
$item = array();
$item['title'] = $article->find('a.c-link--text', 0)->plaintext;
$item['uri'] = urljoin(self::URI, $article->find('a.c-link--text', 0)->href);
if($this->getInput('full_articles')) {
$item['content'] = $this->addCommentsLink(
$this->adjustContent(getSimpleHTMLDomCached($item['uri'])->find('div.article', 0))->innertext,
$item['uri']
);
} else {
$item['content'] = $this->addReadMoreLink($article->find('div.c-article-card__lead', 0), $item['uri']);
}
$item['author'] = 'Новости и тест-драйвы на Drive2.ru';
if (!is_null($article->find('img', 0))) $item['enclosures'][] = $article->find('img', 0)->src;
$this->items[] = $item;
}
}
private function adjustContent($content) {
foreach ($content->find('div.o-group') as $node)
$node->outertext = '';
foreach($content->find('div, span') as $attrs)
foreach ($attrs->getAllAttributes() as $attr => $val)
$attrs->removeAttribute($attr);
foreach ($content->getElementsByTagName('figcaption') as $attrs)
$attrs->setAttribute(
'style',
'font-style: italic; font-size: small; margin: 0 100px 75px;');
foreach ($content->find('script') as $node)
$node->outertext = '';
foreach ($content->find('iframe') as $node) {
preg_match('/embed\/(.*?)\?/', $node->src, $match);
$node->outertext = '<a href="https://www.youtube.com/watch?v=' . $match[1] .
'">https://www.youtube.com/watch?v=' . $match[1] . '</a>';
}
return $content;
}
private function addCommentsLink ($content, $url) {
return $content . '<br><a href="' . $url . '#comments">Перейти к комментариям</a>';
}
private function addReadMoreLink ($content, $url) {
if (!is_null($content))
return preg_replace('!\s+!', ' ', str_replace('Читать дальше', '', $content->plaintext)) .
'<br><a href="' . $url . '">Читать далее</a>';
else return '';
}
public function collectData() {
switch($this->queriedContext) {
default:
case 'Новости и тест-драйвы':
$this->getNews();
break;
case 'Бортжурналы (По модели или марке)':
if (!preg_match('/^https:\/\/www.drive2.ru\/experience/', $this->getInput('url')))
returnServerError('Invalid url');
$this->getLogbooksContent($this->getInput('url'));
break;
case 'Личные блоги':
if (!preg_match('/^[a-zA-Z0-9-]{3,16}$/', $this->getInput('username')))
returnServerError('Invalid username');
$this->getUserContent('https://www.drive2.ru/users/' . $this->getInput('username'));
break;
case 'Публикации по темам (Стоит почитать)':
$this->getUserContent('https://www.drive2.ru/topics/' . $this->getInput('topic'));
break;
}
}
public function getName() {
return $this->title ?: parent::getName();
}
public function getIcon() {
return 'https://www.drive2.ru/favicon.ico';
}
}

View File

@@ -28,14 +28,13 @@ class DuckDuckGoBridge extends BridgeAbstract {
));
public function collectData(){
$html = getSimpleHTMLDOM(self::URI . 'html/?kd=-1&q=' . $this->getInput('u') . $this->getInput('sort'))
or returnServerError('Could not request DuckDuckGo.');
$html = getSimpleHTMLDOM(self::URI . 'html/?kd=-1&q=' . $this->getInput('u') . $this->getInput('sort'));
foreach($html->find('div.results_links') as $element) {
foreach($html->find('div.result') as $element) {
$item = array();
$item['uri'] = $element->find('a', 0)->href;
$item['title'] = $element->find('a', 1)->innertext;
$item['content'] = $element->find('div.snippet', 0)->plaintext;
$item['uri'] = $element->find('a.result__a', 0)->href;
$item['title'] = $element->find('h2.result__title', 0)->plaintext;
$item['content'] = $element->find('a.result__snippet', 0)->plaintext;
$this->items[] = $item;
}
}

View File

@@ -107,8 +107,7 @@ class ETTVBridge extends BridgeAbstract {
// Get results page
$this->results_link = self::URI . $query_str;
$html = getSimpleHTMLDOM($this->results_link)
or returnServerError('Could not request ' . $this->getName());
$html = getSimpleHTMLDOM($this->results_link);
// Loop on each entry
foreach($html->find('table.table tr') as $element) {
@@ -117,8 +116,7 @@ class ETTVBridge extends BridgeAbstract {
// retrieve result page to get more details
$link = rtrim(self::URI, '/') . $entry->href;
$page = getSimpleHTMLDOM($link)
or returnServerError('Could not request page ' . $link);
$page = getSimpleHTMLDOM($link);
// get details & download links
$details = $page->find('fieldset.download table', 0); // WHAT?? It should be the second one…

View File

@@ -35,8 +35,7 @@ on EZTV. Get showID from URLs in https://eztv.ch/shows/showID/show-full-name.';
foreach($showList as $showID) {
// Get show page
$html = getSimpleHTMLDOM(self::URI . 'shows/' . rawurlencode($showID) . '/')
or returnServerError('Could not request EZTV for id "' . $showID . '"');
$html = getSimpleHTMLDOM(self::URI . 'shows/' . rawurlencode($showID) . '/');
// Loop on each element that look like an episode entry...
foreach($html->find('.forum_header_border') as $element) {

View File

@@ -1,70 +1,128 @@
<?php
class EconomistBridge extends BridgeAbstract {
const NAME = 'The Economist: Latest Updates';
const URI = 'https://www.economist.com';
const DESCRIPTION = 'Fetches the latest updates from the Economist.';
const MAINTAINER = 'thefranke';
const CACHE_TIMEOUT = 3600; // 1h
class EconomistBridge extends FeedExpander {
public function getIcon() {
return 'https://www.economist.com/sites/default/files/econfinal_favicon.ico';
const MAINTAINER = 'bockiii';
const NAME = 'Economist Bridge';
const URI = 'https://www.economist.com/';
const CACHE_TIMEOUT = 3600; //1hour
const DESCRIPTION = 'Returns the latest articles for the selected category';
const PARAMETERS = array(
'global' => array(
'limit' => array(
'name' => 'Feed Item Limit',
'required' => true,
'type' => 'number',
'defaultValue' => 10,
'title' => 'Maximum number of returned feed items. Maximum 30, default 10'
)
),
'Topics' => array(
'topic' => array(
'name' => 'Topics',
'type' => 'list',
'title' => 'Select a Topic',
'defaultValue' => 'latest',
'values' => array(
'Latest' => 'latest',
'The world this week' => 'the-world-this-week',
'Letters' => 'letters',
'Leaders' => 'leaders',
'Briefings' => 'briefing',
'Special reports' => 'special-report',
'Britain' => 'britain',
'Europe' => 'europe',
'United States' => 'united-states',
'The Americas' => 'the-americas',
'Middle East and Africa' => 'middle-east-and-africa',
'Asia' => 'asia',
'China' => 'china',
'International' => 'international',
'Business' => 'business',
'Finance and economics' => 'finance-and-economics',
'Science and technology' => 'science-and-technology',
'Books and arts' => 'books-and-arts',
'Obituaries' => 'obituary',
'Graphic detail' => 'graphic-detail',
'Indicators' => 'economic-and-financial-indicators',
)
)
),
'Blogs' => array(
'blog' => array(
'name' => 'Blogs',
'type' => 'list',
'title' => 'Select a Blog',
'values' => array(
'Bagehots notebook' => 'bagehots-notebook',
'Bartleby' => 'bartleby',
'Buttonwoods notebook' => 'buttonwoods-notebook',
'Charlemagnes notebook' => 'charlemagnes-notebook',
'Democracy in America' => 'democracy-in-america',
'Erasmus' => 'erasmus',
'Free exchange' => 'free-exchange',
'Game theory' => 'game-theory',
'Gulliver' => 'gulliver',
'Kaffeeklatsch' => 'kaffeeklatsch',
'Prospero' => 'prospero',
'The Economist Explains' => 'the-economist-explains',
)
)
)
);
public function collectData(){
// get if topics or blogs were selected and store the selected category
switch ($this->queriedContext) {
case 'Topics':
$category = $this->getInput('topic');
break;
case 'Blogs':
$category = $this->getInput('blog');
break;
default:
$category = 'latest';
}
// limit the returned articles to 30 at max
if ((int)$this->getInput('limit') <= 30) {
$limit = (int)$this->getInput('limit');
} else {
$limit = 30;
}
$this->collectExpandableDatas('https://www.economist.com/' . $category . '/rss.xml', $limit);
}
public function collectData() {
$html = getSimpleHTMLDOM(self::URI . '/latest/')
or returnServerError('Could not fetch latest updates form The Economist.');
protected function parseItem($feedItem){
$item = parent::parseItem($feedItem);
foreach($html->find('div.teaser') as $element) {
$a = $element->find('a.headline-link', 0);
$href = $a->href;
if (substr($href, 0, 4) != 'http')
$href = self::URI . $a->href;
$full = getSimpleHTMLDOMCached($href);
$article = $full->find('article', 0);
$header = $article->find('span[itemprop="headline"]', 0);
$headerimg = $article->find('div[itemprop="image"]', 0)->find('img', 0);
$author = $article->find('p[itemprop="byline"]', 0);
$time = $article->find('time', 0);
$content = $article->find('div[itemprop="text"]', 0);
$section = array( $article->find('strong[itemprop="articleSection"]', 0)->plaintext );
// Author
if ($author)
$author = substr($author->innertext, 3, strlen($author));
else
$author = 'The Economist';
// Remove newsletter subscription box
$newsletter = $content->find('div[class="newsletter-form__message"]', 0);
if ($newsletter)
$newsletter->outertext = '';
$newsletterForm = $content->find('form', 0);
if ($newsletterForm)
$newsletterForm->outertext = '';
// Remove next and previous article URLs at the bottom
$nextprev = $content->find('div[class="blog-post__next-previous-wrapper"]', 0);
if ($nextprev)
$nextprev->outertext = '';
$item = array();
$item['title'] = $header->innertext;
$item['uri'] = $href;
$item['timestamp'] = strtotime($time->datetime);
$item['author'] = $author;
$item['categories'] = $section;
$item['content'] = '<img style="max-width: 100%" src="'
. $headerimg->src . '">' . $content->innertext;
$this->items[] = $item;
if (count($this->items) >= 10)
break;
$article = getSimpleHTMLDOM($item['uri']);
// before the article can be added, it needs to be cleaned up, thus, the extra function
$item['content'] = $this->cleanContent($article);
// only the article lead image is retained if it's there
if (!is_null($article->find('div.article__lead-image', 0))) {
$item['enclosures'][] = $article->find('div.article__lead-image', 0)->find('img', 0)->getAttribute('src');
} else {
$item['enclosures'][] = '';
}
return $item;
}
private function cleanContent($article){
// the actual article is in this div
$content = $article->find('div.layout-article-body', 0)->innertext;
// clean the article content. Remove all div's since the text is in paragraph elements
foreach (array(
'<div '
) as $tag_start) {
$content = stripRecursiveHTMLSection($content, 'div', $tag_start);
}
// now remove embedded iframes. The podcast postings contain these for example
$content = preg_replace('/<iframe.*?\/iframe>/i', '', $content);
// fix the relative links
$content = defaultLinkTo($content, $this->getURI());
return $content;
}
}

View File

@@ -25,8 +25,7 @@ class EliteDangerousGalnetBridge extends BridgeAbstract {
$language = $this->getInput('language');
$url = 'https://community.elitedangerous.com/';
$url = $url . $language . '/galnet';
$html = getSimpleHTMLDOM($url)
or returnServerError('Error while downloading the website content');
$html = getSimpleHTMLDOM($url);
foreach($html->find('div.article') as $element) {
$item = array();

View File

@@ -63,8 +63,7 @@ class ElsevierBridge extends BridgeAbstract {
public function collectData(){
$uri = self::URI . $this->getInput('j') . '/recent-articles/';
$html = getSimpleHTMLDOM($uri)
or returnServerError('No results for Elsevier journal ' . $this->getInput('j'));
$html = getSimpleHTMLDOM($uri);
foreach($html->find('.pod-listing') as $article) {
$item = array();

View File

@@ -50,10 +50,8 @@ class EpicgamesBridge extends BridgeAbstract {
// Example: https://store-content.ak.epicgames.com/api/ru/content/blog?limit=25
$urlBlog = $api . $this->getInput('language') . '/content/blog?limit=' . $this->getInput('postcount');
$dataSticky = getContents($urlSticky)
or returnServerError('Unable to get the sticky posts from epicgames.com!');
$dataBlog = getContents($urlBlog)
or returnServerError('Unable to get the news posts from epicgames.com!');
$dataSticky = getContents($urlSticky);
$dataBlog = getContents($urlBlog);
// Merge data
$decodedData = array_merge(json_decode($dataSticky), json_decode($dataBlog));

View File

@@ -42,8 +42,7 @@ class EsquerdaNetBridge extends FeedExpander {
$item = parent::parseItem($newsItem);
# Include all the content
$uri = $item['uri'];
$html = getSimpleHTMLDOMCached($uri)
or returnServerError('Could not load content for ' . $uri);
$html = getSimpleHTMLDOMCached($uri);
$content = $html->find('div#content div.content', 0);
## Fix author
$authorHTML = $html->find('.field-name-field-op-author a', 0);

View File

@@ -8,8 +8,7 @@ class EstCeQuonMetEnProdBridge extends BridgeAbstract {
const DESCRIPTION = 'Should we put a website in production today? (French)';
public function collectData() {
$html = getSimpleHTMLDOM(self::URI)
or returnServerError('Could not request EstCeQuonMetEnProd: ' . self::URI);
$html = getSimpleHTMLDOM(self::URI);
$item = array();
$item['uri'] = $this->getURI() . '#' . date('Y-m-d');

View File

@@ -33,8 +33,7 @@ class EtsyBridge extends BridgeAbstract {
);
public function collectData(){
$html = getSimpleHTMLDOM($this->getURI())
or returnServerError('Failed to receive ' . $this->getURI());
$html = getSimpleHTMLDOM($this->getURI());
$results = $html->find('li.block-grid-item');

59
bridges/ExplosmBridge.php Normal file
View File

@@ -0,0 +1,59 @@
<?php
class ExplosmBridge extends BridgeAbstract {
const MAINTAINER = 'bockiii';
const NAME = 'Explosm Bridge';
const URI = 'https://www.explosm.net/';
const CACHE_TIMEOUT = 4800; //2hours
const DESCRIPTION = 'Returns the last 5 comics';
const PARAMETERS = array(
'Get latest posts' => array(
'limit' => array(
'name' => 'Posts limit',
'type' => 'number',
'title' => 'Maximum number of items to return',
'defaultValue' => 5
)
)
);
public function collectData(){
$limit = $this->getInput('limit');
$latest = getSimpleHTMLDOM('https://explosm.net/comics/latest');
$image = $latest->find('div[id=comic]', 0)->find('img', 0)->getAttribute('src');
$date_string = $latest->find('p[class*=Author__P]', 0)->innertext;
$next_data_string = $latest->find('script[id=__NEXT_DATA__]', 0)->innertext;
$exp = '/{\\\"latest\\\":\[{\\\"slug\\\":\\\"(.*?)\\ /';
$reg_array = array();
preg_match($exp, $next_data_string, $reg_array);
$comic_id = $reg_array[1];
$comic_id = substr($comic_id, 0, strpos($comic_id, '\\'));
$item = array();
$item['uri'] = $this::URI . 'comics/' . $comic_id;
$item['uid'] = $this::URI . 'comics/' . $comic_id;
$item['title'] = 'Comic for ' . $date_string;
$item['timestamp'] = strtotime($date_string);
$item['author'] = $latest->find('p[class*=Author__P]', 2)->innertext;
$item['content'] = '<img src="' . $image . '" />';
$this->items[] = $item;
$next_comic = substr($this::URI, 0, -1)
. $latest->find('div[class*=MainComic__Selector]', 0)->find('a', 0)->getAttribute('href');
// use index 1 as the latest comic was already found
for ($i = 1; $i <= $limit; $i++) {
$this_comic = getSimpleHTMLDOM($next_comic);
$image = $this_comic->find('div[id=comic]', 0)->find('img', 0)->getAttribute('src');
$date_string = $this_comic->find('p[class*=Author__P]', 0)->innertext;
$item = array();
$item['uri'] = $next_comic;
$item['uid'] = $next_comic;
$item['title'] = 'Comic for ' . $date_string;
$item['timestamp'] = strtotime($date_string);
$item['author'] = $this_comic->find('p[class*=Author__P]', 2)->innertext;
$item['content'] = '<img src="' . $image . '" />';
$this->items[] = $item;
$next_comic = substr($this::URI, 0, -1)
. $this_comic->find('div[class*=MainComic__Selector]', 0)->find('a', 0)->getAttribute('href'); // get next comic link
}
}
}

View File

@@ -1,7 +1,7 @@
<?php
class ExtremeDownloadBridge extends BridgeAbstract {
const NAME = 'Extreme Download';
const URI = 'https://www.extreme-down.tv/';
const URI = 'https://www.extreme-down.plus/';
const DESCRIPTION = 'Suivi de série sur Extreme Download';
const MAINTAINER = 'sysadminstory';
const PARAMETERS = array(
@@ -10,7 +10,7 @@ class ExtremeDownloadBridge extends BridgeAbstract {
'name' => 'URL de la série',
'type' => 'text',
'required' => true,
'title' => 'URL d\'une série sans le https://ww1.extreme-d0wn.com/',
'title' => 'URL d\'une série sans le https://www.extreme-down.plus/',
'exampleValue' => 'series-hd/hd-series-vostfr/46631-halt-and-catch-fire-saison-04-vostfr-hdtv-720p.html'),
'filter' => array(
'name' => 'Type de contenu',
@@ -26,8 +26,7 @@ class ExtremeDownloadBridge extends BridgeAbstract {
);
public function collectData(){
$html = getSimpleHTMLDOM(self::URI . $this->getInput('url'))
or returnServerError('Could not request Extreme Download.');
$html = getSimpleHTMLDOM(self::URI . $this->getInput('url'));
$filter = $this->getInput('filter');

View File

@@ -24,8 +24,7 @@ class FDroidBridge extends BridgeAbstract {
public function collectData(){
$url = self::URI;
$html = getSimpleHTMLDOM($url)
or returnServerError('Could not request F-Droid.');
$html = getSimpleHTMLDOM($url);
// targetting the corresponding widget based on user selection
// "updated" is the 5th widget on the page, "added" is the 6th

View File

@@ -36,8 +36,7 @@ class FM4Bridge extends BridgeAbstract
$uri = $uri . '?page=' . $page;
$html = getSimpleHTMLDOM($uri)
or returnServerError('Error while downloading the website content');
$html = getSimpleHTMLDOM($uri);
$page_items = array();
@@ -50,8 +49,7 @@ class FM4Bridge extends BridgeAbstract
$item['timestamp'] = strtotime($article->find('p[class*=time]', 0)->plaintext);
if ($this->getInput('loadcontent')) {
$item['content'] = getSimpleHTMLDOM($item['uri'])->find('div[class=storyText]', 0)->innertext
or returnServerError('Error while downloading the full article');
$item['content'] = getSimpleHTMLDOM($item['uri'])->find('div[class=storyText]', 0);
}
$page_items[] = $item;

View File

@@ -60,6 +60,10 @@ class FSecureBlogBridge extends BridgeAbstract {
private function collectCategory($category) {
$url = $this->getURI() . "/category/$category/";
while ($url) {
//Limit total amount of requests
if(count($this->items) >= 20) {
break;
}
$url = $this->collectListing($url);
}
}

View File

@@ -6,8 +6,7 @@ class FabriceBellardBridge extends BridgeAbstract {
const MAINTAINER = 'somini';
public function collectData() {
$html = getSimpleHTMLDOM(self::URI)
or returnServerError('Could not load content');
$html = getSimpleHTMLDOM(self::URI);
foreach ($html->find('p') as $obj) {
$item = array();

View File

@@ -1,10 +1,10 @@
<?php
class FacebookBridge extends BridgeAbstract {
const MAINTAINER = 'teromene, logmanoriginal';
// const MAINTAINER = 'teromene, logmanoriginal';
const NAME = 'Facebook Bridge | Main Site';
const URI = 'https://www.facebook.com/';
const CACHE_TIMEOUT = 300; // 5min
const CACHE_TIMEOUT = 1800; // 30min
const DESCRIPTION = 'Input a page title or a profile log. For a profile log,
please insert the parameter as follow : myExamplePage/132621766841117';
@@ -181,8 +181,7 @@ class FacebookBridge extends BridgeAbstract {
$this->getURI()
);
$html = getSimpleHTMLDOM($touchURI, $header)
or returnServerError('Failed loading facebook page: ' . $this->getURI());
$html = getSimpleHTMLDOM($touchURI, $header);
if(!$this->isPublicGroup($html)) {
returnClientError('This group is not public! RSS-Bridge only supports public groups!');
@@ -534,8 +533,7 @@ EOD;
CURLOPT_POSTFIELDS => http_build_query($captcha_fields)
);
$html = getSimpleHTMLDOM($captcha_action, $header, $opts)
or returnServerError('Failed to submit captcha response back to Facebook');
$html = getSimpleHTMLDOM($captcha_action, $header, $opts);
return $html;
}
@@ -560,8 +558,7 @@ EOD;
$header = array();
}
$html = getSimpleHTMLDOM($this->getURI(), $header)
or returnServerError('No results for this query.');
$html = getSimpleHTMLDOM($this->getURI(), $header);
}

View File

@@ -77,8 +77,7 @@ class FicbookBridge extends BridgeAbstract {
$header = array('Accept-Language: en-US');
$html = getSimpleHTMLDOM($this->getURI(), $header)
or returnServerError('Could not request ' . $this->getURI());
$html = getSimpleHTMLDOM($this->getURI(), $header);
$html = defaultLinkTo($html, self::URI);

View File

@@ -1,33 +0,0 @@
<?php
class FierPandaBridge extends BridgeAbstract {
const MAINTAINER = 'snroki';
const NAME = 'Fier Panda Bridge';
const URI = 'http://www.fier-panda.fr/';
const CACHE_TIMEOUT = 21600; // 6h
const DESCRIPTION = 'Returns latest articles from Fier Panda.';
public function getIcon() {
return self::URI . 'wp-content/themes/fier-panda/img/favicon.png';
}
public function collectData(){
$html = getSimpleHTMLDOM(self::URI)
or returnServerError('Could not request Fier Panda.');
defaultLinkTo($html, static::URI);
foreach($html->find('article') as $article) {
$item = array();
$item['uri'] = $article->find('a', 0)->href;
$item['title'] = $article->find('a', 0)->title;
$this->items[] = $item;
}
}
}

View File

@@ -2,11 +2,11 @@
class FilterBridge extends FeedExpander {
const MAINTAINER = 'Frenzie';
const MAINTAINER = 'Frenzie, ORelio';
const NAME = 'Filter';
const CACHE_TIMEOUT = 3600; // 1h
const DESCRIPTION = 'Filters a feed of your choice';
const URI = 'https://github.com/rss-bridge/rss-bridge';
const URI = 'https://github.com/RSS-Bridge/rss-bridge';
const PARAMETERS = array(array(
'url' => array(
@@ -14,7 +14,7 @@ class FilterBridge extends FeedExpander {
'required' => true,
),
'filter' => array(
'name' => 'Filter item title (regular expression)',
'name' => 'Filter (regular expression)',
'required' => false,
),
'filter_type' => array(
@@ -22,60 +22,101 @@ class FilterBridge extends FeedExpander {
'type' => 'list',
'required' => false,
'values' => array(
'Permit' => 'permit',
'Block' => 'block',
'Keep matching items' => 'permit',
'Hide matching items' => 'block',
),
'defaultValue' => 'permit',
),
'title_from_content' => array(
'name' => 'Generate title from content',
'case_insensitive' => array(
'name' => 'Case-insensitive filter',
'type' => 'checkbox',
'required' => false,
)
),
'fix_encoding' => array(
'name' => 'Attempt Latin1/UTF-8 fixes when evaluating filter',
'type' => 'checkbox',
'required' => false,
),
'target_title' => array(
'name' => 'Apply filter on title',
'type' => 'checkbox',
'required' => false,
'defaultValue' => 'checked'
),
'target_content' => array(
'name' => 'Apply filter on content',
'type' => 'checkbox',
'required' => false,
),
'target_author' => array(
'name' => 'Apply filter on author',
'type' => 'checkbox',
'required' => false,
),
'title_from_content' => array(
'name' => 'Generate title from content (overwrite existing title)',
'type' => 'checkbox',
'required' => false,
),
'length_limit' => array(
'name' => 'Max length analyzed by filter (-1: no limit)',
'type' => 'number',
'required' => false,
'defaultValue' => -1,
),
));
protected function parseItem($newItem){
$item = parent::parseItem($newItem);
// Generate title from first 50 characters of content?
if($this->getInput('title_from_content') && array_key_exists('content', $item)) {
$content = str_get_html($item['content']);
$pos = strpos($item['content'], ' ', 50);
$item['title'] = substr(
$content->plaintext,
0,
$pos
);
$item['title'] = substr($content->plaintext, 0, $pos);
if(strlen($content->plaintext) >= $pos) {
$item['title'] .= '...';
}
}
switch(true) {
case $this->getFilterType() === 'permit':
if (preg_match($this->getFilter(), $item['title'])) {
return $item;
}
break;
case $this->getFilterType() === 'block':
if (!preg_match($this->getFilter(), $item['title'])) {
return $item;
}
break;
// Build regular expression
$regex = '/' . $this->getInput('filter') . '/';
if($this->getInput('case_insensitive')) {
$regex .= 'i';
}
return null;
}
protected function getFilter(){
return '/' . $this->getInput('filter') . '/';
}
// Retrieve fields to check
$filter_fields = array();
if($this->getInput('target_title')) {
$filter_fields[] = $item['title'];
}
if($this->getInput('target_content')) {
$filter_fields[] = $item['content'];
}
if($this->getInput('target_author')) {
$filter_fields[] = $item['author'];
}
protected function getFilterType(){
return $this->getInput('filter_type');
// Apply filter on item
$keep_item = false;
$length_limit = intval($this->getInput('length_limit'));
foreach($filter_fields as $field) {
if($length_limit > 0) {
$field = substr($field, 0, $length_limit);
}
$keep_item |= boolval(preg_match($regex, $field));
if($this->getInput('fix_encoding')) {
$keep_item |= boolval(preg_match($regex, utf8_decode($field)));
$keep_item |= boolval(preg_match($regex, utf8_encode($field)));
}
}
// Reverse result? (keep everything but matching items)
if($this->getInput('filter_type') === 'block') {
$keep_item = !$keep_item;
}
return $keep_item ? $item : null;
}
public function getURI(){
@@ -84,18 +125,15 @@ class FilterBridge extends FeedExpander {
if(empty($url)) {
$url = parent::getURI();
}
return $url;
}
public function collectData(){
if($this->getInput('url') && substr($this->getInput('url'), 0, strlen('http')) !== 'http') {
// just in case someone find a way to access local files by playing with the url
if($this->getInput('url') && substr($this->getInput('url'), 0, 4) !== 'http') {
// just in case someone finds a way to access local files by playing with the url
returnClientError('The url parameter must either refer to http or https protocol.');
}
try{
$this->collectExpandableDatas($this->getURI());
} catch (Exception $e) {
$this->collectExpandableDatas($this->getURI());
}
$this->collectExpandableDatas($this->getURI());
}
}

View File

@@ -35,29 +35,35 @@ class FirefoxAddonsBridge extends BridgeAbstract {
}
public function collectData() {
$html = getSimpleHTMLDOM($this->getURI())
or returnServerError('Could not request: ' . $this->getURI());
$html = getSimpleHTMLDOM($this->getURI());
$this->feedName = $html->find('h1[class="AddonTitle"] > a', 0)->innertext;
$author = $html->find('span.AddonTitle-author > a', 0)->plaintext;
foreach ($html->find('div.AddonVersionCard-content') as $div) {
foreach ($html->find('li.AddonVersionCard') as $li) {
$item = array();
$item['title'] = $div->find('h2.AddonVersionCard-version', 0)->plaintext;
$item['title'] = $li->find('h2.AddonVersionCard-version', 0)->plaintext;
$item['uid'] = $item['title'];
$item['uri'] = $this->getURI();
$item['author'] = $author;
if (preg_match($this->releaseDateRegex, $div->find('div.AddonVersionCard-fileInfo', 0)->plaintext, $match)) {
if (preg_match($this->releaseDateRegex, $li->find('div.AddonVersionCard-fileInfo', 0)->plaintext, $match)) {
$item['timestamp'] = $match[1];
$size = $match[2];
}
$compatibility = $div->find('div.AddonVersionCard-compatibility', 0)->plaintext;
$license = $div->find('p.AddonVersionCard-license', 0)->innertext;
$downloadlink = $div->find('a.InstallButtonWrapper-download-link', 0)->href;
$releaseNotes = $this->removeOutgoinglink($div->find('div.AddonVersionCard-releaseNotes', 0));
$compatibility = $li->find('div.AddonVersionCard-compatibility', 0)->plaintext;
$license = $li->find('p.AddonVersionCard-license', 0)->innertext;
if ($li->find('a.InstallButtonWrapper-download-link', 0)) {
$downloadlink = $li->find('a.InstallButtonWrapper-download-link', 0)->href;
} elseif ($li->find('a.Button.Button--action.AMInstallButton-button.Button--puffy', 0)) {
$downloadlink = $li->find('a.Button.Button--action.AMInstallButton-button.Button--puffy', 0)->href;
}
$releaseNotes = $this->removeOutgoinglink($li->find('div.AddonVersionCard-releaseNotes', 0));
if (preg_match($this->xpiFileRegex, $downloadlink, $match)) {
$xpiFilename = $match[0];

View File

@@ -14,8 +14,7 @@ class FirstLookMediaTechBridge extends BridgeAbstract {
);
public function collectData() {
$html = getSimpleHTMLDOM(self::URI)
or returnServerError('Could not load content');
$html = getSimpleHTMLDOM(self::URI);
if ($this->getInput('projects')) {
$top_projects = $html->find('.PromoList-ul', 0);

185
bridges/FlashbackBridge.php Normal file
View File

@@ -0,0 +1,185 @@
<?php
class FlashbackBridge extends BridgeAbstract
{
const MAINTAINER = 'fatuus';
const NAME = 'Flashback forum';
const URI = 'https://www.flashback.org';
const DESCRIPTION = 'Returns post from forum';
const CACHE_TIMEOUT = 10800; // 3h
const PARAMETERS = array(
'Category' => array(
'c' => array(
'name' => 'Category number',
'type' => 'number',
'exampleValue' => '249',
'required' => true
)
),
'Tag' => array(
'a' => array(
'name' => 'Tag',
'type' => 'text',
'exampleValue' => 'stockholm',
'required' => true
)
),
'Thread' => array(
't' => array(
'name' => 'Thread number',
'type' => 'number',
'exampleValue' => '1420554',
'required' => true
)
),
/*'User' => array(
'u' => array(
'name' => 'User number',
'type' => 'text',
'exampleValue' => 'not working, need login',
'required' => true
)
),*/
'Search string' => array(
's' => array(
'name' => 'Words',
'type' => 'text',
'exampleValue' => 'sök',
'required' => true
),
'type' => array(
'name' => 'Type of search',
'type' => 'list',
'defaultValue' => 'Posts',
'values' => array(
'Posts' => 'posts',
'Subjects' => 'subjects'
)
)
)
);
public function getName()
{
if ($this->getInput('c')) {
$category = $this->getInput('c');
return 'Category ' . $category . ' - Flashback';
} elseif ($this->getInput('a')) {
$tag = $this->getInput('a');
return 'Tag: ' . $tag . ' - Flashback';
} elseif ($this->getInput('t')) {
$thread = $this->getInput('t');
return 'Thread ' . $thread . ' - Flashback';
} elseif ($this->getInput('u')) {
$user = $this->getInput('u');
return 'User ' . $user . ' - Flashback';
} elseif ($this->getInput('s')) {
$search = $this->getInput('s');
return 'Search: ' . $search . ' - Flashback';
}
return self::NAME;
}
public function collectData()
{
if ($this->getInput('c')) {
$page = self::URI . '/f' . $this->getInput('c');
} elseif ($this->getInput('a')) {
$page = self::URI . '/find_threads_by_tag.php?tag=' . $this->getInput('a');
} elseif ($this->getInput('t')) {
$page = self::URI . '/t' . $this->getInput('t');
$page = $page . 's'; // last-page
} elseif ($this->getInput('u')) {
$page = self::URI . '/find_posts_by_user.php?userid=' . $this->getInput('u');
} elseif ($this->getInput('s')) {
if ($this->getInput('type') == 'posts') {
$page = self::URI . '/sok/?query=' . $this->getInput('s') . '&search_post=1&sp=1&so=pd';
} else {
$page = self::URI . '/sok/?query=' . $this->getInput('s') . '&search_post=0&sp=1&so=pd';
}
}
$html = getSimpleHTMLDOM($page);
if ($this->getInput('c') || $this->getInput('a')) {
$category = $this->getInput('c');
$array = $html->find('table#threadslist tbody tr');
foreach ($array as $key => $element) {
$item = array();
$item['uri'] = self::URI . $element->find('td.td_title a', 0)->href;
$item['title'] = trim(utf8_encode($element->find('td.td_title a', 0)->innertext));
$item['author'] = trim(utf8_encode(
$element->find('td.td_title span.thread-poster span', 0)->innertext)
);
$timestamp = $element->find('td.td_last_post div', 0);
if (isset($timestamp->plaintext)) {
$item['timestamp'] = strtotime(str_replace(array('Ig&aring;r', 'Idag'),
array('yesterday', 'today'), trim($timestamp->plaintext)));
}
$item['content'] = $item['title'] . '<br />' . trim(preg_replace('/\t+/', '',
$element->find('td.td_replies', 0)->innertext));
$item['uid'] = preg_split('/(\/)/', $element->find('td.td_title a', 0)->href)[1];
$this->items[] = $item;
}
} elseif ($this->getInput('t')) {
$tags = $html->find('div.hidden-xs a.tag');
$array = $html->find('div.post');
foreach ($array as $key => $element) {
$item = array();
$item['uri_post'] = self::URI . $element->find('div.post-heading a', 2)->href;
$item['uri'] = self::URI . '/' . preg_split('/(\/s)/', $item['uri_post'])[1] . '#' .
preg_split('/(\/s)/', $item['uri_post'])[1];
$item['uri_thread'] = $page;
$item['author'] = utf8_encode($element->find('div.post-user ul li', 0)->innertext);
$item['author_link'] = self::URI . $element->find('div.post-user ul li a', 0)->href;
$item['post_nr'] = $element->find('div.post-heading a strong', 0)->innertext;
$item['timestamp'] = strtotime(
str_replace(
array('Ig&aring;r', 'Idag'), array('yesterday', 'today'),
current(explode("\t", str_replace("\t\t", "\t", trim(
$element->find('div.post-heading', 0)->plaintext)
)))
)
);
if ($element->find('div.smallfont strong', 0)) {
$item['title'] = trim(utf8_encode($element->find('div.smallfont strong', 0)->innertext));
}
if (empty($item['title'])) {
$item['title'] = date('D j M y H:i', $item['timestamp']);
}
$item['content'] = trim(preg_replace('/\t+/', '', $element->find('div.post_message', 0)));
$item['uid'] = preg_split('/(\#|\/)/', $element->find('div.post-heading a', 2)->href)[1];
foreach ($tags as $tag_key => $tag) {
$item['categories'][] = trim(utf8_encode($tag->innertext));
}
$this->items[] = $item;
}
// } elseif ( $this->getInput('u') ) {
} elseif ($this->getInput('s')) {
$array = $html->find('div.post');
foreach ($array as $key => $element) {
$item = array();
$item['uri'] = self::URI . $element->find('div.post-body a', 0)->href;
$item['uri_thread'] = $page . $element->find('div.post-heading a', 0)->href . 's';
$item['author'] = $element->find('div.post-body a', 1)->innertext;
$item['author_link'] = self::URI . $element->find('div.post-body a', 1)->href;
$time = preg_split('/(\>)/', $element->find('div.post-heading', 0)->innertext);
$item['timestamp'] = strtotime(trim(end($time)));
$item['title'] = trim(utf8_encode($element->find('div.post-body strong', 0)->innertext));
if (empty($item['title'])) {
$item['title'] = date('D j M y H:i', $item['timestamp']);
}
$item['datetime'] = (trim(end($time)));
$item['categories'][] = trim(utf8_encode($element->find('div.post-heading a', 0)->innertext));
$item['content'] = trim(preg_replace('/\t+/', '', $element->find('div.post_message', 0)));
$item['uid'] = preg_split('/(\#|\/)/', $element->find('div.post-body a', 0)->href)[1];
$this->items[] = $item;
}
}
}
}

View File

@@ -83,21 +83,18 @@ class FlickrBridge extends BridgeAbstract {
case 'Explore':
$filter = 'photo-lite-models';
$html = getSimpleHTMLDOM($this->getURI())
or returnServerError('Could not request Flickr.');
$html = getSimpleHTMLDOM($this->getURI());
break;
case 'By keyword':
$filter = 'photo-lite-models';
$html = getSimpleHTMLDOM($this->getURI())
or returnServerError('No results for this query.');
$html = getSimpleHTMLDOM($this->getURI());
break;
case 'By username':
//$filter = 'photo-models';
$filter = 'photo-lite-models';
$html = getSimpleHTMLDOM($this->getURI())
or returnServerError('Requested username can\'t be found.');
$html = getSimpleHTMLDOM($this->getURI());
$this->username = $this->getInput('u');

View File

@@ -7,8 +7,7 @@ class FootitoBridge extends BridgeAbstract {
const DESCRIPTION = 'Footito';
public function collectData(){
$html = getSimpleHTMLDOM(self::URI)
or returnServerError('Could not request Footito.');
$html = getSimpleHTMLDOM(self::URI);
foreach($html->find('div.post') as $element) {
$item = array();

View File

@@ -0,0 +1,68 @@
<?php
class Formula1Bridge extends BridgeAbstract {
const NAME = 'Formula1 Bridge';
const URI = 'https://formula1.com/';
const DESCRIPTION = 'Returns latest official Formula 1 news';
const MAINTAINER = 'AxorPL';
const API_KEY = 'qPgPPRJyGCIPxFT3el4MF7thXHyJCzAP';
const API_URL = 'https://api.formula1.com/v1/editorial/articles?limit=%u';
const ARTICLE_AUTHOR = 'Formula 1';
const ARTICLE_HTML = '<p>%s</p><a href="%s" target="_blank"><img src="%s" alt="%s" title="%s"></a>';
const ARTICLE_URL = 'https://formula1.com/en/latest/article.%s.%s.html';
const LIMIT_MIN = 1;
const LIMIT_DEFAULT = 10;
const LIMIT_MAX = 100;
const PARAMETERS = array(
array(
'limit' => array(
'name' => 'Limit',
'type' => 'number',
'required' => false,
'title' => 'Number of articles to return',
'exampleValue' => self::LIMIT_DEFAULT,
'defaultValue' => self::LIMIT_DEFAULT
)
)
);
public function collectData() {
$limit = $this->getInput('limit') ?: self::LIMIT_DEFAULT;
$limit = min(self::LIMIT_MAX, max(self::LIMIT_MIN, $limit));
$url = sprintf(self::API_URL, $limit);
$json = json_decode(getContents($url, array('apikey: ' . self::API_KEY)));
if(property_exists($json, 'error')) {
returnServerError($json->message);
}
$list = $json->items;
foreach($list as $article) {
if(property_exists($article->thumbnail, 'caption')) {
$caption = $article->thumbnail->caption;
} else {
$caption = $article->thumbnail->image->title;
}
$item = array();
$item['uri'] = sprintf(self::ARTICLE_URL, $article->slug, $article->id);
$item['title'] = $article->title;
$item['timestamp'] = $article->updatedAt;
$item['author'] = self::ARTICLE_AUTHOR;
$item['enclosures'] = array($article->thumbnail->image->url);
$item['uid'] = $article->id;
$item['content'] = sprintf(
self::ARTICLE_HTML,
$article->metaDescription,
$item['uri'],
$item['enclosures'][0],
$caption,
$caption
);
$this->items[] = $item;
}
}
}

View File

@@ -29,8 +29,7 @@ class FourchanBridge extends BridgeAbstract {
public function collectData(){
$html = getSimpleHTMLDOM($this->getURI())
or returnServerError('Could not request 4chan, thread not found');
$html = getSimpleHTMLDOM($this->getURI());
foreach($html->find('div.postContainer') as $element) {
$item = array();

View File

@@ -28,8 +28,7 @@ class FurAffinityUserBridge extends BridgeAbstract {
$cookies = self::login();
$url = self::URI . '/gallery/' . $this->getInput('searchUsername');
$html = getSimpleHTMLDOM($url, $cookies)
or returnServerError('Could not load the user\'s galary page.');
$html = getSimpleHTMLDOM($url, $cookies);
$submissions = $html->find('section[id=gallery-gallery]', 0)->find('figure');
foreach($submissions as $submission) {

View File

@@ -85,9 +85,8 @@ class FuturaSciencesBridge extends FeedExpander {
protected function parseItem($newsItem){
$item = parent::parseItem($newsItem);
$item['uri'] = str_replace('#xtor=RSS-8', '', $item['uri']);
$article = getSimpleHTMLDOMCached($item['uri'])
or returnServerError('Could not request Futura-Sciences: ' . $item['uri']);
$item['uri'] = str_replace('#xtor%3DRSS-8', '', $item['uri']);
$article = getSimpleHTMLDOMCached($item['uri']);
$item['content'] = $this->extractArticleContent($article);
$author = $this->extractAuthor($article);
if (!empty($author))
@@ -96,31 +95,47 @@ class FuturaSciencesBridge extends FeedExpander {
}
private function extractArticleContent($article){
$contents = $article->find('section.article-text', 1)->innertext;
$headline = trim($article->find('p.description', 0)->plaintext);
if(!empty($headline))
$headline = '<p><b>' . $headline . '</b></p>';
$contents = $article->find('section.article-text', 1);
foreach (array(
'<div class="clear',
'<div class="sharebar2',
'<div class="diaporamafullscreen"',
'<div class="module social-button',
'<div class="module social-share',
'<div style="margin-bottom:10px;" class="noprint"',
'<div class="ficheprevnext',
'<div class="bar noprint',
'<div class="toolbar noprint',
'<div class="addthis_toolbox',
'<div class="noprint',
'<div class="bg bglight border border-full noprint',
'<div class="httplogbar-wrapper noprint',
'<div id="forumcomments',
'<div ng-if="active"'
) as $div_start) {
$contents = stripRecursiveHTMLSection($contents, 'div', $div_start);
foreach($contents->find('img') as $img) {
if(!empty($img->getAttribute('data-src'))) {
$img->src = $img->getAttribute('data-src');
}
}
foreach($contents->find('a.tooltip-link') as $a) {
$a->outertext = $a->plaintext;
}
foreach(array(
'clear',
'sharebar2',
'diaporamafullscreen',
'module.social-button',
'module.social-share',
'ficheprevnext',
'addthis_toolbox',
'noprint',
'hubbottom',
'hubbottom2'
) as $div_class_remove) {
foreach($contents->find('div.' . $div_class_remove) as $div) {
$keep_div = false;
foreach(array(
'didyouknow'
) as $div_class_dont_remove) {
if(strpos($div->getAttribute('class'), $div_class_dont_remove) !== false) {
$keep_div = true;
}
}
if(!$keep_div) {
$div->outertext = '';
}
}
}
$contents = $contents->innertext;
$contents = stripWithDelimiters($contents, '<hr ', '/>');
$contents = stripWithDelimiters($contents, '<p class="content-date', '</p>');
$contents = stripWithDelimiters($contents, '<h1 class="content-title', '</h1>');
@@ -131,7 +146,7 @@ class FuturaSciencesBridge extends FeedExpander {
$contents = stripWithDelimiters($contents, '<script ', '</script>');
$contents = stripWithDelimiters($contents, '<script>', '</script>');
return $headline . trim($contents);
return trim($contents);
}
// Extracts the author from an article or element

View File

@@ -76,14 +76,13 @@ class GBAtempBridge extends BridgeAbstract {
public function collectData(){
$html = getSimpleHTMLDOM(self::URI)
or returnServerError('Could not request GBAtemp.');
$html = getSimpleHTMLDOM(self::URI);
switch($this->getInput('type')) {
case 'N':
foreach($html->find('li[class=news_item full]') as $newsItem) {
foreach($html->find('li[class=news_item news full]') as $newsItem) {
$url = self::URI . $newsItem->find('a', 0)->href;
$img = $this->getURI() . $newsItem->find('img', 0)->src . '#.image';
$img = $this->getURI() . extractFromDelimiters($newsItem->find('a.news_image', 0)->style, 'url(', ')') . '#.image';
$time = $this->findItemDate($newsItem);
$author = $newsItem->find('a.username', 0)->plaintext;
$title = $newsItem->find('a', 1)->plaintext;
@@ -97,8 +96,7 @@ class GBAtempBridge extends BridgeAbstract {
$url = self::URI . $reviewItem->find('a', 0)->href;
$img = $this->getURI() . extractFromDelimiters($reviewItem->find('a', 0)->style, 'image:url(', ')');
$title = $reviewItem->find('span.review_title', 0)->plaintext;
$content = getSimpleHTMLDOM($url)
or returnServerError('Could not request GBAtemp: ' . $uri);
$content = getSimpleHTMLDOM($url);
$author = $content->find('a.username', 0)->plaintext;
$time = $this->findItemDate($content);
$intro = '<p><b>' . ($content->find('div#review_intro', 0)->plaintext) . '</b></p>';

View File

@@ -8,8 +8,7 @@ class GOGBridge extends BridgeAbstract {
public function collectData() {
$values = getContents('https://www.gog.com/games/ajax/filtered?limit=25&sort=new')
or returnServerError('Unable to get the news pages from GOG !');
$values = getContents('https://www.gog.com/games/ajax/filtered?limit=25&sort=new');
$decodedValues = json_decode($values);
$limit = 0;
@@ -38,8 +37,7 @@ class GOGBridge extends BridgeAbstract {
private function buildGameContentPage($game) {
$gameDescriptionText = getContents('https://api.gog.com/products/' . $game->id . '?expand=description')
or returnServerError('Unable to get game description from GOG !');
$gameDescriptionText = getContents('https://api.gog.com/products/' . $game->id . '?expand=description');
$gameDescriptionValue = json_decode($gameDescriptionText);

View File

@@ -72,7 +72,7 @@ class GQMagazineBridge extends BridgeAbstract
public function collectData()
{
$html = getSimpleHTMLDOM($this->getURI()) or returnServerError('Could not request ' . $this->getURI());
$html = getSimpleHTMLDOM($this->getURI());
// Since GQ don't want simple class scrapping, let's do it the hard way and ... discover content !
$main = $html->find('main', 0);

View File

@@ -27,15 +27,13 @@ class GenshinImpactBridge extends BridgeAbstract {
$url = 'https://genshin.mihoyo.com/content/yuanshen/getContentList';
$url = $url . '?pageSize=3&pageNum=1&channelId=' . $category;
$api_response = getContents($url)
or returnServerError('Error while downloading the website content');
$api_response = getContents($url);
$json_list = json_decode($api_response, true);
foreach($json_list['data']['list'] as $json_item) {
$article_url = 'https://genshin.mihoyo.com/content/yuanshen/getContent';
$article_url = $article_url . '?contentId=' . $json_item['contentId'];
$article_res = getContents($article_url)
or returnServerError('Error while downloading the website content');
$article_res = getContents($article_url);
$article_json = json_decode($article_res, true);
$article_time = $article_json['data']['start_time'];
$timezone = 'Asia/Shanghai';

Some files were not shown because too many files have changed in this diff Show More