Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Youtube & Reddit support #38

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ brew install php

* [Google](#google)
* [Wikipedia](#wikipedia)
* [YouTube](#youtube)
* [Reddit](#reddit)
* [Wolfram|Alpha](#wolframalpha)
* [Amazon](#amazon)
* [IMDB](#imdb)
Expand Down Expand Up @@ -58,6 +60,22 @@ Support direct term suggestions and preview, **language MUST be specified with [

![wikipedia-zh](screenshots/wikipedia-zh.png)

### YouTube

Support direct term suggestions and preview.
Need to create and add your own API key to youtube.php
Tutorial to create API key: https://blog.hubspot.com/website/how-to-get-youtube-api-key#:~:text=step%2Dby%2Dstep.-,How%20to%20Get%20a%20YouTube%20API%20Key,-Log%20in%20to
Miniature icons will be automatically cached to `ìcon-cache`folder.
Use command `yt erase_cache` to erase the miniature cache folder. [Proxy setting](#proxy-setting) is available.

![youtube](screenshots/youtube.png)

### Reddit

Support subreddit suggestions and preview. [Proxy setting](#proxy-setting) is available.

![reddit](screenshots/reddit.png)

### Wolfram|Alpha

Support direct term suggestions. [Proxy setting](#proxy-setting) is available.
Expand Down
Binary file added screenshots/reddit.png
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make screenshot with the same theme as others (Alfred macOS without an Alfred icon), and make sure your screenshot is captured by ⌘⇧4 then space with shadow, thanks!

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/youtube.png
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
80 changes: 80 additions & 0 deletions src/reddit.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php
use Alfred\Workflows\Workflow;

require_once('vendor/joetannenbaum/alfred-workflow/Workflow.php');
require_once('vendor/joetannenbaum/alfred-workflow/Result.php');
require_once('util/request.php');

const ICON = 'reddit_logo.png';

$wf = new Workflow;

$query = trim($query);

function get_subreddits_data($query) {
$params = [
'query' => urlencode($query),
'include_over_18' => false,
'include_profiles' => false,
'limit' => 7,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggest to change limit to 9 as Alfred defaults to show 9 results

];
$reddit_url = "https://www.reddit.com/api/subreddit_autocomplete_v2.json";
$api_call = $reddit_url . '?' . http_build_query($params);
$headers = array("User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3; charset=utf-8");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the user-agent header seems to be redundant and can be omitted

$ch = curl_init();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be simplified to utilize the request function provided in util/request.php.

curl_setopt($ch, CURLOPT_URL, $api_call);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // Follow redirects
$response = curl_exec($ch);
if (curl_errno($ch)) {
echo 'cURL Error: ' . curl_error($ch);
return []; // Return an empty array on error
}
curl_close($ch);
$data = json_decode($response);
if (json_last_error() !== JSON_ERROR_NONE) {
echo 'JSON Decoding Error: ' . json_last_error_msg();
return []; // Return an empty array on JSON decoding error
}
if (!isset($data->data->children)) {
return []; // Return an empty array if the expected data structure is not present
}
$subbreddits_data = $data->data->children;
return $subbreddits_data;
}
$subreddits_data = get_subreddits_data($query);


if (isset($subreddits_data)) {
foreach ($subreddits_data as $child) {
$kind = $child->kind;
if ($kind == "t5") {
$subreddit_data = $child->data;
$title = $subreddit_data->display_name;
$display_name_prefixed = $subreddit_data->display_name_prefixed;
$description = $subreddit_data->public_description;
$url = "https://www.reddit.com/" . $display_name_prefixed;
$subscribers = $subreddit_data->subscribers;

$wf->result()
->title($title)
->subtitle($description)
->arg($url)
->icon(ICON)
->autocomplete($title)
->copy($title)
->quicklookurl($url);
}
}
} else {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the else case can be ommited as it is suggested to add a normal Web Search with the same keyword as fallback

$wf->result()
->title('No Subreddits')
->subtitle('No subreddits found for '.$query)
->arg("https://www.reddit.com/r/$query")
->icon(ICON)
->copy($query);
}

echo $wf->output();
?>
Binary file added src/reddit_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
101 changes: 101 additions & 0 deletions src/youtube.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?php
use Alfred\Workflows\Workflow;

require_once('vendor/joetannenbaum/alfred-workflow/Workflow.php');
require_once('vendor/joetannenbaum/alfred-workflow/Result.php');
require_once('util/request.php');

const ICON = 'youtube.png';

$wf = new Workflow;

// Update with your YouTube API key
// Use this tutorial to get your API key: https://blog.hubspot.com/website/how-to-get-youtube-api-key#:~:text=step%2Dby%2Dstep.-,How%20to%20Get%20a%20YouTube%20API%20Key,-Log%20in%20to
$youtubeApiKey = 'PUT_YOUR_API_KEY_HERE';

$youtubeApiUrl = 'https://www.googleapis.com/youtube/v3/search';

// See following link for YouTube API quotas
// https://developers.google.com/youtube/v3/determine_quota_cost?hl=fr

$cacheDir = __DIR__ . '/icon-cache/';
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cache dir should be placed to Alfred's default workflow cache dir: getenv('alfred_workflow_cache').'/youtube' then Alfred can automatically manage it with no need to provide an explicit clear method.

Also, please utilize the functions in util/download.php to do file downloading & caching, you can check imdb.php as a simple usage example.


// Check if the directory exists
if (!file_exists($cacheDir) || !is_dir($cacheDir)) {
// Create the directory
if (!mkdir($cacheDir, 0777, true)) {
die('Failed to create directory');
}
} else {
}

if ($query === 'erase_cache') {
$files = glob($cacheDir . '*.png');
foreach ($files as $file) {
if (file_exists($file)) {
unlink($file);
}
}
echo "Cache cleared successfully.";
exit;
}


$params = [
'key' => $youtubeApiKey,
'part' => 'snippet',
'maxResults' => 10,
'q' => urlencode($query),
'type' => 'video',
'order' => 'relevance',
];

$api_call = $youtubeApiUrl . '?' . http_build_query($params);
$response = request($api_call, $opt);
$data = json_decode($response, true);



// Fetch search results
if (isset($data['items'])) {
foreach ($data['items'] as $item) {
$title = $item['snippet']['title'];
$decodedTitle = html_entity_decode($title, ENT_COMPAT | ENT_HTML5, 'UTF-8');

$channel = $item['snippet']['channelTitle'];
$description = $item['snippet']['description'];
$videoId = $item['id']['videoId'];
$thumbnailUrl = $item['snippet']['thumbnails']['default']['url'];

// Generate a unique filename for the cached image
$filename = md5($videoId) . '.png';
$cachePath = __DIR__ . '/icon-cache/' . $filename;

if (!file_exists($cachePath)) {
try {
$ch = curl_init($thumbnailUrl);
$fp = fopen($cachePath, 'wb');
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_exec($ch);
curl_close($ch);
fclose($fp);
} catch (Exception $e) {
// Handle any errors that might occur during the image download
}
}

$wf->result()
->title($decodedTitle)
->subtitle($channel . ' - ' . $description)
->arg('https://www.youtube.com/watch?v=' . $videoId)
->icon($cachePath)
->autocomplete($title)
->data(['cachePath' => $cachePath]); // Store the cache path in the result data
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we also add copy and quicklookurl here?

}
}

// Output search results immediately
echo $wf->output();
?>
Binary file added src/youtube.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.