As much as I love using cloud services, in these days I cannot trust them to continue working. I rely on Google Drive and Gmail, but Google’s reputation in keeping services alive is not exactly stellar.
I have a qnap NAS at home to backup all my clouds in case I cannot access them or some Product Manager decides to kill a service I depend on.
This NAS, apart from serving as a backup also contains a copy of all the files I have produced. As such, it’s a perfect place to search through all my content without wondering if the file was in email, google drive or elsewhere.
I implemented an Alfred workflow for blazing fast local search of all my files and email. Here is me unearthing an article I wrote a decade ago and have stored somewhere in my backup:

Here is how to set it up in case you own a QNAP device:
Setting up QSirch
- Set up your backup policies for Google Drive / Dropbox / Whatever in HBS
- There is an option to autmatically convert Google Docs into .docx locally. That way, even if Google Docs stops existing, you still will be able to open your files.
- Install QMailAgent app to back up your Gmail
- Install QSirch to index your files and emails.
You can use QSirch from your QNAP admin panel, bit it’s just so much friction that I never used it.
Turns out QSirch has an API. Here is a small library I wrote to use it:
QSirch PHP Library
I wrote this simple PHP library to access my QNAP server on the local network:
<?php
class Qnap {
public $url;
public $session = '';
private $user;
private $password;
public $cachefile;
public function __construct( $ip, $user, $password ) {
$this->url = $ip;
$this->user = $user;
$this->password = $password;
// You might want to store this somewhere else, but I want to keep this token locally.
$this->cachefile = $_SERVER['HOME'] . '/.cache/alfredqnaptoken';
$this->session = file_exists( $this->cachefile ) ? file_get_contents( $this->cachefile ) : '';
}
function session_cookie() {
if ( ! $this->session ) {
return '';
}
return "Cookie: QQS_SID={$this->session}\r\n";
}
function request( $url, $data = [], $method = 'GET' ) {
$params = array(
'header' => 'Content-Type: application/json' . "\r\n" . $this->session_cookie(),
'method' => $method,
"ignore_errors" => true,
'content' => json_encode( $data ),
);
$context = stream_context_create( array(
'http' => $params
) );
$result = file_get_contents( $this->url . $url, false, $context );
return json_decode( $result );
}
public function login( $login, $password ) {
$data = array('account' => $login, 'password' => $password );
$data = $this->request( '/qsirch/latest/api/login/', $data, 'POST' );
$this->session = $data->qqs_sid;
file_put_contents( $this->cachefile, $this->session );
return $data;
}
public function search( $str ) {
$result = '';
$request_url = "/qsirch/latest/api/search?sort_by=created&sort_dir=desc&limit=100&q=" . urlencode( $str );
// Maybe we can wing it without relogging in?
if ( $this->session ) {
$result = $this->request( $request_url );
if ( ! isset( $result->error->code ) || $result->error->code !== '101' ) {
return $result;
}
}
// Let's log in
$this->login( $this->user, $this->password );
return $this->request( $request_url );
}
public function map_to_alfred( $results ) {
return array_map( [ $this, 'item_to_alfred' ], $results->items );
}
function metadata_to_array( $item ) {
$meta = [];
if ( ! isset( $item->preview->info ) ) {
return $meta;
}
foreach ( $item->preview->info as $m ) {
$meta[ $m->key ] = $m->value;
}
return $meta;
}
public function item_to_alfred( $item ) {
$file = "/Volumes/{$item->path}/{$item->name}.{$item->extension}";
$title = '';
$m = $this->metadata_to_array( $item );
if ( $item->extension = 'eml' && isset( $m['from'], $m['subject'], $m['sent_date'] ) ) {
$title = explode( ' ', $m['sent_date'] )[0] . ' ' . $m['subject'] . ': ' . $m['from'];
} else if( isset( $item->title, $item->extension ) ) {
$title = "{$item->title}.{$item->extension}";
}
return array(
'title' => $title,
'subtitle' => $item->content ?? '',
'arg' => $file,
'icon' => $this->url . '/' . $item->actions->icon,
'quicklookurl' => $file,
// 'mods'=> array(
// 'cmd'=> array(
// 'valid' => ( !! $note['source_url'] ),
// 'arg' => $note['source_url'],
// 'subtitle' => 'Open URL ' . $note['source_url']
// ),
// 'alt'=> array(
// 'valid' => true,
// 'arg' => $cmdresult,
// 'subtitle' => 'Paste URL ' . $cmdresult
// ),
// )
);
}
}
QSirch Alfred Workflow
This is a very simple Alfred Workflow to search all my files:

