From 7c480db01205625ecc56f5d5e96c32654a6aaa74 Mon Sep 17 00:00:00 2001 From: peppy6582 Date: Fri, 10 Jan 2025 15:15:24 -0600 Subject: [PATCH 1/8] Create CruncyrollBridge.php This bridge fetches the latest news from the Crunchyroll News API and provides it as an RSS Feed --- bridges/CruncyrollBridge.php | 121 +++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 bridges/CruncyrollBridge.php diff --git a/bridges/CruncyrollBridge.php b/bridges/CruncyrollBridge.php new file mode 100644 index 00000000..2001e7e4 --- /dev/null +++ b/bridges/CruncyrollBridge.php @@ -0,0 +1,121 @@ + [ + 'name' => 'Category', + 'type' => 'text', + 'required' => true, + 'exampleValue' => 'Announcements,News,News', + ], + 'page_size' => [ + 'name' => 'Page Size', + 'type' => 'number', + 'required' => false, + 'defaultValue' => 16, + ], + 'page' => [ + 'name' => 'Page', + 'type' => 'number', + 'required' => false, + 'defaultValue' => 1, + ], + ] + ]; + + /** + * Collects data from the Crunchyroll API and formats it as an RSS feed. + */ + public function collectData() { + // Define API base URL + $apiBaseUrl = 'https://cr-news-api-service.prd.crunchyrollsvc.com/v1/en-US/stories/search'; + + // Retrieve input parameters + $category = $this->getInput('category'); + $pageSize = $this->getInput('page_size'); + $page = $this->getInput('page'); + + // Construct the API URL + $apiUrl = sprintf( + '%s?category=%s&page_size=%d&page=%d', + $apiBaseUrl, + urlencode($category), + $pageSize, + $page + ); + + // Define HTTP headers for the API request + $options = [ + 'http' => [ + 'method' => 'GET', + 'header' => [ + 'User-Agent: Mozilla/5.0 (compatible; RSSBridge/2025)', + 'Accept: application/json', + 'Accept-Language: en-US,en;q=0.5', + 'Origin: https://www.crunchyroll.com', + 'Referer: https://www.crunchyroll.com/', + ], + ], + ]; + + // Create a context for the HTTP request + $context = stream_context_create($options); + + // Fetch data from the API + $response = file_get_contents($apiUrl, false, $context); + + if ($response === false) { + throw new Exception('Failed to fetch data from the Crunchyroll API.'); + } + + // Parse the JSON response + $data = json_decode($response, true); + + if (json_last_error() !== JSON_ERROR_NONE) { + throw new Exception('Failed to decode JSON response: ' . json_last_error_msg()); + } + + // Process each story in the response + foreach ($data['stories'] as $story) { + $item = []; + + // Set the item properties + $item['uri'] = self::URI . '/' . $story['slug']; + $item['title'] = $story['content']['headline']; + $item['timestamp'] = strtotime($story['content']['article_date']); + $item['author'] = 'Unknown'; // Author data can be added if available + $item['content'] = sprintf( + '%s
%s', + $story['content']['thumbnail']['filename'], + htmlspecialchars($story['content']['thumbnail']['alt']), + htmlspecialchars($story['content']['lead']) + ); + $item['categories'] = $story['tag_list'] ?? []; + $item['uid'] = $story['uuid']; + + // Add the item to the feed + $this->items[] = $item; + } + } +} From 671b2948d878c5a94428403c06baa82e922837e6 Mon Sep 17 00:00:00 2001 From: peppy6582 Date: Fri, 10 Jan 2025 15:39:02 -0600 Subject: [PATCH 2/8] Update CruncyrollBridge.php Updated to correct article author parsing --- bridges/CruncyrollBridge.php | 47 ++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/bridges/CruncyrollBridge.php b/bridges/CruncyrollBridge.php index 2001e7e4..7cff443a 100644 --- a/bridges/CruncyrollBridge.php +++ b/bridges/CruncyrollBridge.php @@ -1,26 +1,10 @@ [ @@ -41,12 +25,9 @@ class CrunchyrollBridge extends BridgeAbstract { 'required' => false, 'defaultValue' => 1, ], - ] + ], ]; - /** - * Collects data from the Crunchyroll API and formats it as an RSS feed. - */ public function collectData() { // Define API base URL $apiBaseUrl = 'https://cr-news-api-service.prd.crunchyrollsvc.com/v1/en-US/stories/search'; @@ -56,7 +37,7 @@ class CrunchyrollBridge extends BridgeAbstract { $pageSize = $this->getInput('page_size'); $page = $this->getInput('page'); - // Construct the API URL + // Construct the API URL with query parameters $apiUrl = sprintf( '%s?category=%s&page_size=%d&page=%d', $apiBaseUrl, @@ -96,15 +77,35 @@ class CrunchyrollBridge extends BridgeAbstract { throw new Exception('Failed to decode JSON response: ' . json_last_error_msg()); } + // Map UUIDs to author names from the `rels` array + $authorMap = []; + foreach ($data['rels'] as $rel) { + if (isset($rel['uuid']) && isset($rel['content']['name'])) { + $authorMap[$rel['uuid']] = $rel['content']['name']; + } + } + // Process each story in the response foreach ($data['stories'] as $story) { $item = []; + // Find the author name(s) for the story + $authorNames = []; + if (!empty($story['content']['authors'])) { + foreach ($story['content']['authors'] as $authorUuid) { + if (isset($authorMap[$authorUuid])) { + $authorNames[] = $authorMap[$authorUuid]; + } + } + } + + // Set the `author` field to the resolved names or default to 'Unknown' + $item['author'] = implode(', ', $authorNames) ?: 'Unknown'; + // Set the item properties $item['uri'] = self::URI . '/' . $story['slug']; $item['title'] = $story['content']['headline']; $item['timestamp'] = strtotime($story['content']['article_date']); - $item['author'] = 'Unknown'; // Author data can be added if available $item['content'] = sprintf( '%s
%s', $story['content']['thumbnail']['filename'], From 03681ae4b4173f7cd78091bb600a48c089403b4b Mon Sep 17 00:00:00 2001 From: peppy6582 Date: Sat, 11 Jan 2025 07:46:08 -0600 Subject: [PATCH 3/8] Update CruncyrollBridge.php --- bridges/CruncyrollBridge.php | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/bridges/CruncyrollBridge.php b/bridges/CruncyrollBridge.php index 7cff443a..b5413e4a 100644 --- a/bridges/CruncyrollBridge.php +++ b/bridges/CruncyrollBridge.php @@ -47,28 +47,16 @@ class CrunchyrollBridge extends BridgeAbstract { ); // Define HTTP headers for the API request - $options = [ - 'http' => [ - 'method' => 'GET', - 'header' => [ - 'User-Agent: Mozilla/5.0 (compatible; RSSBridge/2025)', - 'Accept: application/json', - 'Accept-Language: en-US,en;q=0.5', - 'Origin: https://www.crunchyroll.com', - 'Referer: https://www.crunchyroll.com/', - ], - ], + $headers = [ + 'User-Agent: Mozilla/5.0 (compatible; RSSBridge/2025)', + 'Accept: application/json', + 'Accept-Language: en-US,en;q=0.5', + 'Origin: https://www.crunchyroll.com', + 'Referer: https://www.crunchyroll.com/', ]; - // Create a context for the HTTP request - $context = stream_context_create($options); - // Fetch data from the API - $response = file_get_contents($apiUrl, false, $context); - - if ($response === false) { - throw new Exception('Failed to fetch data from the Crunchyroll API.'); - } + $response = getContents($apiUrl, $headers); // Parse the JSON response $data = json_decode($response, true); From e384202f51752ea3ace6d74fc1b3abb3d1616d54 Mon Sep 17 00:00:00 2001 From: peppy6582 Date: Sat, 11 Jan 2025 07:47:28 -0600 Subject: [PATCH 4/8] Update CruncyrollBridge.php --- bridges/CruncyrollBridge.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/CruncyrollBridge.php b/bridges/CruncyrollBridge.php index b5413e4a..9bc21fca 100644 --- a/bridges/CruncyrollBridge.php +++ b/bridges/CruncyrollBridge.php @@ -4,7 +4,7 @@ class CrunchyrollBridge extends BridgeAbstract { const NAME = 'Crunchyroll News Bridge'; const URI = 'https://www.crunchyroll.com/news'; const DESCRIPTION = 'Returns latest news from Crunchyroll'; - const MAINTAINER = 'YourName'; + const MAINTAINER = 'peppy6582'; const PARAMETERS = [ [ 'category' => [ From c7a5a21c61af3212edd831a29fc178f01f02acd7 Mon Sep 17 00:00:00 2001 From: peppy6582 Date: Sat, 11 Jan 2025 07:53:44 -0600 Subject: [PATCH 5/8] Update CruncyrollBridge.php --- bridges/CruncyrollBridge.php | 43 ++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/bridges/CruncyrollBridge.php b/bridges/CruncyrollBridge.php index 9bc21fca..131f0bd3 100644 --- a/bridges/CruncyrollBridge.php +++ b/bridges/CruncyrollBridge.php @@ -1,10 +1,14 @@ [ @@ -28,7 +32,13 @@ class CrunchyrollBridge extends BridgeAbstract { ], ]; - public function collectData() { + /** + * Collects data from the Crunchyroll API and populates items. + * + * @throws Exception If the API call or JSON decoding fails. + */ + public function collectData() + { // Define API base URL $apiBaseUrl = 'https://cr-news-api-service.prd.crunchyrollsvc.com/v1/en-US/stories/search'; @@ -47,16 +57,25 @@ class CrunchyrollBridge extends BridgeAbstract { ); // Define HTTP headers for the API request - $headers = [ - 'User-Agent: Mozilla/5.0 (compatible; RSSBridge/2025)', - 'Accept: application/json', - 'Accept-Language: en-US,en;q=0.5', - 'Origin: https://www.crunchyroll.com', - 'Referer: https://www.crunchyroll.com/', + $options = [ + 'http' => [ + 'method' => 'GET', + 'header' => [ + 'User-Agent: Mozilla/5.0 (compatible; RSSBridge/2025)', + 'Accept: application/json', + 'Accept-Language: en-US,en;q=0.5', + 'Origin: https://www.crunchyroll.com', + 'Referer: https://www.crunchyroll.com/', + ], + ], ]; - // Fetch data from the API - $response = getContents($apiUrl, $headers); + // Use getContents for better error handling and compliance + $response = getContents($apiUrl, [], $options); + + if ($response === false) { + throw new Exception('Failed to fetch data from the Crunchyroll API.'); + } // Parse the JSON response $data = json_decode($response, true); @@ -68,7 +87,7 @@ class CrunchyrollBridge extends BridgeAbstract { // Map UUIDs to author names from the `rels` array $authorMap = []; foreach ($data['rels'] as $rel) { - if (isset($rel['uuid']) && isset($rel['content']['name'])) { + if (isset($rel['uuid'], $rel['content']['name'])) { $authorMap[$rel['uuid']] = $rel['content']['name']; } } From c739e6aa373751fe8b9806f9a196fc38ebb01af6 Mon Sep 17 00:00:00 2001 From: peppy6582 Date: Sat, 11 Jan 2025 07:54:08 -0600 Subject: [PATCH 6/8] Update CruncyrollBridge.php --- bridges/CruncyrollBridge.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/CruncyrollBridge.php b/bridges/CruncyrollBridge.php index 131f0bd3..12f8ba98 100644 --- a/bridges/CruncyrollBridge.php +++ b/bridges/CruncyrollBridge.php @@ -8,7 +8,7 @@ class CrunchyrollBridge extends BridgeAbstract const NAME = 'Crunchyroll News Bridge'; const URI = 'https://www.crunchyroll.com/news'; const DESCRIPTION = 'Returns latest news from Crunchyroll'; - const MAINTAINER = 'YourName'; + const MAINTAINER = 'peppy6582'; const PARAMETERS = [ [ 'category' => [ From 183909aa4d9a69b5436b9b9700423386fd669941 Mon Sep 17 00:00:00 2001 From: peppy6582 Date: Sat, 11 Jan 2025 12:27:32 -0600 Subject: [PATCH 7/8] Create HidiveBridge.php --- bridges/HidiveBridge.php | 103 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 bridges/HidiveBridge.php diff --git a/bridges/HidiveBridge.php b/bridges/HidiveBridge.php new file mode 100644 index 00000000..e60dfe44 --- /dev/null +++ b/bridges/HidiveBridge.php @@ -0,0 +1,103 @@ + 9, + 'skip' => 0, + 'filter' => new stdClass() + ]); + + // Define headers + $headers = [ + 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:133.0) Gecko/20100101 Firefox/133.0', + 'Accept: */*', + 'Accept-Language: en-US,en;q=0.5', + 'Content-Type: application/json', + 'Origin: https://news.hidive.com', + 'Referer: https://news.hidive.com/' + ]; + + // Create a stream context + $options = [ + 'http' => [ + 'method' => 'POST', + 'header' => implode("\r\n", $headers), + 'content' => $postData, + 'ignore_errors' => true + ] + ]; + + $context = stream_context_create($options); + + // Execute the request + $response = file_get_contents($apiUrl, false, $context); + + if ($response === false) { + returnServerError('Unable to fetch data from HIDIVE API.'); + } + + // Decode the response + $data = json_decode($response, true); + if (json_last_error() !== JSON_ERROR_NONE) { + returnServerError('Failed to decode JSON: ' . json_last_error_msg()); + } + + // Process each news item + foreach ($data as $item) { + $newsItem = []; + + // Clean and format the data + $excerpt = isset($item['excerpt']) ? ltrim($item['excerpt']) : ''; + $seoUrl = isset($item['seoUrl']) ? 'https://news.hidive.com' . $item['seoUrl'] : ''; + $image = isset($item['image']) ? 'https:' . $item['image'] : ''; + + // Create feed item + $newsItem['uri'] = $seoUrl; + $newsItem['title'] = $item['title'] ?? ''; + $newsItem['timestamp'] = strtotime($item['releaseDate'] ?? ''); + + // Construct content with image and excerpt + $content = ''; + if ($image) { + $content .= '' . 
+                           htmlspecialchars($item['title'] ?? '') . ''; + } + $content .= '

