Merge branch 'search_provider' into 'master'
WIP: Implement Shell Search Provider See merge request World/podcasts!100
This commit is contained in:
commit
1cfc80b8f5
393
Cargo.lock
generated
393
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -23,6 +23,7 @@
|
||||
"--socket=pulseaudio",
|
||||
"--device=dri",
|
||||
"--own-name=org.mpris.MediaPlayer2.Podcasts",
|
||||
"--own-name=org.gnome.Podcasts.Devel.SearchProvider",
|
||||
"--env=USE_PLAYBING3=1"
|
||||
],
|
||||
"build-options" : {
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
"--socket=pulseaudio",
|
||||
"--device=dri",
|
||||
"--own-name=org.mpris.MediaPlayer2.Podcasts",
|
||||
"--own-name=org.gnome.Podcasts.SearchProvider",
|
||||
"--env=USE_PLAYBING3=1"
|
||||
],
|
||||
"build-options" : {
|
||||
|
||||
@ -87,6 +87,25 @@ pub(crate) fn get_downloaded_episodes() -> Result<Vec<EpisodeCleanerModel>, Data
|
||||
.map_err(From::from)
|
||||
}
|
||||
|
||||
// Only searches from downloaded episodes
|
||||
pub fn search_downloaded_episodes(terms: Vec<&str>) -> Result<Vec<Episode>, DataError> {
|
||||
use crate::schema::episodes::dsl::*;
|
||||
let db = connection();
|
||||
let con = db.get()?;
|
||||
|
||||
let mut search_term = "".to_string();
|
||||
for term in terms {
|
||||
search_term.push_str(&format!("%{}%", term));
|
||||
}
|
||||
|
||||
episodes
|
||||
.filter(local_uri.is_not_null())
|
||||
.filter(title.like(&search_term))
|
||||
.order(epoch.desc())
|
||||
.load::<Episode>(&con)
|
||||
.map_err(From::from)
|
||||
}
|
||||
|
||||
// pub(crate) fn get_played_episodes() -> Result<Vec<Episode>, DataError> {
|
||||
// use schema::episodes::dsl::*;
|
||||
|
||||
|
||||
@ -28,6 +28,7 @@ reqwest = "0.9.12"
|
||||
serde_json = "1.0.39"
|
||||
# html2text = "0.1.8"
|
||||
html2text = { git = "https://github.com/jugglerchris/rust-html2text" }
|
||||
search-provider = { git = "https://gitlab.gnome.org/World/Rust/search-provider" }
|
||||
|
||||
[dependencies.gettext-rs]
|
||||
git = "https://github.com/danigm/gettext-rs"
|
||||
|
||||
@ -65,4 +65,23 @@ podcasts_resources = gnome.compile_resources(
|
||||
source_dir: meson.current_build_dir()
|
||||
)
|
||||
|
||||
# Search Provider
|
||||
service_conf = configuration_data()
|
||||
service_conf.set('appid', application_id)
|
||||
service_conf.set('bindir', podcasts_bindir)
|
||||
configure_file(
|
||||
input: 'org.gnome.Podcasts.SearchProvider.service.in',
|
||||
output: '@0@.SearchProvider.service'.format(application_id),
|
||||
configuration: service_conf,
|
||||
install_dir: join_paths(datadir,'dbus-1', 'services')
|
||||
)
|
||||
search_conf = configuration_data()
|
||||
search_conf.set('appid', application_id)
|
||||
configure_file(
|
||||
input: 'org.gnome.Podcasts.search-provider.ini',
|
||||
output: '@0@.search-provider.ini'.format(application_id),
|
||||
configuration: search_conf,
|
||||
install_dir: join_paths(datadir, 'gnome-shell', 'search-providers'),
|
||||
)
|
||||
|
||||
meson.add_install_script('../../scripts/compile-gschema.py')
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
[D-BUS Service]
|
||||
Name=@appid@.SearchProvider
|
||||
Exec=@bindir@/gnome-podcasts --gapplication-service
|
||||
@ -66,6 +66,7 @@
|
||||
<kudos>
|
||||
<kudo>ModernToolkit</kudo>
|
||||
<kudo>HiDpiIcon</kudo>
|
||||
<kudo>SearchProvider</kudo>
|
||||
</kudos>
|
||||
<launchable type="desktop-id">@appid@.desktop</launchable>
|
||||
<url type="homepage">https://wiki.gnome.org/Apps/Podcasts</url>
|
||||
|
||||
5
podcasts-gtk/resources/org.gnome.Podcasts.search-provider.ini
Executable file
5
podcasts-gtk/resources/org.gnome.Podcasts.search-provider.ini
Executable file
@ -0,0 +1,5 @@
|
||||
[Shell Search Provider]
|
||||
DesktopId=@appid@.desktop
|
||||
BusName=@appid@.SearchProvider
|
||||
ObjectPath=/org/gnome/Podcasts/SearchProvider
|
||||
Version=2
|
||||
0
podcasts-gtk/resources/org.gnome.Podcasts.service.in
Normal file → Executable file
0
podcasts-gtk/resources/org.gnome.Podcasts.service.in
Normal file → Executable file
@ -87,6 +87,7 @@ mod headerbar;
|
||||
mod window;
|
||||
|
||||
mod manager;
|
||||
mod search_provider;
|
||||
mod settings;
|
||||
mod static_resource;
|
||||
mod utils;
|
||||
|
||||
@ -53,6 +53,7 @@ podcasts_sources = files(
|
||||
'i18n.rs',
|
||||
'main.rs',
|
||||
'manager.rs',
|
||||
'search_provider.rs',
|
||||
'settings.rs',
|
||||
'utils.rs'
|
||||
)
|
||||
|
||||
123
podcasts-gtk/src/search_provider.rs
Normal file
123
podcasts-gtk/src/search_provider.rs
Normal file
@ -0,0 +1,123 @@
|
||||
// search_provider.rs
|
||||
//
|
||||
// Copyright 2019 Felix Häcker <haeckerfelix@gnome.org>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
use crossbeam_channel::Sender;
|
||||
use gtk::prelude::*;
|
||||
use podcasts_data::dbqueries;
|
||||
use search_provider::{ResultMetadata, SearchProvider as SP};
|
||||
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::app::Action;
|
||||
use crate::config::APP_ID;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct SearchProvider {
|
||||
search_provider: Arc<SP>,
|
||||
window: gtk::ApplicationWindow,
|
||||
sender: Sender<Action>,
|
||||
}
|
||||
|
||||
impl SearchProvider {
|
||||
pub(crate) fn new(window: gtk::ApplicationWindow, sender: Sender<Action>) -> SearchProvider {
|
||||
let search_provider = SP::new(
|
||||
APP_ID.to_string(),
|
||||
"/org/gnome/Podcasts/SearchProvider".to_string(),
|
||||
);
|
||||
|
||||
let sp = SearchProvider {
|
||||
search_provider,
|
||||
window,
|
||||
sender,
|
||||
};
|
||||
|
||||
sp.setup_callbacks();
|
||||
sp
|
||||
}
|
||||
|
||||
fn setup_callbacks(&self) {
|
||||
let window = self.window.clone();
|
||||
let sender = self.sender.clone();
|
||||
self.search_provider
|
||||
.connect_activate_result(move |sp_id, _, timestamp| {
|
||||
// Show window
|
||||
window.present_with_time(timestamp);
|
||||
window.show_all();
|
||||
window.present();
|
||||
|
||||
// Get episode
|
||||
let mut id = sp_id.clone().split("_").collect::<Vec<&str>>();
|
||||
let title: &str = id.pop().unwrap();
|
||||
let show_id: i32 = std::str::FromStr::from_str(id.pop().unwrap()).unwrap();
|
||||
let episode = dbqueries::get_episode_from_pk(title, show_id).unwrap();
|
||||
|
||||
// Play episode
|
||||
sender
|
||||
.send(Action::InitEpisode(episode.rowid()))
|
||||
.expect("Action channel blew up somehow");
|
||||
});
|
||||
|
||||
self.search_provider
|
||||
.connect_get_initial_result_set(|terms| Self::search(terms));
|
||||
|
||||
self.search_provider
|
||||
.connect_get_subsearch_result_set(|_, terms| Self::search(terms));
|
||||
|
||||
self.search_provider.connect_get_result_metas(|sp_ids| {
|
||||
let mut metas = Vec::new();
|
||||
for sp_id in sp_ids {
|
||||
let id = sp_id.clone().split("_").collect::<Vec<&str>>();
|
||||
let show_id: i32 = FromStr::from_str(id[0]).unwrap();
|
||||
|
||||
let episode = dbqueries::get_episode_from_pk(id[1], show_id).unwrap();
|
||||
|
||||
let image_uri = &dbqueries::get_podcast_cover_from_id(show_id)
|
||||
.map(|cover| {
|
||||
cover
|
||||
.image_uri()
|
||||
.unwrap_or("image-x-generic-symbolic")
|
||||
.to_owned()
|
||||
})
|
||||
.unwrap_or(String::from("image-x-generic-symbolic"));
|
||||
|
||||
let meta = ResultMetadata::new(
|
||||
sp_id,
|
||||
episode.title(),
|
||||
image_uri,
|
||||
episode.description().unwrap_or(""),
|
||||
);
|
||||
metas.insert(0, meta);
|
||||
}
|
||||
metas
|
||||
});
|
||||
}
|
||||
|
||||
fn search(terms: Vec<&str>) -> Vec<String> {
|
||||
let episodes = dbqueries::search_downloaded_episodes(terms)
|
||||
.expect("Could not search for episodes (search provider)");
|
||||
|
||||
let mut sp_ids = Vec::new();
|
||||
for episode in episodes {
|
||||
let sp_id = format!("{}_{}", episode.show_id(), episode.title());
|
||||
sp_ids.insert(0, sp_id);
|
||||
}
|
||||
sp_ids
|
||||
}
|
||||
}
|
||||
@ -29,6 +29,7 @@ use crossbeam_channel::{unbounded, Receiver, Sender};
|
||||
|
||||
use crate::app::{Action, PdApplication};
|
||||
use crate::headerbar::Header;
|
||||
use crate::search_provider::SearchProvider;
|
||||
use crate::settings::{self, WindowGeometry};
|
||||
use crate::stacks::Content;
|
||||
use crate::utils;
|
||||
@ -149,6 +150,9 @@ impl MainWindow {
|
||||
glib::Continue(true)
|
||||
});
|
||||
|
||||
// Setup GNOME Shell search provider integration
|
||||
let search_provider = SearchProvider::new(window.clone(), sender.clone());
|
||||
|
||||
Self {
|
||||
app: app.clone(),
|
||||
window,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user