' . htmlspecialchars($excerpt) . '

'; + + $newsItem['content'] = $content; + + // Add categories if available + if (isset($item['categories']) && is_array($item['categories'])) { + $newsItem['categories'] = $item['categories']; + } + + // Add author if available + if (isset($item['author'])) { + $newsItem['author'] = $item['author']; + } + + $this->items[] = $newsItem; + } + } + + public function getName() { + return self::NAME; + } + + public function getURI() { + return self::URI; + } + + public function getIcon() { + return 'https://www.hidive.com/favicon.ico'; + } +} From 7aca0738599905dcd9c65ab0d4abaccf3c3464df Mon Sep 17 00:00:00 2001 From: peppy6582 Date: Sat, 11 Jan 2025 13:19:31 -0600 Subject: [PATCH 8/8] Update HidiveBridge.php Changed request from file_get_contents to getContent --- bridges/HidiveBridge.php | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/bridges/HidiveBridge.php b/bridges/HidiveBridge.php index e60dfe44..9b92df14 100644 --- a/bridges/HidiveBridge.php +++ b/bridges/HidiveBridge.php @@ -2,13 +2,13 @@ class HidiveBridge extends BridgeAbstract { const NAME = 'HIDIVE News Bridge'; const URI = 'https://news.hidive.com/'; - const DESCRIPTION = 'Returns the latest news from HIDIVE'; + const DESCRIPTION = 'Fetches the latest news from HIDIVE.'; const MAINTAINER = 'Your Name'; const CACHE_TIMEOUT = 3600; // 1 hour cache public function collectData() { $apiUrl = 'https://apigw.hidive.com/news/news'; - + // Define POST payload $postData = json_encode([ 'take' => 9, @@ -26,7 +26,7 @@ class HidiveBridge extends BridgeAbstract { 'Referer: https://news.hidive.com/' ]; - // Create a stream context + // Prepare the HTTP options for getContents $options = [ 'http' => [ 'method' => 'POST', @@ -36,16 +36,14 @@ class HidiveBridge extends BridgeAbstract { ] ]; - $context = stream_context_create($options); + // Use getContents for the HTTP request + $response = getContents($apiUrl, $options); - // Execute the request - $response = file_get_contents($apiUrl, false, $context); - if ($response === false) { returnServerError('Unable to fetch data from HIDIVE API.'); } - // Decode the response + // Decode the JSON response $data = json_decode($response, true); if (json_last_error() !== JSON_ERROR_NONE) { returnServerError('Failed to decode JSON: ' . json_last_error_msg()); @@ -54,17 +52,17 @@ class HidiveBridge extends BridgeAbstract { // Process each news item foreach ($data as $item) { $newsItem = []; - + // Clean and format the data $excerpt = isset($item['excerpt']) ? ltrim($item['excerpt']) : ''; $seoUrl = isset($item['seoUrl']) ? 'https://news.hidive.com' . $item['seoUrl'] : ''; $image = isset($item['image']) ? 'https:' . $item['image'] : ''; - + // Create feed item $newsItem['uri'] = $seoUrl; $newsItem['title'] = $item['title'] ?? ''; $newsItem['timestamp'] = strtotime($item['releaseDate'] ?? ''); - + // Construct content with image and excerpt $content = ''; if ($image) { @@ -72,19 +70,19 @@ class HidiveBridge extends BridgeAbstract { htmlspecialchars($item['title'] ?? '') . '">'; } $content .= '

' . htmlspecialchars($excerpt) . '

'; - + $newsItem['content'] = $content; - + // Add categories if available if (isset($item['categories']) && is_array($item['categories'])) { $newsItem['categories'] = $item['categories']; } - + // Add author if available if (isset($item['author'])) { $newsItem['author'] = $item['author']; } - + $this->items[] = $newsItem; } }