267 Commits
Author SHA1 Message Date
Jordan Petridis 885b796f85 New release 0.4.6 2018-10-07 10:18:15 +03:00
Jordan Petridis fa47806c93 Update Changelog 2018-10-07 10:18:14 +03:00
Emin Tufan Çetin 5997666bad Update Turkish translation 2018-10-06 15:53:09 +00:00
Jordan Petridis 40186ce155 Update dependancies 2018-10-06 11:29:52 +03:00
Jordan Petridis 9cfdb35224 AboutDialog: Update contributors list 2018-10-06 11:09:07 +03:00
Jordan Petridis 64860c4624 Update screenshots 2018-10-06 10:32:46 +03:00
Jordan Petridis 32bd2a89a3 Stacks: Check if there episodes insteads of shows
If you added a Feed where a Show exists but it had no episodes
entries, the stack would end up in a populated state, but the
HomeView would be blank without widgets.

This changes it so the stack state depends upon the episodes
table being populated instead of the show. The downside
is that if your only feed is one without episodes you can
no longer navigate and interact with it.
2018-10-06 10:22:24 +03:00
Rafael Fontenelle 1e6eca307b Update Brazilian Portuguese translation 2018-10-05 14:19:31 +00:00
Jordan Petridis fc23fcd7c1 Merge branch 'patch-1' into 'master'
Fix typo

See merge request World/podcasts!78
2018-10-05 11:47:38 +00:00
Alexandre Franke ab52825c71 Fix typo 2018-10-05 10:04:44 +00:00
Jordan Petridis 28def30510 Update CHANGELOG.md 2018-10-05 09:25:56 +00:00
Florian Heiser 8f4d017180 Update German translation 2018-10-05 04:41:29 +00:00
Jordan Petridis df302ad517 Pipeline: Do not terminate the stream upon errors
Stream::for_each terminated the stream upon the first error. This
was causing feeds to not update if any one returned a non-200ish
result. To work around this, we create a succesfull result for
every entry regardless at the end.

While we are at it, aslo switch from FuturesOrdered stream
to FuturesUnordered. There is no reason to use Ordered, this was
a typo initially.
2018-10-04 18:48:11 +03:00
Jordan Petridis 357d99ac7c Remove temporary file 2018-10-04 17:18:07 +03:00
Jordan Petridis 7e3fecc44a Source: Refactor the clear_etags method api 2018-10-04 16:46:04 +03:00
Jordan Petridis e0b3dd9795 Mpris: Implement the raise method 2018-10-04 16:46:04 +03:00
Jordan Petridis 6a52a2bc46 Merge branch 'alatiera/player-timeout' into 'master'
Player: Tweak the smart rewind behavior

See merge request World/podcasts!76
2018-10-04 07:55:21 +00:00
Jordan Petridis cd2b087006 Player: Check the episode id before triggering a smart rewind 2018-10-04 05:27:11 +00:00
Jordan Petridis ef2940142c PlayerInfo: Store the id of the current playing episode 2018-10-04 05:27:11 +00:00
Jordan Petridis e13b8b8827 Player: Tweak the smart rewind behavior
Check if time interval passed since the last pause, and only
rewind if the delta to indicates that the user had
switched their focus.

In other words, avoid rewinding if the track was just paused and
resumed.
2018-10-04 05:27:11 +00:00
Jordan Petridis 43fce2e89a CI: Use generic bundle name
When run an environment CI_COMMIT_SHA is not resolved and results
into a 404 url in the review apps
2018-10-04 08:25:50 +03:00
Jordan Petridis 7856f0d602 Merge branch 'mpris' into 'master'
Implement MPRIS, Close #68

See merge request World/podcasts!74
2018-10-04 03:26:03 +00:00
Jordan Petridis bcc6ab50e2 PlayerInfo: Minor refactor of the mpris cover 2018-10-04 02:19:56 +00:00
Jordan Petridis 23aa8c05ab Player: Do not duplicate the mpris instance 2018-10-04 02:19:56 +00:00
Jordan Petridis 986d898217 Player: Remove an unwrap 2018-10-04 02:19:56 +00:00
Jordan Petridis 654c0e5e56 Player: Remove Initialization test
When constucting Player in the Sandbox, it tries to use X-11
for dbus-autolaunch which is disabled in the flatpak environment.

It fails with the following error:
D-Bus error: Using X11 for dbus-daemon autolaunch was disabled at compile time, set your DBUS_SESSION_BUS_ADDRESS instead (org.freedesktop.DBus.Error.NotSupported)
2018-10-04 02:19:56 +00:00
Felix Häcker fed0edbf16 Player: use crates.io package of mpris 2018-10-04 02:19:56 +00:00
Felix Häcker ede91da6f8 cargo fmt 2018-10-04 02:19:56 +00:00
Felix Häcker 1f18d4291f Player: implement fast forward / rewind for mpris 2018-10-04 02:19:56 +00:00
Felix Häcker d066e8939d Player: implement mpris play/pause callbacks 2018-10-04 02:19:56 +00:00
Felix Häcker e4c3435d34 Player: add basic mpris support 2018-10-04 02:19:56 +00:00
Jordan Petridis fc80b180bc CI: Allow rusftm check to fail for now
Stuff broke with rustfmt nightly 0.99.5 and I haven't found time
to look into it yet.
2018-10-04 05:17:21 +03:00
Jordan Petridis 0d9dca99e9 Meson: capture the output of the custom target
Previously meson would withhold the stdout output, and only
print it once the job was finished. This was a bit problematic
with the current way we wire cargo into meson, cause you have
no output of whats going on till then causing a degraded UX.

With this change output is printed directly to stdout and
behaves as expected.

https://mesonbuild.com/Reference-manual.html#custom_target
2018-10-04 04:58:57 +03:00
Kukuh Syafaat 2ff921cc1a Update Indonesian translation 2018-10-02 13:34:32 +00:00
Piotr Drąg e0e66fa6af Update Polish translation 2018-09-30 17:17:46 +00:00
Marek Černocký 110e29ec5a Update Czech translation 2018-09-29 19:31:22 +00:00
Jordan Petridis a2b6d622de Update dependancies 2018-09-29 13:58:45 +03:00
Jordan Petridis 0887789f5e Pipeline: Complete the move to Tokio Runtime 2018-09-29 13:35:45 +03:00
Jordan Petridis 3c4574f2ec scripts/test.sh: Don't keep build-dirs 2018-09-29 12:07:21 +03:00
Jordan Petridis 62029f6164 Pipeline: Minor refactor
Use the proper Stream API to return a future to run on the
executor. Previously I was using a workaround to convert the
Stream into a future and run it to completion in the Executor,
since I was not aware of a better API.
2018-09-29 12:03:00 +03:00
Jordan Petridis ba986847d6 Merge branch 'wip/hyper-12.X-update'
See merge request World/podcasts!75
2018-09-29 11:42:53 +03:00
Jordan Petridis fded78ce6f Flatpak: Switch to the master runtime again 2018-09-29 11:18:39 +03:00
Jordan Petridis 8d44649a1e Update dependancies 2018-09-29 09:54:06 +03:00
Jordan Petridis f4d0c51dc2 podcasts-downloader: Upgrade reqwests to 0.9 2018-09-29 09:54:06 +03:00
Jordan Petridis ba60db9977 podcasts-gtk: Upgrade reqwests to 0.9 2018-09-29 09:54:06 +03:00
Jordan Petridis 0a5a7a684d podcasts-downloader: Remove direct dependancy on hyper 2018-09-29 09:54:06 +03:00
Christopher Davis 7181c46ed5 podcasts-data: Upgrade hyper to 12.X
This also allows us to bump hyper_tls and native_tls, bringing
support for openssl 1.1.
2018-09-29 09:54:06 +03:00
Jordan Petridis afa7e69347 Update CHANGELOG.md 2018-09-28 23:56:51 +00:00
Jordan Petridis 5050dda4d2 Hamburger: Update to reflect the latest HIG changes
Preferences are now grouped with the application actions.
The About label was renamed to "About Podcasts".

https://mail.gnome.org/archives/desktop-devel-list/2018-September/msg00015.html
2018-09-29 02:43:19 +03:00
Florian Heiser 9ea1e16ac8 Update German translation 2018-09-27 15:32:33 +00:00
Rodrigo Lledó 922f44f605 Update Spanish translation 2018-09-24 10:17:44 +00:00
Adolfo Jayme Barrientos 6ea3fc918b Add Catalan translation 2018-09-24 07:52:18 +00:00
Kukuh Syafaat 0c201533f0 Update Indonesian translation 2018-09-22 08:20:38 +00:00
Jordan Petridis b1e99b96c4 CI: Switch the flatpak image 2018-09-22 04:53:08 +03:00
Jordan Petridis de1c8485ae Fix rustc warnings
New warnings were introduced with rustc 1.29.0

podcasts-data/src/lib.rs: this one can be removed once diesel is
upgraded.
https://github.com/diesel-rs/diesel/issues/1785#issuecomment-422577018

podcasts-gtk/src/i18n.rs: This is just a deprication warning
2018-09-22 04:47:24 +03:00
Jordan Petridis 565d1d0388 Build against the 3.28 runtime for now
Freedesktop 18.08.11 dropped openssl 1.0 which caused hyper to
no longer build. Reverting to the old runtime till hyper is
upgraded. See #84 for more.
2018-09-22 04:47:24 +03:00
gogo a15fea1d65 Update Croatian translation 2018-09-19 12:24:57 +00:00
Jordan Petridis da3cf6ca27 Flatpak: Pass configuration to libhandy
Do not build what we don't need.
2018-09-18 12:43:51 +03:00
Emin Tufan Çetin d8dbbc6832 Update Turkish translation 2018-09-15 20:11:23 +00:00
Marek Černocký 3563a964ef Add Czech translation 2018-09-15 05:15:51 +00:00
Jordan Petridis 208f0c248d Update rest of the dependancies 2018-09-13 15:47:40 +03:00
Jordan Petridis c0e034726a Upgrade gtk-rs and friends
Upgrade gtk-rs, and everything dependings on it like gst-rs and
libhandy bindigns
2018-09-13 15:47:40 +03:00
Rafael Fontenelle d676a7071a Update Brazilian Portuguese translation 2018-09-11 00:12:22 +00:00
Rūdolfs Mazurs a681b2c944 Add Latvian translation 2018-09-10 17:34:18 +00:00
Mario Blättermann c53701d56b Update German translation 2018-09-10 07:50:51 +00:00
Anders Jonsson a8c1f2eccc Update Swedish translation 2018-09-09 17:39:22 +00:00
Piotr Drąg cf7ee44efc Update Polish translation 2018-09-09 17:00:14 +00:00
Jordan Petridis 2e4a9eaaeb Update dependancies 2018-09-08 21:41:39 +03:00
Jordan Petridis 569a2b5694 Update CHANGELOG.md 2018-09-08 20:17:48 +03:00
Jordan Petridis baa84773a5 EpisodeWidget: Change the "cancel" action icon 2018-09-08 20:17:47 +03:00
Jordan Petridis a39e642b5a EpisodeWidget: Change cacnel button to an icon
This also reworks the button_box to be just a GtkBox instead.
I couldn't get ButtonBox to behave the way I wanted.

Fixes #89
2018-09-08 20:17:47 +03:00
Jordan Petridis e42cb49cbe EpisodeWidget: Hide total_size if request fails
This moves the rest of the methods of Progress struct to the downloader
trait and cancels the Progress if the request does nto succed.

Close #90
2018-09-08 20:17:47 +03:00
Jordan Petridis b40c12efbd Pipeline: Avoid spamming stderr when not needed
This commit add a new DataError Variant for feeds that return 304.
Its expected behaviror and the current API of Source::into_feed
is kinda limiting the return type to make it easier to handle.

Up till now 304 was returning an Error to early return. Ideally
Source::into_feed will return a Multi variant Result Enum.

example:

enum FeedResult {
    Ok(Success(feed)),
    Ok(NotModified),
    Err(err),
}

Hopefully in a refactor in the near Future™

Till then we will just have to match and ignore
DataError::FeedNotModified.
2018-09-08 20:17:47 +03:00
Jordan Petridis 3c5ddad133 DataError: Improve the FeedRedirect variant 2018-09-08 20:17:47 +03:00
Jordan Petridis 1ca4cb40dd Pipeline: clear the imports of fututres 2018-09-08 20:17:47 +03:00
Jordan Petridis 678b0b9db1 BaseView: Set minimum width to 360
While the HomeView and ShowView can't yet scale that low,
the ShowWidget could get to about 270p already which is not
desirable.

This commit sets the minimum width of all the Views to 360p,
which is our mobile target size.
2018-09-08 20:17:47 +03:00
Jordan Petridis e633fa41ac Pipeline: Minor formatting improvment 2018-09-08 20:17:47 +03:00
Jordan Petridis dac303e33b Pipeline: Use a custom tokio threadpool
This reverts commit e64883eecb
and 40dd2d6923

Seems like core.run() returns once its done even if there
are still tasks in the Runtime underneath. A way to solve that
would be to call the shutdown_on_idle method.

We need ownership of the threadpool in order to invoke
`shutdown_on_idle` method but core.runtime only returns a
referrence so we need to create our own threadpool.
2018-09-08 20:17:46 +03:00
Jordan Petridis fd77d672c5 Update Changelog 2018-09-08 20:17:46 +03:00
Jordan Petridis a991d9f512 DataError: Remove unused error variant
This was added due to Threadpool::spawn returning errors, but its
no longer used.
2018-09-08 20:17:46 +03:00
Jordan Petridis 674b3b54dc Pipeline: reuse the prexisting runtime executor
Instead of creating our own threadpool, we should reuse the executor
of the tokio::runtime::Runtime that backs the tokio::reactor::Core.
2018-09-08 20:17:46 +03:00
Jordan Petridis 09948845ca Pipeline: Remove use of clone! macro 2018-09-08 20:17:46 +03:00
Jordan Petridis 0b8a0695f7 Pipeline: Use tokio threadpool to index feeds 2018-09-08 20:17:46 +03:00
Jordan Petridis a7c95d5718 Pipeline: Remove dependancy on rayon_futures
This requires a RUSTFLAG to be set before hand for rayon to build.
This brakes a lot of tools like rls and clippy by default and
require special configs for itnegration.

Additionally, rayon_futures is still 0.1 and not much work seem
to have gone into it. Ideally it should be replased with the tokio
runtime/threadpool.
2018-09-08 20:17:46 +03:00
Jordan Petridis 8a05597e52 Feed: Remove another unnecessary wrapper 2018-09-08 20:17:45 +03:00
Jordan Petridis 145d45f800 Feed: Remove unnecessary function wrapper 2018-09-08 20:17:45 +03:00
gogo 0476b67b2f Add Croatian translation 2018-09-06 18:29:14 +00:00
Balázs Meskó 2751a828e0 Add Hungarian translation 2018-09-05 22:09:46 +00:00
Jordan Petridis a3f5dbfe07 CI: cache only the sources from flatpak-builder
Gitlab CI zips its cache target which causes it to strip
xattributes. This breaks ostree and the cache artifacts.

Cache only the sources instead.
2018-09-05 00:47:45 +03:00
Fabio Tomat 60e09c0dd7 Add Friulian translation 2018-09-04 18:18:27 +00:00
Milo Casagrande a23297e56a Add Italian translation 2018-09-03 13:42:38 +00:00
Jordan Petridis b05163632b test.sh: build with devel profile as well 2018-09-03 11:17:03 +03:00
Daniel Mustieles f59be31ded Update Spanish translation 2018-09-03 06:32:14 +00:00
Anders Jonsson 2e527250de Add Swedish translation 2018-09-02 23:35:41 +00:00
Piotr Drąg 91dd378f5d Update Polish translation 2018-09-01 17:49:54 +00:00
Jiri Grönroos f5b3d033a3 Update Finnish translation 2018-09-01 16:59:07 +00:00
Rafael Fontenelle db98e3b722 Add Brazilian Portuguese translation 2018-09-01 13:29:55 +00:00
Mario Blättermann 586cf16fdc Update German translation 2018-08-31 19:05:31 +00:00
Jordan Petridis 064d877205 Appdata: add release notes 2018-08-31 21:00:18 +03:00
Jordan Petridis fbf8cc87c9 Version bump 2018-08-31 14:14:17 +00:00
Jordan Petridis bebabf84a0 Update Changelog 2018-08-31 14:11:29 +00:00
Seong-ho Cho 36f169635a Add Korean translation 2018-08-31 00:56:44 +00:00
Jordan Petridis 3f509f44a1 Update dependancies. 2018-08-30 20:45:53 +03:00
Jordan Petridis 9bc8a8ac2b Release: include meson_options.txt 2018-08-30 20:21:11 +03:00
Jordan Petridis abfe98283b EpisodeWidget: Allow size labels to elipsize 2018-08-30 20:21:10 +03:00
Kukuh Syafaat ded0224f51 Add Indonesian translation 2018-08-30 10:43:20 +00:00
Fran Dieguez 0060a634d2 Add Galician translation 2018-08-30 08:45:45 +00:00
Jordan Petridis d34005e04f Merge branch 'chng-readme' into 'master'
README.md: Change instructions for translators

See merge request World/podcasts!68
2018-08-29 08:43:48 +00:00
Rafael Fontenelle c734bd48b5 README.md: Change instructions for translators
Change translation instructions to steer the translator to the translation platform D-L, to follow the proper workflow of the language teams.
2018-08-29 07:29:38 +00:00
Jordan Petridis 174c814541 Merge branch 'alatiera/cover_load_perf' into 'master'
Cover loading performance improvments

See merge request World/podcasts!67
2018-08-29 07:29:17 +00:00
Jordan Petridis 993b6e9d0a Utils: only queue a single cover download
Before we were inserting the id of the cover into the registry
from a rayon thread. But rayon will only execute N threads at the
same time and let the rest into a queue. This would casue mutliple
jobs being queued since the cover id was not inserted in the
registry until the downloading had started.

This fixes said behavior by having the main thread block and write
in the id in the registry.
2018-08-28 21:15:52 +00:00
Jordan Petridis 273c9f7b99 Utils: Change the priority of the cover caches
Since loadign a pixbuf from the pre-rendered cache is the most
common operation and it does not affect the behavior we can
first check that and then if the cover is midway downloading.

This avoids a mutex lock for the most common path.
2018-08-28 21:15:52 +00:00
Jordan Petridis 822deb2867 Utils: do not block the cover_dl registry
Accidently after f21398357b when a download would start,
it would lock the cover_dl_registry hashmap till it had finished.

Since the registry.read() happens on the main thread this would
cause the UI to block until the download was and the mutex guard
from the download thread dropped.
2018-08-28 21:15:52 +00:00
Merge Bot 132e2afce0 Merge branch 'wip/piotrdrag/update-potfiles-180828' into 'master'
Update POTFILES.in

See merge request World/podcasts!66
2018-08-28 20:54:09 +00:00
Piotr Drąg 2a888f0bce Update POTFILES.in 2018-08-28 19:44:34 +02:00
Jordan Petridis f8202a7add Merge branch 'alatiera/meson-stuff' into 'master'
Parallel installation and meson cleanups

See merge request World/podcasts!64
2018-08-28 16:13:31 +00:00
Jordan Petridis 87e8d0b775 Fix the test-suite 2018-08-28 17:50:57 +03:00
Jordan Petridis 569c00ff5f Allow for parallel development instance
This adds a configuration option in meson, if set it changes the
application ID allowing for stable and development version to be
run at the same time.
2018-08-28 17:22:13 +03:00
Jordan Petridis 15457e1db4 App/Build: Use env! macro to fetch the variable
env! is resolved at compile time which means we don't need to read
and set LOCALDI from build.rs
2018-08-28 17:22:13 +03:00
Jordan Petridis 0ae1eb9578 build: Translate desktop and appdata files 2018-08-28 17:22:13 +03:00
Jordan Petridis aa1d0161d3 cargo fmt 2018-08-27 21:08:29 +03:00
Emin Tufan Çetin a2d8b88337 Update Turkish translation 2018-08-27 08:43:08 +00:00
Jordan Petridis a1b4306954 Merge remote-tracking branch 'zander/master'
See !65 for more.
2018-08-27 10:53:07 +03:00
Zander Brown 88e07031a6 Workaround for FileChooserNative oddness 2018-08-27 07:52:09 +00:00
Piotr Drąg cb4daa1ba1 Update Polish translation 2018-08-26 16:31:44 +00:00
Jordan Petridis 46cfa79e89 Update .gitignore 2018-08-26 12:39:26 +03:00
Jordan Petridis a480f47cea cargo fmt 2018-08-26 12:38:36 +03:00
Jordan Petridis 4ef789e7b9 Meson: Fix build_always deprication warning
build_always has been replaced by build_always_stale and
build_by_default is assumed now.

https://mesonbuild.com/Reference-manual.html#custom_target
2018-08-26 12:38:36 +03:00
Mario Blättermann 05d6d8399d Update German translation 2018-08-25 21:27:45 +00:00
Jordan Petridis 8bc81d6b5e Update pot files 2018-08-22 08:29:30 +03:00
Jordan Petridis 6c5cb8f07d AboutDialog: Translate website label 2018-08-22 08:29:12 +03:00
Jordan Petridis 70e79e50d6 Change the website to point to the wiki page
Thanks a lot to @svito for creating the page!
2018-08-21 20:11:38 +03:00
Jordan Petridis d2c6c6cc4d Update pot files 2018-08-20 14:09:34 +03:00
Jordan Petridis ba5e22bd21 README: Add flathub banner 2018-08-20 13:55:15 +03:00
Jordan Petridis 9d0bfdea44 README: Update build instructions 2018-08-20 13:41:19 +03:00
Jordan Petridis 92ae681517 Add gtk tests
Currently we only test the GtkBuilder files.
Also I can't find a way to get gtk to uninitialize and reinitialize
in a different thread.

Close #56
2018-08-20 13:14:41 +03:00
Jordan Petridis 3afa8c4441 Flatpak: avoid the git redirect 2018-08-20 05:27:31 +03:00
Emin Tufan Çetin 547fdef9c4 Update Turkish translation 2018-08-19 14:54:27 +00:00
Merge Bot 152c250300 Merge branch 'alatiera/content_states' into 'master'
content states

Closes #71

See merge request World/podcasts!63
2018-08-19 11:56:06 +00:00
Jordan Petridis 04161284a7 Headerbar: Make the switcher insensitive if empty
If there are no shows/episodes to display, there isn't any point
to being able to hit the switcher.
2018-08-19 14:31:27 +03:00
Jordan Petridis 14d4818867 App: Disable refresh action while in empty state
Close #71
2018-08-19 14:25:41 +03:00
Jordan Petridis 9f42e91088 Refactor content state with Application actions
Instead of each view/widget determening if its populated on its own,
make add Application Actions and apply the state globally.
2018-08-19 13:44:11 +03:00
Jordan Petridis 79ac3b9700 Update dependancies 2018-08-18 18:33:26 +03:00
Jordan Petridis 7a3178896b Remove criterion
Haven't been able to use it effectivly with futures. Maybe will
revisit it again at a later time. For now it just adds extra
build time.
2018-08-18 18:02:28 +03:00
Jordan Petridis ee95512321 Update .gitignore 2018-08-18 17:37:06 +03:00
Jordan Petridis 55519b1855 scripts/test.sh: avoid conlfict with vanila cargo
I am not sure why, but cargo does not like it if flatpak-builder
and gnome Builder try to use the same target/ directory, even
though the environment should be almost identical.

The only difference is that Builder uses flatpak build instead of
flatpak-builder as far as I can see.
2018-08-18 17:34:49 +03:00
Jordan Petridis 89b99614a0 Refactor the tests to use ? operator
As of ructc 1.27 #[test] can return errors. This improves a great
deal the ergonomics.
2018-08-18 17:02:31 +03:00
Jordan Petridis 7bbd9a1a4f Update the test script 2018-08-18 16:25:43 +03:00
Tobias Bernard 0dfb48593e App icon: orange -> red 2018-08-17 12:49:44 +02:00
Jordan Petridis 3abe0803d6 Source: Remove reduntant save() call
Source::clear_etags does a save already, so it's not needed to call
it twice.
2018-08-17 09:57:49 +03:00
Merge Bot 1e0a919dc7 Merge branch 'alatiera/source-redirects-85' into 'master'
Source: Improve http redirections handling

Closes #85

See merge request World/podcasts!62
2018-08-17 04:33:12 +00:00
Jordan Petridis 489e8aa4b3 Source: Improve http redirections handling
We should follow all redirects, and update the Source uri for
301, 302 and 308 codes.

Closes #85
2018-08-17 07:09:12 +03:00
Merge Bot 5058f2f8d8 Merge branch 'alatiera/source-etags-71' into 'master'
Source: Only save Etag headers upon succesful requests

Closes #64

See merge request World/podcasts!61
2018-08-17 02:56:04 +00:00
Jordan Petridis 49aff9f22e Source: Only save Etag headers upon succesful requests
Additionally clear the Etags if the returned code is not 200 or 304.
Just to be extra safe. This is not as clean as it should, as this is
a temporary workaround until the API is reworked.

Related to #64
2018-08-17 05:36:34 +03:00
Jordan Petridis 775d4accf7 Flatpak: switch to master branch of the runtime 2018-08-16 08:30:46 +03:00
Jordan Petridis 703a1baf8b CI: Move repo export right before the bundle generation 2018-08-16 08:30:42 +03:00
Jordan Petridis 51fdad2ae2 CI: Use variables instread of hardcoding things 2018-08-16 08:30:38 +03:00
Jordan Petridis 86545e5f99 CI: Add git commit sha in the bundle name 2018-08-16 08:30:34 +03:00
Jordan Petridis 5a0413b3e4 CI: Remove --libdir=/app/libdir from meson config
This is not needed anymore I think.
2018-08-16 08:30:29 +03:00
Jordan Petridis a0ff2b8ae4 CI: set CARGO_HOME before cargo test
This is set in the scripts/cargo.sh during building, but since we
invoke cargo test manually we need to also set it here.
2018-08-16 08:30:24 +03:00
Mario Blättermann 6b6c390cb8 Add German translation 2018-08-15 17:19:11 +00:00
Jordan Petridis cd937c4844 utils: Refactor refresh_feed
Move channel creation inside the thread.
Drop the Result return type as its not needed anymore.
2018-08-14 15:24:25 +03:00
Jordan Petridis cc1a5783fd App: Do not update the db if its empty
If the source table is empty skipp the database refresh.
2018-08-14 15:19:31 +03:00
Jordan Petridis 5631caad36 Gitlab: Fix the bug issue template
Forgot to change this when it was copied from nautilus.
2018-08-14 14:23:35 +03:00
Jordan Petridis d0144fb0d0 CI: Do not spin up review enviorment for tags 2018-08-14 14:11:16 +03:00
Jordan Petridis 5d1870c1cf CI: Lower flatpak bundle lifespan 2018-08-14 14:03:06 +03:00
Jordan Petridis fb87460bc7 CI: Clear unused stuff
No point on keeping them around since libhandy was added as a dep.
No distro packages libhandy currently, so only the flatpak will be
buildable for the foreseeable future.
2018-08-14 14:01:14 +03:00
Jordan Petridis cb122cbc61 ShowWidget: Change description wrap mode 2018-08-14 13:59:42 +03:00
Jordan Petridis efc4e299ac CI: Add env viariable for the tests 2018-08-14 13:58:09 +03:00
Jordan Petridis 848caa275b CI: re-enable the test-suite 2018-08-14 13:41:12 +03:00
Jordan Petridis 03754c56c6 Fix the test-suite 2018-08-14 13:40:37 +03:00
Jordan Petridis 471f6ff93b Source: Remove ignore_etags option
This is never used anywhere else apart from the testsuite. Instead
of ignoring etags we should instead not save them if the feed does
not return 200 or 304. See #64.
2018-08-14 13:40:33 +03:00
Jordan Petridis c53ad56a6d Remove TODOs and FIXMEs
They are either no longer relevant or just forgotten
2018-08-14 13:40:28 +03:00
Tobias Bernard 646439d86a Merge branch 'alatiera/move-update-notif' into 'master'
Move the update indication into an In-app Notification

Closes #72

See merge request World/podcasts!60
2018-08-14 10:11:28 +00:00
Jordan Petridis ae7f65e938 InAppNotif: Switch the timer to milliseconds
This allows for more responsive updates. The implementation still
sucks though. Ideally we would pass a receiver in the callback
and have an even lower timeout_add.
2018-08-14 07:58:29 +03:00
Jordan Petridis b2d71a037c Headerbar: Remove the update indicator 2018-08-14 07:52:41 +03:00
Jordan Petridis 019ec8972f InAppNotif: Add a spinner 2018-08-14 07:41:58 +03:00
Jordan Petridis e25e411ebe App: Use the new updater notif
Initial wiring of the new InAppNotif update indicator. This still
misses a spinner, and its overall teribly implemented!
2018-08-14 07:28:10 +03:00
Jordan Petridis 911dcbac9f InAppNotif: Pass revealer to the callback
Let the callback handle if/when the visibility of the notification
2018-08-14 06:22:02 +03:00
Jordan Petridis 25195c972c InAppNotif: add a method to show/hide the close button
This will enable us to create persistant notifications.
2018-08-14 05:36:17 +03:00
Jordan Petridis 304c92f733 InAppNotification: Allow to set a custom timer
This allows for a custom timer to be set before the
callback will be run. Currently all the callbacks only
run once and then retunr glib::Continue(false) but this
would allow for setting a low timer and have a callback
that would determine if it needs to be run again, Continue(true),
in a relative responsive way.
2018-08-13 09:13:39 +03:00
Jordan Petridis 336b9a126e InAppNotif: Fix ref cycles 2018-08-13 09:13:10 +03:00
Jordan Petridis 01efbf5c79 InAppNotif: Refactor to infer the undo state
If we use an Optional instead of passing empty closures, we
can infer if the Undo button needs to be shown.
2018-08-13 08:31:52 +03:00
Merge Bot, Bors Wannabe 866fa6a758 Merge branch 'fix-ref-cycles' into 'master'
Fix more refference cycles

See merge request World/podcasts!59
2018-08-13 04:28:49 +00:00
Jordan Petridis b8bb5e6d82 ShowMenu: Fix a reference cycle 2018-08-13 06:48:39 +03:00
Jordan Petridis cc4b3cce55 Player: Fix a refference cycle 2018-08-13 06:35:19 +03:00
Jordan Petridis 5699562133 App: Fix more refference cycles 2018-08-13 03:34:37 +00:00
Piotr Drąg dae064d2bb Update Polish translation 2018-08-13 01:45:08 +00:00
Jordan Petridis d54e15cd15 EmptyView: Fix a typo 2018-08-13 04:05:28 +03:00
Jordan Petridis dbdf56d494 App: Do not placeholder strings as translatable
Also add license metadata, and update the targeted gtk3 version
2018-08-13 02:34:53 +03:00
Jordan Petridis b07cd5515a Refactor Empty states 2018-08-13 02:05:09 +03:00
Emin Tufan Çetin 73929f2d25 Add Turkish translation 2018-08-12 11:05:29 +00:00
Jordan Petridis acaa06749e ShowWidget: Keep the Frame from filling available the space 2018-08-12 02:41:22 +03:00
Piotr Drąg 1bd6efc0c1 Add Polish translation 2018-08-11 16:43:15 +00:00
Jiri Grönroos 936960269d Add Finnish translation 2018-08-11 14:10:14 +00:00
Jordan Petridis 5780df20ad Merge branch 'wip/piotrdrag/gschema-gettext' into 'master'
gschema: Add gettext-domain

See merge request World/podcasts!56
2018-08-11 09:24:05 +00:00
Piotr Drąg 14e5f33f2a gschema: Add gettext-domain
Without it DConf Editor won’t show localized schemas.
2018-08-11 01:35:28 +02:00
Jordan Petridis 7aa86bcec4 Merge branch 'wip/piotrdrag/update-potfiles-180810' into 'master'
Update POTFILES.in

See merge request World/podcasts!54
2018-08-10 23:20:38 +00:00
Piotr Drąg c77a1e85a4 Update POTFILES.in 2018-08-10 20:58:18 +00:00
Jordan Petridis 093b8cb6df Merge branch 'wip/piotrdrag/desktop-comments' into 'master'
desktop: Add translator comments

See merge request World/podcasts!55
2018-08-10 20:56:30 +00:00
Piotr Drąg 6460198e1d desktop: Add translator comments 2018-08-10 22:03:04 +02:00
Jordan Petridis 662dc3fa85 HomeView: Properly align the frames
d5ea0d5a17 broke alignment
between the listboxes and the frame labels.
2018-08-10 22:49:54 +03:00
Merge Bot, Bors Wannabe 5a4bce2816 Merge branch 'alatiera/libhandy' into 'master'
Refactor views and use HdyColumn for views with that display `EpisodeWidget`s

Closes #70

See merge request World/podcasts!48
2018-08-10 18:35:58 +00:00
Jordan Petridis d5ea0d5a17 HoveView and ShowWiget: Add margins around the listboxes 2018-08-10 21:14:49 +03:00
Jordan Petridis 9e525727fd InAppNotif: allow the text to elipsize
Also fixe deprications in the glae file, add license
2018-08-10 20:49:59 +03:00
Jordan Petridis 95b6995649 Headerbar: Elipsisize update label 2018-08-10 15:54:12 +03:00
Jordan Petridis 745064c5ce AddPopOver: Remove deprication warning 2018-08-10 15:47:20 +03:00
Jordan Petridis 223a3b46bf Headerbar: Elipsisize show title 2018-08-10 15:46:28 +03:00
Jordan Petridis fee3e320ab ShowWidget: Move the listbox back to the glade file 2018-08-10 15:43:19 +03:00
Jordan Petridis e068cff37b ShowWidget: Increase the description char limit 2018-08-10 14:44:02 +03:00
Jordan Petridis 28f08ed196 HomeView: handle vadjustment with BaseView
Instead of using lazy_static to save the adjustment,
pass it to the widget upon creation. If its the first instance
created, pass None instead.
2018-08-10 14:43:58 +03:00
Jordan Petridis 056d971000 HomeView: make base view field publick
There is no point to re-export BaseView's methods.
2018-08-10 14:42:57 +03:00
Jordan Petridis aa5195e5a9 ShowsView: handle vadjustment with BaseView
Instead of using lazy_static to save the adjustment,
pass it to the widget upon creation. If its the first instance
created, pass None instead.
2018-08-10 14:42:52 +03:00
Jordan Petridis 85aaaf80ac ShowsView: make base view field publick
There is no point to re-export BaseView's methods.
2018-08-10 13:54:19 +03:00
Jordan Petridis 5d467a22d0 ShowWidget: make base view field publick
There is no point to re-export BaseView's methods.
2018-08-10 13:54:14 +03:00
Jordan Petridis bcc1cfb67b ShowWidget: handle vadjustment with BaseView
Instead of using lazy_static to save the adjustment,
pass it to the widget upon creation. If previous it doesn't
exists pass None instead.
2018-08-10 13:54:07 +03:00
Jordan Petridis 70a24fba69 BaseView: implement a set_adjustments method
Ment to replace the individual set_vadjustment of widgets.
Also remove unused method from ShowWiget.
2018-08-10 13:53:25 +03:00
Jordan Petridis c3121bef84 ShowsChild: Remove the need for a .ui file
Its a simple enough widget that can be written by hand
and does not need a .ui builder file.
2018-08-10 13:53:12 +03:00
Jordan Petridis 8c25be7d05 BaseView: Set scroll policy 2018-08-10 13:53:00 +03:00
Jordan Petridis 7463e9d42c ShowsView: Use BaseView abstraction 2018-08-10 13:52:56 +03:00
Jordan Petridis e4dd9f5bb3 HomeView: Use libhandy::Column for the main widget 2018-08-10 13:52:52 +03:00
Jordan Petridis 4e59d648ef HomeView: Use the new BaseView abstraction 2018-08-10 13:52:48 +03:00
Jordan Petridis 7538e76537 Gtk: Abstract a BaseView Widget
Currently it just re-exports its children with getters,
but the idea is that it will also be able to handle
the saving the state of its height adjustment later.
2018-08-10 13:52:44 +03:00
Jordan Petridis 20ddc54edc ShowWidget: Put the whole thing inside HdyColumn 2018-08-10 13:52:35 +03:00
Jordan Petridis ac75205933 ShowWidget: Initial prototype w/ libhandy::Column 2018-08-10 13:47:59 +03:00
Jordan Petridis 17b58b159a ShowWidget: Remove unsused menu from the glade file 2018-08-10 13:47:12 +03:00
Jordan Petridis 191cf445ef ShowWidget: Do not hardcode the episode listbox in the glade file 2018-08-10 13:47:07 +03:00
Jordan Petridis 65a0c08cb3 Flatpak: fix previous commit that broke it
I broke both the manifest and the CI in 4da68ff89c
2018-08-10 13:44:48 +03:00
Jordan Petridis 4da68ff89c Flatpak: switch to using the 3.28 runtime again
Gtk was upgraded yesterday to 3.24. This causes a warning about
widgets being drawn without prior allocation. I am not sure yet if
the bug is in the bindings or if gtk's ABI broke.
2018-08-10 12:11:08 +03:00
Jordan Petridis a7da4525fd Flatpak: Grant access to thee dri
A warning suggested it, I assume it was coming from Gtk.
2018-08-10 12:08:54 +03:00
Jordan Petridis 92dfbce45a Merge branch 'wip/piotrdrag/review-strings' into 'master'
podcasts-gtk: Review ALL the strings!

See merge request World/podcasts!53
2018-08-10 05:58:11 +00:00
Piotr Drąg bfdd6b5f7c podcasts-gtk: Review ALL the strings! 2018-08-10 05:23:15 +00:00
Jordan Petridis a8a8c09b90 Merge branch 'wip/piotrdrag/appdata-fix' into 'master'
appdata: Remove duplicate tag

See merge request World/podcasts!49
2018-08-10 05:21:47 +00:00
Piotr Drąg 4bd13b81cf appdata: Remove duplicate tag 2018-08-10 08:00:32 +03:00
Jordan Petridis 5a07600664 Merge branch 'wip/piotrdrag/unmark-placeholders' into 'master'
episode_widget: Unmark placeholder strings from translation

See merge request World/podcasts!50
2018-08-10 04:59:39 +00:00
Piotr Drąg f2bc4b21cb episode_widget: Unmark placeholder strings from translation 2018-08-10 04:37:32 +00:00
Jordan Petridis 778975fbde Merge branch 'wip/piotrdrag/unmark-copyright' into 'master'
aboutdialog: Unmark copyright string from translation

See merge request World/podcasts!51
2018-08-10 04:36:54 +00:00
Piotr Drąg 57f920624f aboutdialog: Unmark copyright string from translation 2018-08-10 04:05:31 +00:00
Jordan Petridis 3d87940da9 Merge branch 'wip/piotrdrag/translate-appname' into 'master'
aboutdialog: Translate the app name

See merge request World/podcasts!52
2018-08-10 04:04:04 +00:00
Piotr Drąg c33d0836eb app: Translate the app name 2018-08-10 06:34:02 +03:00
Jordan Petridis a01cf21f2c Flatpak: use master runtime of GNOME sdk 2018-08-09 10:25:02 +03:00
Jordan Petridis 10fc1d4a2a Gitlab: Add merge request template 2018-08-09 10:23:34 +03:00
Jordan Petridis 4fa973007d App: Make sure to quit on delete event.
Since 9a76c6428a I've noticed
the applciation does not quit properly, and then it's unable
to be launched again till it's killed manually.

This patch is an attept to correct/workaroud that by calling
explcitly gio::ApplicationExt::quit().
2018-08-06 01:01:21 +03:00
Jordan Petridis c27f5ec02e Merge branch 'readme_improvements' into 'master'
Fix some typos in README.md

See merge request World/podcasts!47
2018-08-05 08:57:26 +00:00
Matthew 5d6cf3d17d Fix some typos in README.md 2018-08-05 11:32:42 +03:00
Jordan Petridis f6c7731377 Fix Rust 2018 edition warnings 2018-08-05 11:24:20 +03:00
Jordan Petridis bcd739da76 Merge branch 'i18n' into 'master'
Translation support and initial spanish translation

See merge request World/podcasts!46
2018-08-02 14:54:41 +00:00
Daniel García Moreno bea4915317 Translation support and initial spanish translation
Added translation support based on the Fractal i18n. To do this I've
added the gettext-rs crate dep. I'm using my own fork because the
official gettext-rs release includes the gettext source files and that
increase the distribution package a lot and for distribution with
flatkap we don't need to build gettext, the lib is in the gnome sdk. So
this gettext-rs fork is the same, but removing the not needed gettext
source files.

The i18n.rs file adds some useful functions to translate strings. These
functions wraps the original gettext and adds more functionality, to be
able to translate compound strings, something that's not supported by
the gettext function.

The 'i18n' function works like the gettext, receives a plain string
without params.

The 'i18n_f' function receives a string with "{}" and a ref to an array
of &str with substitutions for the "{}" in the original string. The
substitution is done by order.

The 'i18n_k' function receives a string with "{named}" and a ref to an
array of (&str, &str) with substitutions for the "{named}" in the
original string. The substitution is done by name, where the first &str
in the tuple is the name and the second the string to use for the
replace.

This mod also include ni18n variants of the three functions for plural
and singular translations.

I've also created the spanish translation.

See #61

https://gitlab.gnome.org/World/podcasts/issues/61
2018-08-02 15:24:19 +02:00
Jordan Petridis f695ba4605 cargo fmt 2018-08-02 07:10:22 +03:00
Jordan Petridis 24983ba3af Readme.md: Improve wording and formatting 2018-08-02 02:44:29 +00:00
Jordan Petridis 0a2a3b3377 Remove unused permission. 2018-08-01 02:14:27 +03:00
Jordan Petridis d8d7193cbc README: Add flahub instructions. 2018-07-31 11:40:05 +03:00
Jordan Petridis 042c9eed9c Appdata: Remove 'lang=' metadata tag from the description.
This seems to confuse the flathub store website.
2018-07-31 11:22:15 +03:00
Jordan Petridis b0c94dd998 Appdata: Add OARS Rating
This help with content age rating. Usefull for store
clients like gnome-software.

https://hughsie.github.io/oars/
2018-07-31 10:58:04 +03:00
Jordan Petridis 4c8cc9e823 Version bump. 2018-07-31 00:31:44 +03:00
Jordan Petridis 132c9bbdff ShowModel: Remove dead unused methods. 2018-07-30 23:23:58 +03:00
Jordan Petridis cc0caff8d0 EpisodeModels: Remove unused methods. 2018-07-30 23:20:10 +03:00
Jordan Petridis 3496df24f8 App: Fix refference cycles in actions. 2018-07-28 19:52:37 +03:00
Jordan Petridis e4e35e4c57 App: Replace action macro with a generic fuction. 2018-07-28 19:52:27 +03:00
Jordan Petridis 838320785e Replace SendCell with fragile.
SendCell is depricated now and its advised to use the Fragile
crate instead.

https://github.com/sdroege/send-cell/issues/5
2018-07-28 19:46:01 +03:00
Jordan Petridis 91bea85519 Update dependancies. 2018-07-28 19:15:26 +03:00
Jordan Petridis 88ea081661 Version bump. 2018-07-28 00:12:08 +03:00
Jordan Petridis 39c0a0dba5 Fix cargo vendor config. 2018-07-28 00:08:48 +03:00
Jordan Petridis 9d64d3e30d AppStream: captions were too short. 2018-07-27 21:45:33 +03:00
Jordan Petridis 39ff238716 Version bump. 2018-07-27 20:21:28 +03:00
Jordan Petridis 7c96152f3f Gtk: Change the minimum requests of the views. 2018-07-27 19:46:18 +03:00
Jordan Petridis a2440c19e1 Appdata: Fix screenshots metadata. 2018-07-27 00:42:54 +03:00
110 changed files with 11110 additions and 3768 deletions
+9
View File
@@ -8,3 +8,12 @@ build/
vendor/
.criterion/
org.gnome.*.json~
podcasts-gtk/po/gnome-podcasts.pot
# scripts/test.sh
target_*/
# flatpak-builder stuff
.flatpak-builder/
app/
repo/
+36 -83
View File
@@ -4,90 +4,56 @@ stages:
- review
variables:
BUNDLE: "gnome-podcasts-dev.flatpak"
.cargo_cache_template: &cargo_cache
cache:
# JOB_NAME - Each job will have it's own cache
# COMMIT_REF_SLUG = Lowercase name of the branch
# ^ Keep diffrerent caches for each branch
key: "$CI_JOB_NAME"
paths:
- target/
- .cargo_cache/
.cargo_test_template: &cargo_test
stage: test
variables:
RUSTFLAGS: "--cfg rayon_unstable"
RUST_BACKTRACE: "FULL"
before_script:
- apt-get update -yqq
- apt-get install -yqq --no-install-recommends build-essential libgtk-3-dev meson libgstreamer1.0-dev
- mkdir -p .cargo_cache
# Only stuff inside the repo directory can be cached
# Override the CARGO_HOME variable to force it location
- export CARGO_HOME="${PWD}/.cargo_cache"
script:
- rustc -Vv && cargo -Vv
# Force regeneration of gresources regardless of artifacts chage
- cd podcasts-gtk/resources/ && glib-compile-resources --generate resources.xml && cd ../../
- cargo build
- cargo test -- --test-threads=1
- cargo test -- --test-threads=1 --ignored
<<: *cargo_cache
.rust:stable:
# https://hub.docker.com/_/rust/
image: "rust"
<<: *cargo_test
.rust:nightly:
# https://hub.docker.com/r/rustlang/rust/
image: "rustlang/rust:nightly"
<<: *cargo_test
only:
- schedule
- web
BUNDLE: "org.gnome.Podcasts.Devel.flatpak"
flatpak:
image: registry.gitlab.gnome.org/gnome/gnome-runtime-images/rust_bundle:3.28
stage: test
image: "registry.gitlab.gnome.org/gnome/gnome-runtime-images/rust_bundle:master"
stage: "test"
variables:
MANIFEST_PATH: "org.gnome.PodcastsDevel.json"
FLATPAK_MODULE: "gnome-podcasts"
CONFIGURE_ARGS: "-Dprofile=development"
DBUS_ID: "org.gnome.PodcastsDevel"
script:
- flatpak-builder --stop-at=gnome-podcasts app org.gnome.Podcasts.json
- flatpak-builder --stop-at=${FLATPAK_MODULE} app ${MANIFEST_PATH}
# https://gitlab.gnome.org/World/podcasts/issues/55
# Force regeneration of gresources regardless of artifacts chage
- flatpak-builder --run app org.gnome.Podcasts.json glib-compile-resources --sourcedir=podcasts-gtk/resources/ podcasts-gtk/resources/resources.xml
- flatpak-builder --run app ${MANIFEST_PATH} glib-compile-resources --sourcedir=podcasts-gtk/resources/ podcasts-gtk/resources/resources.xml
# Build the flatpak repo
- flatpak-builder --run app org.gnome.Podcasts.json meson --prefix=/app --libdir=/app/lib _build
- flatpak-builder --run app org.gnome.Podcasts.json ninja -C _build install
- flatpak-builder --finish-only app org.gnome.Podcasts.json
- flatpak build-export repo app
- flatpak-builder --run app ${MANIFEST_PATH} meson --prefix=/app ${CONFIGURE_ARGS} _build
- flatpak-builder --run app ${MANIFEST_PATH} ninja -C _build install
# Run the tests
- |
xvfb-run -a -s "-screen 0 1024x768x24" \
flatpak-builder --run \
--env=APP_ID="org.gnome.PodcastsDevel" \
--env=LOCALEDIR="./podcasts-gtk/po" \
--env=VERSION="0.0.0" \
--env=CARGO_HOME="target/cargo-home" \
--env=CARGO_TARGET_DIR="target_test/" \
app ${MANIFEST_PATH} \
cargo test -j 1 -- --test-threads=1
# Create a flatpak bundle
- flatpak build-bundle repo ${BUNDLE} org.gnome.Podcasts
# Run the tests
# - flatpak-builder --run app org.gnome.Podcasts.json cargo test -- --test-threads=1
# - flatpak-builder --run app org.gnome.Podcasts.json cargo test -- --test-threads=1 --ignored
- flatpak-builder --finish-only app ${MANIFEST_PATH}
- flatpak build-export repo app
- flatpak build-bundle repo ${BUNDLE} ${DBUS_ID}
artifacts:
paths:
- $BUNDLE
expire_in: 30 days
expire_in: 2 days
cache:
# JOB_NAME - Each job will have it's own cache
# COMMIT_REF_SLUG = Lowercase name of the branch
# ^ Keep diffrerent caches for each branch
key: "$CI_JOB_NAME"
key: "flatpak"
paths:
- .flatpak-builder/cache/
- .flatpak-builder/downloads/
- .flatpak-builder/git/
- target/
- target_test/
review:
stage: review
@@ -105,6 +71,7 @@ review:
on_stop: stop_review
except:
- master@World/podcasts
- tags
stop_review:
stage: review
@@ -116,6 +83,7 @@ stop_review:
action: stop
except:
- master@World/podcasts
- tags
# Configure and run rustfmt on nightly
# Exits and builds fails if on bad format
@@ -126,19 +94,4 @@ rustfmt:
- rustc -Vv && cargo -Vv
- cargo fmt --version
- cargo fmt --all -- --check
# Configure and run clippy on nightly
# Only fails on errors atm.
.clippy:
image: "registry.gitlab.gnome.org/alatiera/hammond-container-images/clippy:nightly"
stage: lint
variables:
RUSTFLAGS: "--cfg rayon_unstable"
CARGO_HOME: ".cargo_cache"
script:
- rustc --version && cargo --version
- cargo clippy --version
# Force regeneration of gresources regardless of artifacts chage
- cd podcasts-gtk/resources/ && glib-compile-resources --generate resources.xml && cd ../../
- cargo clippy --all
<<: *cargo_cache
allow_failure: true
+1 -3
View File
@@ -11,9 +11,7 @@ Reproducible in:
Please test if the issue was already fixed in the unstable version of the app.
For that, follow these steps:
1. Make sure Flatpak is installed or install it following these steps https://flatpak.org/setup
2. Install the unstable version of the app following this link
https://gitlab.gnome.org/GNOME/gnome-apps-nightly/raw/master/nautilus.flatpakref and save
it as a ".flatpakref" file
2. Install the unstable version of the app following, flatpak bundles can be found in the CI artifacts.
If these steps failed, write in 'Other' the distribution youre using and
the version of the app.
+1
View File
@@ -0,0 +1 @@
### Please attach a relevant issue to this MR, if this doesn't exist please create one.
+103 -1
View File
@@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added:
### Changed:
@@ -13,6 +14,107 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Removed:
## [0.4.6] - 2018-10-07
### Added:
- Felix, @haecker-felix, wrote an [mpris crate](https://crates.io/crates/mpris-player) and implemented MPRIS2 client side support! !74 #68
### Changed:
- Download Cancel button was changed to an Icon instead of a label !72
- The applciation will no longer scale below 360p in width 1933c79f7a87d8261d91ca4e14eb51c1ddc66624
- Update to the latest HIG 5050dda4d2f75b706842de8507d115dd5a1bd0a9
- Chris, @brainblasted, upgraded hyper to 0.12, this brings openssl 1.1 support !75
- Pipeline backend is now completly migrated to tokio-runtime 0887789f5e653dd92ad397fb39561df6dffcb45c
- Resume playing an episode will attempt to rewind the track only if more than a minute has passed since the last pause !76
### Fixed:
- Fixed a regression where indexing feeds was blocking the `tokio reactor` #88 !70
- Episodeds Listbox no longer resizes when a download starts #89 !72
- The `total_size` label of the `EpisodeWidget` now behaves correctly if the request fails #90 !73
- The Pipeline will no longer log things in stderr for Requests that returned 304 and are expected to be skipped da361d0cb93cd8edd076859b2c607509a96dac8d
- A bug where the HomeView wold get into an invalid state if your only shows had no episodes 32bd2a89a34e8e940b3b260c6be76defe11835ed
### Translations:
**Added**
- Brazilian Portuguese translation 586cf16f
- Swedish translation 2e527250
- Italian translation a23297e5
- Friulian translation 60e09c0d
- Hungarian translation 2751a828
- Croatian translation 0476b67b
- Latvian translation a681b2c9
- Czech translation 3563a964
- Catalan translation 6ea3fc91
**Updated**
- German translation
- Finnish translation
- Polish translation
- Turkish translation
- Croatian translation
- Indonesian translation
- Spanish translation
## [0.4.5] - 2018-08-31
### Added:
- [OARS](https://hughsie.github.io/oars/) Tags where added for compatibility with Store clients b0c94dd9
- Daniel added support for Translations !46
- Svitozar Cherepii(@svito) created a [wiki page](https://wiki.gnome.org/Apps/Podcasts) 70e79e50
- Libhandy was added as a dependancy #70
- Development builds can now be installed in parallel with stable builds !64
### Changed:
- The update indication was moved to an In-App notification #72
- The app icon's accent color was changed from orange to red 0dfb4859
- The stack switcher in the Headerbar is now insesitive on Empty Views !63
### Fixed:
- Improved handling of HTTP redirections #64 !61 !62
- Fixed a major performance regression when loading show covers !67
- More refference cycles have been fixed !59
- OPML import dialog now exits properly and no longer keeps the application from shuting down !65
- Update action is disabled if there isn't something to update #71
### Translations:
- Added Finish 93696026
- Added Polish 1bd6efc0
- Added Turkish 73929f2d
- Added Spanish !46
- Added German 6b6c390c
- Added Galician 0060a634
- Added Indonesian ded0224f
- Added Korean 36f16963
## [0.4.4] - 2018-07-31
### Changed:
- `SendCell` crate was replaced with `Fragile`. (Jorda Petridis) 838320785ebbea94e009698b473495cfec076f54
- Update dependancies (Jorda Petridis) 91bea8551998b16e44e5358fdd43c53422bcc6f3
### Fixed:
- Fix more refference cycles. (Jorda Petridis) 3496df24f8d8bfa8c8a53d8f00262d42ee39b41c
- Actually fix cargo-vendor (Jorda Petridis)
## [0.4.3] - 2018-07-27
### Fixed:
- Fix the cargo vendor config for the tarball releash script. (Jorda Petridis) a2440c19e11ca4dcdbcb67cd85259a41fe3754d6
## [0.4.2] - 2018-07-27
### Changed:
- Minimum size requested by the Views. (Jorda Petridis) 7c96152f3f53f271247230dccf1c9cd5947b685f
### Fixed:
- Screenshot metadata in appstream data. (Jorda Petridis) a2440c19e11ca4dcdbcb67cd85259a41fe3754d6
## [0.4.1] - 2018-07-26
### Added:
@@ -138,4 +240,4 @@ not being able to access any file.
- Added appdata.xml file
## [0.1.0] - 2017-11-13
- Initial Release
- Initial Release
Generated
+1107 -1189
View File
File diff suppressed because it is too large Load Diff
+46 -57
View File
@@ -3,61 +3,57 @@
### A Podcast application for GNOME.
Listen to your favorite podcasts, right from your desktop.
![episdes_view](./screenshots/episodes_view.png)
![episdes_view](./screenshots/home_view.png)
![shows_view](./screenshots/shows_view.png)
![show_widget](./screenshots/show_widget.png)
## Available on Flathub
[![Get it from Flathub!](https://flathub.org/assets/badges/flathub-badge-en.svg)](https://flathub.org/apps/details/org.gnome.Podcasts)
## Quick start
GNOME Podcasts can be built and run with [Gnome Builder](https://wiki.gnome.org/Apps/Builder) >= 3.28.
GNOME Podcasts can be built and run with [Gnome Builder][builder] >= 3.28.
Just clone the repo and hit the run button!
Get Builder [here](https://wiki.gnome.org/Apps/Builder/Downloads)
You can get Builder from [here][get_builder].
## Broken Feeds
Found a feed that does not work in GNOME Podcasts?
Please [open an issue](https://gitlab.gnome.org/World/podcasts/issues/new) and choose the `BrokenFeed` template so we will know and fix it!
Please [open an issue][new_issue] and choose the `BrokenFeed` template so we will know and fix it!
## Getting in Touch
If you have any questions regarding the use or development of GNOME Podcasts,
want to discuss design or simply hang out, please join us in `#hammond` on
[irc.gnome.org.][irc] or [matrix][matrix].
Note:
There isn't much documentation yet, so you will probably have question about parts of the Code.
want to discuss design or simply hang out, please join us on our [irc][irc] or [matrix][matrix] channel.
## Building
### Flatpak
Flatpak is the reccomended way of building and installing GNOME Podcasts.
Flatpak is the recommended way of building and installing GNOME Podcasts.
Here are the depencancies you will need.
#### Building a Flatpak
```sh
# Add flathub and the gnome-nightly repo
flatpak remote-add --user --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak remote-add --user --if-not-exists gnome-nightly https://sdk.gnome.org/gnome-nightly.flatpakrepo
Download the `org.gnome.Podcasts.json` flatpak manifest from this repo.
# Install the gnome-nightly Sdk and Platform runtime
flatpak install --user gnome-nightly org.gnome.Sdk org.gnome.Platform
```bash
# Add flathub repo
flatpak --user remote-add flathub --if-not-exists https://dl.flathub.org/repo/flathub.flatpakrepo
# Add the gnome-nightly repo
flatpak --user remote-add gnome-nightly --if-not-exists https://sdk.gnome.org/gnome-nightly.flatpakrepo
# Install the gnome-nightly Sdk and Platform runtim
flatpak --user install gnome-nightly org.gnome.Sdk org.gnome.Platform
# Install the required rust-stable extension from flathub
flatpak --user install flathub org.freedesktop.Sdk.Extension.rust-stable
flatpak-builder --user --repo=repo podcasts org.gnome.Podcasts.json --force-clean
flatpak install --user flathub org.freedesktop.Sdk.Extension.rust-stable//18.08
```
To install the resulting flatpak you can do:
```bash
flatpak build-bundle repo gnome-podcasts.flatpak org.gnome.Podcasts
flatpak install --user --bundle gnome-podcasts.flatpak
flatpak-builder --user --install --force-clean --repo=repo podcasts org.gnome.Podcasts.json
```
### Building from soure
### Building from source
```sh
git clone https://gitlab.gnome.org/World/podcasts.git
@@ -72,48 +68,31 @@ sudo ninja -C build install
* Rust stable 1.27 or later along with cargo.
* Gtk+ 3.22 or later
* Gstreamer 1.12 or later
* libhandy
* Meson
* A network connection
Offline build are possible too, but [`cargo-vendor`][vendor] would have to be setup first
**Debian/Ubuntu**
```sh
apt-get install -yqq cargo rustc libsqlite3-dev libssl-dev libgtk-3-dev meson \
libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \
gstreamer1.0-plugins-base gstreamer1.0-plugins-good \
gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly \
gstreamer1.0-libav libgstrtspserver-1.0-dev ibgstreamer-plugins-bad1.0-dev
```
**Fedora**
```sh
dnf install -y rust cargo gtk3-devel glib2-devel openssl-devel sqlite-devel meson \
gstreamer1-devel gstreamer1-plugins-base-tools gstreamer1-devel-docs \
gstreamer1-plugins-base-devel gstreamer1-plugins-base-devel-docs \
gstreamer1-plugins-good gstreamer1-plugins-good-extras \
gstreamer1-plugins-bad-free gstreamer1-plugins-bad-free-devel \
gstreamer1-plugins-bad-free-extras
```
If you happen to build it on other distributions please let me know the names
of the corresponding libraries. Feel free to open a MR or an Issue to note it.
## Contributing
There alot of thins yet to be done.
There are a lot of things yet to be done.
If you want to contribute, please check the [Contributions Guidelines][contribution-guidelines].
You can start by taking a look at [Issues](https://gitlab.gnome.org/World/podcasts/issues) or by opening a [New issue](https://gitlab.gnome.org/World/podcasts/issues/new?issue%5Bassignee_id%5D=&issue%5Bmilestone_id%5D=).
You can start by taking a look at [Issues][issues] or by opening a [New issue][new_issue].
There are also some minor tasks tagged with `TODO:` and `FIXME:` in the source code.
[contribution-guidelines]: https://gitlab.gnome.org/World/podcasts/blob/master/CONTRIBUTING.md
### Translations
Translation of this project takes place on the GNOME translation platform,
[Damned Lies](https://l10n.gnome.org/module/podcasts). For further
information on how to join a language team, or even to create one, please see
[GNOME Translation Project wiki page](https://wiki.gnome.org/TranslationProject).
## Overview
@@ -138,17 +117,27 @@ $ tree -d
## A note about the project's name
The project used to be called Hammond, after Allan Moore's character [Evey Hammond](https://en.wikipedia.org/wiki/Evey_Hammond) from the graphic novel V for Vendetta.
The project used to be called Hammond, after Allan Moore's character [Evey Hammond][hammond] from the graphic novel V for Vendetta.
It was renamed to GNOME Podcasts on 2018/07/24 shortly before its first public release.
## Acknowledgments
GNOME Podcasts's design is heavily insired by [GNOME Music](https://wiki.gnome.org/Design/Apps/Music) and [Vocal](http://vocalproject.net/).
GNOME Podcasts's design is heavily inspired by [GNOME Music][music] and [Vocal][vocal].
We also copied some elements from [GNOME News](https://wiki.gnome.org/Design/Apps/Potential/News).
We also copied some elements from [GNOME News][news].
And almost the entirety of the build system is copied from the [Fractal](https://gitlab.gnome.org/danigm/fractal) project.
And almost the entirety of the build system is copied from the [Fractal][fractal] project.
[vendor]: https://github.com/alexcrichton/cargo-vendor
[irc]: irc://irc.gnome.org/#hammond
[matrix]: https://matrix.to/#/#gnome-podcasts:matrix.org
[matrix]: https://matrix.to/#/#gnome-podcasts:matrix.org
[flatpak_setup]: https://flatpak.org/setup/
[music]: https://wiki.gnome.org/Design/Apps/Music
[vocal]: http://vocalproject.net/
[news]: https://wiki.gnome.org/Design/Apps/Potential/News
[fractal]: https://gitlab.gnome.org/World/fractal
[hammond]: https://en.wikipedia.org/wiki/Evey_Hammond
[issues]: https://gitlab.gnome.org/World/podcasts/issues
[new_issue]: https://gitlab.gnome.org/World/podcasts/issues/new
[builder]: https://wiki.gnome.org/Apps/Builder
[get_builder]: https://wiki.gnome.org/Apps/Builder/Downloads
+33 -6
View File
@@ -3,10 +3,29 @@
project(
'gnome-podcasts', 'rust',
version: '0.4.1',
version: '0.4.6',
license: 'GPLv3',
)
if get_option('profile') == 'development'
profile = 'Devel'
vcs_tag = run_command('git', 'rev-parse', '--short', 'HEAD').stdout().strip()
if vcs_tag == ''
version_suffix = '-devel'
else
version_suffix = '-@0@'.format (vcs_tag)
endif
else
profile = ''
version_suffix = ''
endif
application_id = 'org.gnome.Podcasts@0@'.format(profile)
i18n = import('i18n')
subdir('podcasts-gtk/po')
podir = join_paths (meson.source_root (), 'podcasts-gtk', 'po')
podcasts_version = meson.project_version()
version_array = podcasts_version.split('.')
podcasts_major_version = version_array[0].to_int()
@@ -15,12 +34,13 @@ podcasts_version_micro = version_array[2].to_int()
podcasts_prefix = get_option('prefix')
podcasts_bindir = join_paths(podcasts_prefix, get_option('bindir'))
podcasts_localedir = join_paths(podcasts_prefix, get_option('localedir'))
podcasts_conf = configuration_data()
podcasts_conf.set('BINDIR', podcasts_bindir)
podcasts_conf.set('appid', application_id)
podcasts_conf.set('bindir', podcasts_bindir)
datadir = get_option('datadir')
icondir = join_paths(datadir, 'icons')
subdir('podcasts-gtk/resources')
cargo = find_program('cargo', required: false)
@@ -29,12 +49,19 @@ cargo_vendor = find_program('cargo-vendor', required: false)
cargo_script = find_program('scripts/cargo.sh')
cargo_release = custom_target('cargo-build',
build_by_default: true,
build_always: true,
build_always_stale: true,
output: ['gnome-podcasts'],
install: true,
install_dir: podcasts_bindir,
command: [cargo_script, '@CURRENT_SOURCE_DIR@', '@OUTPUT@'])
console: true,
command: [cargo_script,
'@CURRENT_SOURCE_DIR@',
'@OUTPUT@',
podcasts_localedir,
application_id,
podcasts_version + version_suffix,
profile
])
run_target('release', command: ['scripts/release.sh',
meson.project_name() + '-' + podcasts_version
+9
View File
@@ -0,0 +1,9 @@
option (
'profile',
type: 'combo',
choices: [
'default',
'development'
],
value: 'default'
)
@@ -1,7 +1,7 @@
{
"app-id" : "org.gnome.Podcasts",
"app-id" : "org.gnome.PodcastsDevel",
"runtime" : "org.gnome.Platform",
"runtime-version" : "3.28",
"runtime-version" : "master",
"sdk" : "org.gnome.Sdk",
"sdk-extensions" : [
"org.freedesktop.Sdk.Extension.rust-stable"
@@ -21,7 +21,8 @@
"--socket=x11",
"--socket=wayland",
"--socket=pulseaudio",
"--talk-name=org.freedesktop.Desktop"
"--device=dri",
"--own-name=org.mpris.MediaPlayer2.Podcasts"
],
"build-options" : {
"append-path" : "/usr/lib/sdk/rust-stable/bin",
@@ -30,14 +31,33 @@
],
"env" : {
"CARGO_HOME" : "/run/build/Podcasts/cargo",
"RUST_BACKTRACE" : "1",
"DEBUG" : "true"
/* "RUSTFLAGS" : "--error-format=short", */
"RUST_BACKTRACE" : "1"
}
},
"modules" : [
{
"name" : "libhandy",
"buildsystem" : "meson",
"config-opts" : [
"-Dintrospection=true",
"-Dgtk-doc=false",
"-Dtests=false",
"-Dexamples=false",
"-Dvapi=false",
"-Dglade_catalog=false"
],
"sources" : [
{
"type" : "git",
"url" : "https://source.puri.sm/Librem5/libhandy.git"
}
]
},
{
"name" : "gnome-podcasts",
"buildsystem" : "meson",
"config-opts" : [ "-Dprofile=development" ],
"sources" : [
{
"type" : "git",
@@ -47,4 +67,4 @@
]
}
]
}
}
+14 -20
View File
@@ -2,45 +2,39 @@
authors = ["Jordan Petridis <jordanpetridis@protonmail.com>"]
name = "podcasts-data"
version = "0.1.0"
workspace = "../"
[dependencies]
ammonia = "1.2.0"
chrono = "0.4.4"
derive_builder = "0.5.1"
lazy_static = "1.0.2"
log = "0.4.3"
chrono = "0.4.6"
derive_builder = "0.6.0"
lazy_static = "1.1.0"
log = "0.4.5"
rayon = "1.0.2"
rayon-futures = "0.1.0"
rfc822_sanitizer = "0.3.3"
rss = "1.5.0"
url = "1.7.1"
xdg = "2.1.0"
xml-rs = "0.8.0"
futures = "0.1.23"
hyper = "0.11.27"
tokio-core = "0.1.17"
hyper-tls = "0.1.3"
native-tls = "0.1.5"
futures = "0.1.24"
hyper = "0.12.11"
http = "0.1.13"
tokio = "0.1.11"
hyper-tls = "0.3.1"
native-tls = "0.2.1"
num_cpus = "1.8.0"
failure = "0.1.1"
failure_derive = "0.1.1"
failure = "0.1.2"
failure_derive = "0.1.2"
[dependencies.diesel]
features = ["sqlite", "r2d2"]
version = "1.3.2"
version = "1.3.3"
[dependencies.diesel_migrations]
features = ["sqlite"]
version = "1.3.0"
[dev-dependencies]
rand = "0.5.4"
rand = "0.5.5"
tempdir = "0.3.7"
criterion = "0.2.4"
pretty_assertions = "0.5.1"
maplit = "1.0.1"
[[bench]]
name = "bench"
harness = false
-104
View File
@@ -1,104 +0,0 @@
#![allow(unused)]
#[macro_use]
extern crate criterion;
use criterion::Criterion;
// extern crate futures;
extern crate hyper;
extern crate hyper_tls;
extern crate podcasts_data;
extern crate rand;
extern crate tokio_core;
// extern crate rayon;
extern crate rss;
// use rayon::prelude::*;
// use futures::future::*;
use tokio_core::reactor::Core;
use podcasts_data::database::truncate_db;
use podcasts_data::pipeline;
use podcasts_data::FeedBuilder;
use podcasts_data::Source;
// use podcasts_data::errors::*;
use std::io::BufReader;
// RSS feeds
const INTERCEPTED: &[u8] = include_bytes!("../tests/feeds/2018-01-20-Intercepted.xml");
const INTERCEPTED_URL: &str = "https://web.archive.org/web/20180120083840if_/https://feeds.\
feedburner.com/InterceptedWithJeremyScahill";
const UNPLUGGED: &[u8] = include_bytes!("../tests/feeds/2018-01-20-LinuxUnplugged.xml");
const UNPLUGGED_URL: &str =
"https://web.archive.org/web/20180120110314if_/https://feeds.feedburner.com/linuxunplugged";
const TIPOFF: &[u8] = include_bytes!("../tests/feeds/2018-01-20-TheTipOff.xml");
const TIPOFF_URL: &str =
"https://web.archive.org/web/20180120110727if_/https://rss.acast.com/thetipoff";
// This feed has HUGE descripion and summary fields which can be very
// very expensive to parse.
const CODE: &[u8] = include_bytes!("../tests/feeds/2018-01-20-GreaterThanCode.xml");
const CODE_URL: &str =
"https://web.archive.org/web/20180120104741if_/https://www.greaterthancode.com/feed/podcast";
// Relative small feed
const STARS: &[u8] = include_bytes!("../tests/feeds/2018-01-20-StealTheStars.xml");
const STARS_URL: &str =
"https://web.archive.org/web/20180120104957if_/https://rss.art19.com/steal-the-stars";
static FEEDS: &[(&[u8], &str)] = &[
(INTERCEPTED, INTERCEPTED_URL),
(UNPLUGGED, UNPLUGGED_URL),
(TIPOFF, TIPOFF_URL),
(CODE, CODE_URL),
(STARS, STARS_URL),
];
fn bench_index_large_feed(c: &mut Criterion) {
truncate_db().unwrap();
let url = "https://www.greaterthancode.com/feed/podcast";
let mut core = Core::new().unwrap();
c.bench_function("index_large_feed", move |b| {
b.iter(|| {
let s = Source::from_url(url).unwrap();
// parse it into a channel
let chan = rss::Channel::read_from(BufReader::new(CODE)).unwrap();
let feed = FeedBuilder::default()
.channel(chan)
.source_id(s.id())
.build()
.unwrap();
core.run(feed.index()).unwrap();
})
});
truncate_db().unwrap();
}
fn bench_index_small_feed(c: &mut Criterion) {
truncate_db().unwrap();
let url = "https://rss.art19.com/steal-the-stars";
let mut core = Core::new().unwrap();
c.bench_function("index_small_feed", move |b| {
b.iter(|| {
let s = Source::from_url(url).unwrap();
// parse it into a channel
let chan = rss::Channel::read_from(BufReader::new(STARS)).unwrap();
let feed = FeedBuilder::default()
.channel(chan)
.source_id(s.id())
.build()
.unwrap();
core.run(feed.index()).unwrap();
})
});
truncate_db().unwrap();
}
criterion_group!(benches, bench_index_large_feed, bench_index_small_feed);
criterion_main!(benches);
+1 -1
View File
@@ -66,7 +66,7 @@ fn run_migration_on(connection: &SqliteConnection) -> Result<(), DataError> {
/// Reset the database into a clean state.
// Test share a Temp file db.
#[allow(dead_code)]
#[cfg(test)]
pub fn truncate_db() -> Result<(), DataError> {
let db = connection();
let con = db.get()?;
+45 -14
View File
@@ -5,7 +5,6 @@ use diesel::prelude::*;
use diesel;
use diesel::dsl::exists;
use diesel::query_builder::AsQuery;
use diesel::select;
use database::connection;
@@ -111,8 +110,7 @@ pub fn get_episode_widget_from_rowid(ep_id: i32) -> Result<EpisodeWidgetModel, D
episodes
.select((
rowid, title, uri, local_uri, epoch, length, duration, played, show_id,
))
.filter(rowid.eq(ep_id))
)).filter(rowid.eq(ep_id))
.get_result::<EpisodeWidgetModel>(&con)
.map_err(From::from)
}
@@ -295,6 +293,23 @@ pub(crate) fn get_episode_minimal_from_pk(
.map_err(From::from)
}
#[cfg(test)]
pub(crate) fn get_episode_cleaner_from_pk(
title_: &str,
pid: i32,
) -> Result<EpisodeCleanerModel, DataError> {
use schema::episodes::dsl::*;
let db = connection();
let con = db.get()?;
episodes
.select((rowid, local_uri, played))
.filter(title.eq(title_))
.filter(show_id.eq(pid))
.get_result::<EpisodeCleanerModel>(&con)
.map_err(From::from)
}
pub(crate) fn remove_feed(pd: &Show) -> Result<(), DataError> {
let db = connection();
let con = db.get()?;
@@ -363,20 +378,20 @@ pub(crate) fn episode_exists(title_: &str, show_id_: i32) -> Result<bool, DataEr
/// Check if the `episodes table contains any rows
///
/// Return true if `episodes` table is populated.
pub fn is_episodes_populated() -> Result<bool, DataError> {
pub fn is_episodes_populated(filter_show_ids: &[i32]) -> Result<bool, DataError> {
use schema::episodes::dsl::*;
let db = connection();
let con = db.get()?;
select(exists(episodes.as_query()))
select(exists(episodes.filter(show_id.ne_all(filter_show_ids))))
.get_result(&con)
.map_err(From::from)
}
/// Check if the `shows` table contains any rows
///
/// Return true if `shows table is populated.
/// Return true if `shows` table is populated.
pub fn is_podcasts_populated(filter_ids: &[i32]) -> Result<bool, DataError> {
use schema::shows::dsl::*;
@@ -388,6 +403,20 @@ pub fn is_podcasts_populated(filter_ids: &[i32]) -> Result<bool, DataError> {
.map_err(From::from)
}
/// Check if the `source` table contains any rows
///
/// Return true if `source` table is populated.
pub fn is_source_populated(filter_ids: &[i32]) -> Result<bool, DataError> {
use schema::source::dsl::*;
let db = connection();
let con = db.get()?;
select(exists(source.filter(id.ne_all(filter_ids))))
.get_result(&con)
.map_err(From::from)
}
pub(crate) fn index_new_episodes(eps: &[NewEpisode]) -> Result<(), DataError> {
use schema::episodes::dsl::*;
let db = connection();
@@ -418,24 +447,26 @@ pub fn update_none_to_played_now(parent: &Show) -> Result<usize, DataError> {
mod tests {
use super::*;
use database::*;
use failure::Error;
use pipeline;
#[test]
fn test_update_none_to_played_now() {
truncate_db().unwrap();
fn test_update_none_to_played_now() -> Result<(), Error> {
truncate_db()?;
let url = "https://web.archive.org/web/20180120083840if_/https://feeds.feedburner.\
com/InterceptedWithJeremyScahill";
let source = Source::from_url(url).unwrap();
let source = Source::from_url(url)?;
let id = source.id();
pipeline::run(vec![source], true).unwrap();
let pd = get_podcast_from_source_id(id).unwrap();
pipeline::run(vec![source])?;
let pd = get_podcast_from_source_id(id)?;
let eps_num = get_pd_unplayed_episodes(&pd).unwrap().len();
let eps_num = get_pd_unplayed_episodes(&pd)?.len();
assert_ne!(eps_num, 0);
update_none_to_played_now(&pd).unwrap();
let eps_num2 = get_pd_unplayed_episodes(&pd).unwrap().len();
update_none_to_played_now(&pd)?;
let eps_num2 = get_pd_unplayed_episodes(&pd)?.len();
assert_eq!(eps_num2, 0);
Ok(())
}
}
+13 -3
View File
@@ -1,6 +1,7 @@
use diesel;
use diesel::r2d2;
use diesel_migrations::RunMigrationsError;
use http;
use hyper;
use native_tls;
use rss;
@@ -46,8 +47,9 @@ pub enum DataError {
R2D2PoolError(#[cause] r2d2::PoolError),
#[fail(display = "Hyper Error: {}", _0)]
HyperError(#[cause] hyper::Error),
#[fail(display = "ToStr Error: {}", _0)]
HttpToStr(#[cause] http::header::ToStrError),
#[fail(display = "Failed to parse a url: {}", _0)]
// TODO: print the url too
UrlError(#[cause] url::ParseError),
#[fail(display = "TLS Error: {}", _0)]
TLSError(#[cause] native_tls::Error),
@@ -61,8 +63,10 @@ pub enum DataError {
Bail(String),
#[fail(display = "{}", _0)]
HttpStatusGeneral(HttpStatusError),
#[fail(display = "FIXME: This should be better")]
F301(Source),
#[fail(display = "Source redirects to a new url")]
FeedRedirect(Source),
#[fail(display = "Feed is up to date")]
FeedNotModified(Source),
#[fail(
display = "Error occured while Parsing an Episode. Reason: {}",
reason
@@ -102,6 +106,12 @@ impl From<hyper::Error> for DataError {
}
}
impl From<http::header::ToStrError> for DataError {
fn from(err: http::header::ToStrError) -> Self {
DataError::HttpToStr(err)
}
}
impl From<url::ParseError> for DataError {
fn from(err: url::ParseError) -> Self {
DataError::UrlError(err)
+43 -48
View File
@@ -26,7 +26,7 @@ pub struct Feed {
impl Feed {
/// Index the contents of the RSS `Feed` into the database.
pub fn index(self) -> impl Future<Item = (), Error = DataError> + Send {
self.parse_podcast_async()
ok(self.parse_podcast())
.and_then(|pd| pd.to_podcast())
.and_then(move |pd| self.index_channel_items(pd))
}
@@ -35,16 +35,13 @@ impl Feed {
NewShow::new(&self.channel, self.source_id)
}
fn parse_podcast_async(&self) -> impl Future<Item = NewShow, Error = DataError> + Send {
ok(self.parse_podcast())
}
fn index_channel_items(self, pd: Show) -> impl Future<Item = (), Error = DataError> + Send {
let stream = stream::iter_ok::<_, DataError>(self.channel.into_items());
// Parse the episodes
let episodes = stream.filter_map(move |item| {
glue(&item, pd.id())
NewEpisodeMinimal::new(&item, pd.id())
.and_then(move |ep| determine_ep_state(ep, &item))
.map_err(|err| error!("Failed to parse an episode: {}", err))
.ok()
});
@@ -56,10 +53,6 @@ impl Feed {
}
}
fn glue(item: &rss::Item, id: i32) -> Result<IndexState<NewEpisode>, DataError> {
NewEpisodeMinimal::new(item, id).and_then(move |ep| determine_ep_state(ep, item))
}
fn determine_ep_state(
ep: NewEpisodeMinimal,
item: &rss::Item,
@@ -87,21 +80,22 @@ fn filter_episodes<'a, S>(
where
S: Stream<Item = IndexState<NewEpisode>, Error = DataError> + Send + 'a,
{
stream.filter_map(|state| match state {
IndexState::NotChanged => None,
// Update individual rows, and filter them
IndexState::Update((ref ep, rowid)) => {
ep.update(rowid)
.map_err(|err| error!("{}", err))
.map_err(|_| error!("Failed to index episode: {:?}.", ep.title()))
.ok();
stream
.filter_map(|state| match state {
IndexState::NotChanged => None,
// Update individual rows, and filter them
IndexState::Update((ref ep, rowid)) => {
ep.update(rowid)
.map_err(|err| error!("{}", err))
.map_err(|_| error!("Failed to index episode: {:?}.", ep.title()))
.ok();
None
},
IndexState::Index(s) => Some(s),
})
// only Index is left, collect them for batch index
.collect()
None
}
IndexState::Index(s) => Some(s),
})
// only Index is left, collect them for batch index
.collect()
}
fn batch_insert_episodes(episodes: &[NewEpisode]) {
@@ -114,8 +108,7 @@ fn batch_insert_episodes(episodes: &[NewEpisode]) {
.map_err(|err| {
error!("Failed batch indexng: {}", err);
info!("Fallign back to individual indexing.");
})
.unwrap_or_else(|_| {
}).unwrap_or_else(|_| {
episodes.iter().for_each(|ep| {
ep.index()
.map_err(|err| error!("Error: {}.", err))
@@ -127,8 +120,9 @@ fn batch_insert_episodes(episodes: &[NewEpisode]) {
#[cfg(test)]
mod tests {
use failure::Error;
use rss::Channel;
use tokio_core::reactor::Core;
use tokio::{self, prelude::*};
use database::truncate_db;
use dbqueries;
@@ -171,8 +165,8 @@ mod tests {
};
#[test]
fn test_complete_index() {
truncate_db().unwrap();
fn test_complete_index() -> Result<(), Error> {
truncate_db()?;
let feeds: Vec<_> = URLS
.iter()
@@ -180,44 +174,45 @@ mod tests {
// Create and insert a Source into db
let s = Source::from_url(url).unwrap();
get_feed(path, s.id())
})
.collect();
}).collect();
let mut core = Core::new().unwrap();
// Index the channels
let list: Vec<_> = feeds.into_iter().map(|x| x.index()).collect();
let _foo = core.run(join_all(list));
// Index the channes
let stream_ = stream::iter_ok(feeds).for_each(|x| x.index());
tokio::run(stream_.map_err(|_| ()));
// Assert the index rows equal the controlled results
assert_eq!(dbqueries::get_sources().unwrap().len(), 5);
assert_eq!(dbqueries::get_podcasts().unwrap().len(), 5);
assert_eq!(dbqueries::get_episodes().unwrap().len(), 354);
assert_eq!(dbqueries::get_sources()?.len(), 5);
assert_eq!(dbqueries::get_podcasts()?.len(), 5);
assert_eq!(dbqueries::get_episodes()?.len(), 354);
Ok(())
}
#[test]
fn test_feed_parse_podcast() {
truncate_db().unwrap();
fn test_feed_parse_podcast() -> Result<(), Error> {
truncate_db()?;
let path = "tests/feeds/2018-01-20-Intercepted.xml";
let feed = get_feed(path, 42);
let file = fs::File::open(path).unwrap();
let channel = Channel::read_from(BufReader::new(file)).unwrap();
let file = fs::File::open(path)?;
let channel = Channel::read_from(BufReader::new(file))?;
let pd = NewShow::new(&channel, 42);
assert_eq!(feed.parse_podcast(), pd);
Ok(())
}
#[test]
fn test_feed_index_channel_items() {
truncate_db().unwrap();
fn test_feed_index_channel_items() -> Result<(), Error> {
truncate_db()?;
let path = "tests/feeds/2018-01-20-Intercepted.xml";
let feed = get_feed(path, 42);
let pd = feed.parse_podcast().to_podcast().unwrap();
let pd = feed.parse_podcast().to_podcast()?;
feed.index_channel_items(pd).wait().unwrap();
assert_eq!(dbqueries::get_podcasts().unwrap().len(), 1);
assert_eq!(dbqueries::get_episodes().unwrap().len(), 43);
feed.index_channel_items(pd).wait()?;
assert_eq!(dbqueries::get_podcasts()?.len(), 1);
assert_eq!(dbqueries::get_episodes()?.len(), 43);
Ok(())
}
}
+5 -11
View File
@@ -23,13 +23,8 @@
)
)]
// Enable lint group collections
#![warn(
nonstandard_style,
edition_2018,
rust_2018_idioms,
bad_style,
unused
)]
#![warn(nonstandard_style, bad_style, unused)]
#![allow(edition_2018, rust_2018_idioms)]
// standalone lints
#![warn(
const_err,
@@ -50,9 +45,8 @@
elided_lifetime_in_paths,
missing_copy_implementations
)]
#![allow(proc_macro_derive_resolution_fallback)]
#![deny(warnings)]
// warn when code is not using dyn Trait syntax. req rustc 1.27
// #![deny(bare_trait_object)]
//! FIXME: Docs
@@ -82,15 +76,15 @@ extern crate log;
extern crate ammonia;
extern crate chrono;
extern crate futures;
extern crate http;
extern crate hyper;
extern crate hyper_tls;
extern crate native_tls;
extern crate num_cpus;
extern crate rayon;
extern crate rayon_futures;
extern crate rfc822_sanitizer;
extern crate rss;
extern crate tokio_core;
extern crate tokio;
extern crate url;
extern crate xdg;
extern crate xml;
+1 -63
View File
@@ -53,11 +53,6 @@ impl Episode {
&self.title
}
/// Set the `title`.
pub fn set_title(&mut self, value: &str) {
self.title = value.to_string();
}
/// Get the value of the `uri`.
///
/// Represents the url(usually) that the media file will be located at.
@@ -65,11 +60,6 @@ impl Episode {
self.uri.as_ref().map(|s| s.as_str())
}
/// Set the `uri`.
pub fn set_uri(&mut self, value: Option<&str>) {
self.uri = value.map(|x| x.to_string());
}
/// Get the value of the `local_uri`.
///
/// Represents the local uri,usually filesystem path,
@@ -78,31 +68,16 @@ impl Episode {
self.local_uri.as_ref().map(|s| s.as_str())
}
/// Set the `local_uri`.
pub fn set_local_uri(&mut self, value: Option<&str>) {
self.local_uri = value.map(|x| x.to_string());
}
/// Get the `description`.
pub fn description(&self) -> Option<&str> {
self.description.as_ref().map(|s| s.as_str())
}
/// Set the `description`.
pub fn set_description(&mut self, value: Option<&str>) {
self.description = value.map(|x| x.to_string());
}
/// Get the Episode's `guid`.
pub fn guid(&self) -> Option<&str> {
self.guid.as_ref().map(|s| s.as_str())
}
/// Set the `guid`.
pub fn set_guid(&mut self, value: Option<&str>) {
self.guid = value.map(|x| x.to_string());
}
/// Get the `epoch` value.
///
/// Retrieved from the rss Item publish date.
@@ -111,11 +86,6 @@ impl Episode {
self.epoch
}
/// Set the `epoch`.
pub fn set_epoch(&mut self, value: i32) {
self.epoch = value;
}
/// Get the `length`.
///
/// The number represents the size of the file in bytes.
@@ -123,11 +93,6 @@ impl Episode {
self.length
}
/// Set the `length`.
pub fn set_length(&mut self, value: Option<i32>) {
self.length = value;
}
/// Get the `duration` value.
///
/// The number represents the duration of the item/episode in seconds.
@@ -135,11 +100,6 @@ impl Episode {
self.duration
}
/// Set the `duration`.
pub fn set_duration(&mut self, value: Option<i32>) {
self.duration = value;
}
/// Epoch representation of the last time the episode was played.
///
/// None/Null for unplayed.
@@ -147,22 +107,10 @@ impl Episode {
self.played
}
/// Set the `played` value.
pub fn set_played(&mut self, value: Option<i32>) {
self.played = value;
}
/// `Show` table foreign key.
pub fn show_id(&self) -> i32 {
self.show_id
}
/// Sets the `played` value with the current `epoch` timestap and save it.
pub fn set_played_now(&mut self) -> Result<(), DataError> {
let epoch = Utc::now().timestamp() as i32;
self.set_played(Some(epoch));
self.save().map(|_| ())
}
}
#[derive(Queryable, AsChangeset, PartialEq)]
@@ -275,11 +223,6 @@ impl EpisodeWidgetModel {
self.duration
}
/// Set the `duration`.
pub fn set_duration(&mut self, value: Option<i32>) {
self.duration = value;
}
/// Epoch representation of the last time the episode was played.
///
/// None/Null for unplayed.
@@ -288,7 +231,7 @@ impl EpisodeWidgetModel {
}
/// Set the `played` value.
pub fn set_played(&mut self, value: Option<i32>) {
fn set_played(&mut self, value: Option<i32>) {
self.played = value;
}
@@ -447,11 +390,6 @@ impl EpisodeMinimal {
self.length
}
/// Set the `length`.
pub fn set_length(&mut self, value: Option<i32>) {
self.length = value;
}
/// Get the `duration` value.
///
/// The number represents the duration of the item/episode in seconds.
+72 -70
View File
@@ -217,7 +217,8 @@ impl NewEpisodeMinimal {
let enc = item.enclosure();
// Get the url
let uri = enc.map(|s| url_cleaner(s.url().trim()))
let uri = enc
.map(|s| url_cleaner(s.url().trim()))
// Fallback to Rss.Item.link if enclosure is None.
.or_else(|| item.link().map(|s| url_cleaner(s.trim())));
@@ -312,6 +313,7 @@ impl NewEpisodeMinimal {
mod tests {
use database::truncate_db;
use dbqueries;
use failure::Error;
use models::new_episode::{NewEpisodeMinimal, NewEpisodeMinimalBuilder};
use models::*;
@@ -330,8 +332,7 @@ mod tests {
.title("The Super Bowl of Racism")
.uri(Some(String::from(
"http://traffic.megaphone.fm/PPY6458293736.mp3",
)))
.guid(Some(String::from("7df4070a-9832-11e7-adac-cb37b05d5e24")))
))).guid(Some(String::from("7df4070a-9832-11e7-adac-cb37b05d5e24")))
.epoch(1505296800)
.length(Some(66738886))
.duration(Some(4171))
@@ -344,8 +345,7 @@ mod tests {
.title("Atlas Golfed — U.S.-Backed Think Tanks Target Latin America")
.uri(Some(String::from(
"http://traffic.megaphone.fm/FL5331443769.mp3",
)))
.guid(Some(String::from("7c207a24-e33f-11e6-9438-eb45dcf36a1d")))
))).guid(Some(String::from("7c207a24-e33f-11e6-9438-eb45dcf36a1d")))
.epoch(1502272800)
.length(Some(67527575))
.duration(Some(4415))
@@ -364,8 +364,7 @@ mod tests {
.title("The Super Bowl of Racism")
.uri(Some(String::from(
"http://traffic.megaphone.fm/PPY6458293736.mp3",
)))
.description(Some(String::from(descr)))
))).description(Some(String::from(descr)))
.guid(Some(String::from("7df4070a-9832-11e7-adac-cb37b05d5e24")))
.length(Some(66738886))
.epoch(1505296800)
@@ -388,8 +387,7 @@ mod tests {
.title("Atlas Golfed — U.S.-Backed Think Tanks Target Latin America")
.uri(Some(String::from(
"http://traffic.megaphone.fm/FL5331443769.mp3",
)))
.description(Some(String::from(descr)))
))).description(Some(String::from(descr)))
.guid(Some(String::from("7c207a24-e33f-11e6-9438-eb45dcf36a1d")))
.length(Some(67527575))
.epoch(1502272800)
@@ -403,8 +401,7 @@ mod tests {
.title("The Super Bowl of Racism")
.uri(Some(String::from(
"http://traffic.megaphone.fm/PPY6458293736.mp3",
)))
.description(Some(String::from("New description")))
))).description(Some(String::from("New description")))
.guid(Some(String::from("7df4070a-9832-11e7-adac-cb37b05d5e24")))
.length(Some(66738886))
.epoch(1505296800)
@@ -418,8 +415,7 @@ mod tests {
.title("Hacking Devices with Kali Linux | LUP 214")
.uri(Some(String::from(
"http://www.podtrac.com/pts/redirect.mp3/traffic.libsyn.com/jnite/lup-0214.mp3",
)))
.guid(Some(String::from("78A682B4-73E8-47B8-88C0-1BE62DD4EF9D")))
))).guid(Some(String::from("78A682B4-73E8-47B8-88C0-1BE62DD4EF9D")))
.length(Some(46479789))
.epoch(1505280282)
.duration(Some(5733))
@@ -432,8 +428,7 @@ mod tests {
.title("Gnome Does it Again | LUP 213")
.uri(Some(String::from(
"http://www.podtrac.com/pts/redirect.mp3/traffic.libsyn.com/jnite/lup-0213.mp3",
)))
.guid(Some(String::from("1CE57548-B36C-4F14-832A-5D5E0A24E35B")))
))).guid(Some(String::from("1CE57548-B36C-4F14-832A-5D5E0A24E35B")))
.epoch(1504670247)
.length(Some(36544272))
.duration(Some(4491))
@@ -451,8 +446,7 @@ mod tests {
.title("Hacking Devices with Kali Linux | LUP 214")
.uri(Some(String::from(
"http://www.podtrac.com/pts/redirect.mp3/traffic.libsyn.com/jnite/lup-0214.mp3",
)))
.description(Some(String::from(descr)))
))).description(Some(String::from(descr)))
.guid(Some(String::from("78A682B4-73E8-47B8-88C0-1BE62DD4EF9D")))
.length(Some(46479789))
.epoch(1505280282)
@@ -473,8 +467,7 @@ mod tests {
.title("Gnome Does it Again | LUP 213")
.uri(Some(String::from(
"http://www.podtrac.com/pts/redirect.mp3/traffic.libsyn.com/jnite/lup-0213.mp3",
)))
.description(Some(String::from(descr)))
))).description(Some(String::from(descr)))
.guid(Some(String::from("1CE57548-B36C-4F14-832A-5D5E0A24E35B")))
.length(Some(36544272))
.epoch(1504670247)
@@ -486,68 +479,72 @@ mod tests {
}
#[test]
fn test_new_episode_minimal_intercepted() {
let file = File::open("tests/feeds/2018-01-20-Intercepted.xml").unwrap();
let channel = Channel::read_from(BufReader::new(file)).unwrap();
fn test_new_episode_minimal_intercepted() -> Result<(), Error> {
let file = File::open("tests/feeds/2018-01-20-Intercepted.xml")?;
let channel = Channel::read_from(BufReader::new(file))?;
let episode = channel.items().iter().nth(14).unwrap();
let ep = NewEpisodeMinimal::new(&episode, 42).unwrap();
let ep = NewEpisodeMinimal::new(&episode, 42)?;
assert_eq!(ep, *EXPECTED_MINIMAL_INTERCEPTED_1);
let episode = channel.items().iter().nth(15).unwrap();
let ep = NewEpisodeMinimal::new(&episode, 42).unwrap();
let ep = NewEpisodeMinimal::new(&episode, 42)?;
assert_eq!(ep, *EXPECTED_MINIMAL_INTERCEPTED_2);
Ok(())
}
#[test]
fn test_new_episode_intercepted() {
let file = File::open("tests/feeds/2018-01-20-Intercepted.xml").unwrap();
let channel = Channel::read_from(BufReader::new(file)).unwrap();
fn test_new_episode_intercepted() -> Result<(), Error> {
let file = File::open("tests/feeds/2018-01-20-Intercepted.xml")?;
let channel = Channel::read_from(BufReader::new(file))?;
let episode = channel.items().iter().nth(14).unwrap();
let ep = NewEpisode::new(&episode, 42).unwrap();
let ep = NewEpisode::new(&episode, 42)?;
assert_eq!(ep, *EXPECTED_INTERCEPTED_1);
let episode = channel.items().iter().nth(15).unwrap();
let ep = NewEpisode::new(&episode, 42).unwrap();
let ep = NewEpisode::new(&episode, 42)?;
assert_eq!(ep, *EXPECTED_INTERCEPTED_2);
Ok(())
}
#[test]
fn test_new_episode_minimal_lup() {
let file = File::open("tests/feeds/2018-01-20-LinuxUnplugged.xml").unwrap();
let channel = Channel::read_from(BufReader::new(file)).unwrap();
fn test_new_episode_minimal_lup() -> Result<(), Error> {
let file = File::open("tests/feeds/2018-01-20-LinuxUnplugged.xml")?;
let channel = Channel::read_from(BufReader::new(file))?;
let episode = channel.items().iter().nth(18).unwrap();
let ep = NewEpisodeMinimal::new(&episode, 42).unwrap();
let ep = NewEpisodeMinimal::new(&episode, 42)?;
assert_eq!(ep, *EXPECTED_MINIMAL_LUP_1);
let episode = channel.items().iter().nth(19).unwrap();
let ep = NewEpisodeMinimal::new(&episode, 42).unwrap();
let ep = NewEpisodeMinimal::new(&episode, 42)?;
assert_eq!(ep, *EXPECTED_MINIMAL_LUP_2);
Ok(())
}
#[test]
fn test_new_episode_lup() {
let file = File::open("tests/feeds/2018-01-20-LinuxUnplugged.xml").unwrap();
let channel = Channel::read_from(BufReader::new(file)).unwrap();
fn test_new_episode_lup() -> Result<(), Error> {
let file = File::open("tests/feeds/2018-01-20-LinuxUnplugged.xml")?;
let channel = Channel::read_from(BufReader::new(file))?;
let episode = channel.items().iter().nth(18).unwrap();
let ep = NewEpisode::new(&episode, 42).unwrap();
let ep = NewEpisode::new(&episode, 42)?;
assert_eq!(ep, *EXPECTED_LUP_1);
let episode = channel.items().iter().nth(19).unwrap();
let ep = NewEpisode::new(&episode, 42).unwrap();
let ep = NewEpisode::new(&episode, 42)?;
assert_eq!(ep, *EXPECTED_LUP_2);
Ok(())
}
#[test]
fn test_minimal_into_new_episode() {
truncate_db().unwrap();
fn test_minimal_into_new_episode() -> Result<(), Error> {
truncate_db()?;
let file = File::open("tests/feeds/2018-01-20-Intercepted.xml").unwrap();
let channel = Channel::read_from(BufReader::new(file)).unwrap();
let file = File::open("tests/feeds/2018-01-20-Intercepted.xml")?;
let channel = Channel::read_from(BufReader::new(file))?;
let item = channel.items().iter().nth(14).unwrap();
let ep = EXPECTED_MINIMAL_INTERCEPTED_1
@@ -564,42 +561,44 @@ mod tests {
.clone()
.into_new_episode(&item);
assert_eq!(ep, *EXPECTED_INTERCEPTED_2);
Ok(())
}
#[test]
fn test_new_episode_insert() {
truncate_db().unwrap();
fn test_new_episode_insert() -> Result<(), Error> {
truncate_db()?;
let file = File::open("tests/feeds/2018-01-20-Intercepted.xml").unwrap();
let channel = Channel::read_from(BufReader::new(file)).unwrap();
let file = File::open("tests/feeds/2018-01-20-Intercepted.xml")?;
let channel = Channel::read_from(BufReader::new(file))?;
let episode = channel.items().iter().nth(14).unwrap();
let new_ep = NewEpisode::new(&episode, 42).unwrap();
new_ep.insert().unwrap();
let ep = dbqueries::get_episode_from_pk(new_ep.title(), new_ep.show_id()).unwrap();
let new_ep = NewEpisode::new(&episode, 42)?;
new_ep.insert()?;
let ep = dbqueries::get_episode_from_pk(new_ep.title(), new_ep.show_id())?;
assert_eq!(new_ep, ep);
assert_eq!(&new_ep, &*EXPECTED_INTERCEPTED_1);
assert_eq!(&*EXPECTED_INTERCEPTED_1, &ep);
let episode = channel.items().iter().nth(15).unwrap();
let new_ep = NewEpisode::new(&episode, 42).unwrap();
new_ep.insert().unwrap();
let ep = dbqueries::get_episode_from_pk(new_ep.title(), new_ep.show_id()).unwrap();
let new_ep = NewEpisode::new(&episode, 42)?;
new_ep.insert()?;
let ep = dbqueries::get_episode_from_pk(new_ep.title(), new_ep.show_id())?;
assert_eq!(new_ep, ep);
assert_eq!(&new_ep, &*EXPECTED_INTERCEPTED_2);
assert_eq!(&*EXPECTED_INTERCEPTED_2, &ep);
Ok(())
}
#[test]
fn test_new_episode_update() {
truncate_db().unwrap();
let old = EXPECTED_INTERCEPTED_1.clone().to_episode().unwrap();
fn test_new_episode_update() -> Result<(), Error> {
truncate_db()?;
let old = EXPECTED_INTERCEPTED_1.clone().to_episode()?;
let updated = &*UPDATED_DURATION_INTERCEPTED_1;
updated.update(old.rowid()).unwrap();
let new = dbqueries::get_episode_from_pk(old.title(), old.show_id()).unwrap();
updated.update(old.rowid())?;
let new = dbqueries::get_episode_from_pk(old.title(), old.show_id())?;
// Assert that updating does not change the rowid and show_id
assert_ne!(old, new);
@@ -608,11 +607,12 @@ mod tests {
assert_eq!(updated, &new);
assert_ne!(updated, &old);
Ok(())
}
#[test]
fn test_new_episode_index() {
truncate_db().unwrap();
fn test_new_episode_index() -> Result<(), Error> {
truncate_db()?;
let expected = &*EXPECTED_INTERCEPTED_1;
// First insert
@@ -620,7 +620,7 @@ mod tests {
// Second identical, This should take the early return path
assert!(expected.index().is_ok());
// Get the episode
let old = dbqueries::get_episode_from_pk(expected.title(), expected.show_id()).unwrap();
let old = dbqueries::get_episode_from_pk(expected.title(), expected.show_id())?;
// Assert that NewPodcast is equal to the Indexed one
assert_eq!(*expected, old);
@@ -629,31 +629,33 @@ mod tests {
// Update the podcast
assert!(updated.index().is_ok());
// Get the new Podcast
let new = dbqueries::get_episode_from_pk(expected.title(), expected.show_id()).unwrap();
let new = dbqueries::get_episode_from_pk(expected.title(), expected.show_id())?;
// Assert it's diff from the old one.
assert_ne!(new, old);
assert_eq!(*updated, new);
assert_eq!(new.rowid(), old.rowid());
assert_eq!(new.show_id(), old.show_id());
Ok(())
}
#[test]
fn test_new_episode_to_episode() {
fn test_new_episode_to_episode() -> Result<(), Error> {
let expected = &*EXPECTED_INTERCEPTED_1;
// Assert insert() produces the same result that you would get with to_podcast()
truncate_db().unwrap();
expected.insert().unwrap();
let old = dbqueries::get_episode_from_pk(expected.title(), expected.show_id()).unwrap();
let ep = expected.to_episode().unwrap();
truncate_db()?;
expected.insert()?;
let old = dbqueries::get_episode_from_pk(expected.title(), expected.show_id())?;
let ep = expected.to_episode()?;
assert_eq!(old, ep);
// Same as above, diff order
truncate_db().unwrap();
let ep = expected.to_episode().unwrap();
truncate_db()?;
let ep = expected.to_episode()?;
// This should error as a unique constrain violation
assert!(expected.insert().is_err());
let old = dbqueries::get_episode_from_pk(expected.title(), expected.show_id()).unwrap();
let old = dbqueries::get_episode_from_pk(expected.title(), expected.show_id())?;
assert_eq!(old, ep);
Ok(())
}
}
+55 -47
View File
@@ -157,6 +157,7 @@ mod tests {
use super::*;
// use tokio_core::reactor::Core;
use failure::Error;
use rss::Channel;
use database::truncate_db;
@@ -199,8 +200,7 @@ mod tests {
.description(descr)
.image_uri(Some(String::from(
"http://www.jupiterbroadcasting.com/images/LASUN-Badge1400.jpg",
)))
.source_id(42)
))).source_id(42)
.build()
.unwrap()
};
@@ -222,8 +222,7 @@ mod tests {
.image_uri(Some(String::from(
"https://imagecdn.acast.com/image?h=1500&w=1500&source=http%3A%2F%2Fi1.sndcdn.\
com%2Favatars-000317856075-a2coqz-original.jpg",
)))
.source_id(42)
))).source_id(42)
.build()
.unwrap()
};
@@ -257,8 +256,7 @@ mod tests {
.description(descr)
.image_uri(Some(String::from(
"http://www.greaterthancode.com/wp-content/uploads/2016/10/code1400-4.jpg",
)))
.source_id(42)
))).source_id(42)
.build()
.unwrap()
};
@@ -289,73 +287,80 @@ mod tests {
}
#[test]
fn test_new_podcast_intercepted() {
let file = File::open("tests/feeds/2018-01-20-Intercepted.xml").unwrap();
let channel = Channel::read_from(BufReader::new(file)).unwrap();
fn test_new_podcast_intercepted() -> Result<(), Error> {
let file = File::open("tests/feeds/2018-01-20-Intercepted.xml")?;
let channel = Channel::read_from(BufReader::new(file))?;
let pd = NewShow::new(&channel, 42);
assert_eq!(*EXPECTED_INTERCEPTED, pd);
Ok(())
}
#[test]
fn test_new_podcast_lup() {
let file = File::open("tests/feeds/2018-01-20-LinuxUnplugged.xml").unwrap();
let channel = Channel::read_from(BufReader::new(file)).unwrap();
fn test_new_podcast_lup() -> Result<(), Error> {
let file = File::open("tests/feeds/2018-01-20-LinuxUnplugged.xml")?;
let channel = Channel::read_from(BufReader::new(file))?;
let pd = NewShow::new(&channel, 42);
assert_eq!(*EXPECTED_LUP, pd);
Ok(())
}
#[test]
fn test_new_podcast_thetipoff() {
let file = File::open("tests/feeds/2018-01-20-TheTipOff.xml").unwrap();
let channel = Channel::read_from(BufReader::new(file)).unwrap();
fn test_new_podcast_thetipoff() -> Result<(), Error> {
let file = File::open("tests/feeds/2018-01-20-TheTipOff.xml")?;
let channel = Channel::read_from(BufReader::new(file))?;
let pd = NewShow::new(&channel, 42);
assert_eq!(*EXPECTED_TIPOFF, pd);
Ok(())
}
#[test]
fn test_new_podcast_steal_the_stars() {
let file = File::open("tests/feeds/2018-01-20-StealTheStars.xml").unwrap();
let channel = Channel::read_from(BufReader::new(file)).unwrap();
fn test_new_podcast_steal_the_stars() -> Result<(), Error> {
let file = File::open("tests/feeds/2018-01-20-StealTheStars.xml")?;
let channel = Channel::read_from(BufReader::new(file))?;
let pd = NewShow::new(&channel, 42);
assert_eq!(*EXPECTED_STARS, pd);
Ok(())
}
#[test]
fn test_new_podcast_greater_than_code() {
let file = File::open("tests/feeds/2018-01-20-GreaterThanCode.xml").unwrap();
let channel = Channel::read_from(BufReader::new(file)).unwrap();
fn test_new_podcast_greater_than_code() -> Result<(), Error> {
let file = File::open("tests/feeds/2018-01-20-GreaterThanCode.xml")?;
let channel = Channel::read_from(BufReader::new(file))?;
let pd = NewShow::new(&channel, 42);
assert_eq!(*EXPECTED_CODE, pd);
Ok(())
}
#[test]
fn test_new_podcast_ellinofreneia() {
let file = File::open("tests/feeds/2018-03-28-Ellinofreneia.xml").unwrap();
let channel = Channel::read_from(BufReader::new(file)).unwrap();
fn test_new_podcast_ellinofreneia() -> Result<(), Error> {
let file = File::open("tests/feeds/2018-03-28-Ellinofreneia.xml")?;
let channel = Channel::read_from(BufReader::new(file))?;
let pd = NewShow::new(&channel, 42);
assert_eq!(*EXPECTED_ELLINOFRENEIA, pd);
Ok(())
}
#[test]
// This maybe could be a doc test on insert.
fn test_new_podcast_insert() {
truncate_db().unwrap();
let file = File::open("tests/feeds/2018-01-20-Intercepted.xml").unwrap();
let channel = Channel::read_from(BufReader::new(file)).unwrap();
fn test_new_podcast_insert() -> Result<(), Error> {
truncate_db()?;
let file = File::open("tests/feeds/2018-01-20-Intercepted.xml")?;
let channel = Channel::read_from(BufReader::new(file))?;
let npd = NewShow::new(&channel, 42);
npd.insert().unwrap();
let pd = dbqueries::get_podcast_from_source_id(42).unwrap();
npd.insert()?;
let pd = dbqueries::get_podcast_from_source_id(42)?;
assert_eq!(npd, pd);
assert_eq!(*EXPECTED_INTERCEPTED, npd);
assert_eq!(&*EXPECTED_INTERCEPTED, &pd);
Ok(())
}
#[test]
@@ -363,31 +368,32 @@ mod tests {
// Currently there's a test that only checks new description or title.
// If you have time and want to help, implement the test for the other fields
// too.
fn test_new_podcast_update() {
truncate_db().unwrap();
let old = EXPECTED_INTERCEPTED.to_podcast().unwrap();
fn test_new_podcast_update() -> Result<(), Error> {
truncate_db()?;
let old = EXPECTED_INTERCEPTED.to_podcast()?;
let updated = &*UPDATED_DESC_INTERCEPTED;
updated.update(old.id()).unwrap();
let new = dbqueries::get_podcast_from_source_id(42).unwrap();
updated.update(old.id())?;
let new = dbqueries::get_podcast_from_source_id(42)?;
assert_ne!(old, new);
assert_eq!(old.id(), new.id());
assert_eq!(old.source_id(), new.source_id());
assert_eq!(updated, &new);
assert_ne!(updated, &old);
Ok(())
}
#[test]
fn test_new_podcast_index() {
truncate_db().unwrap();
fn test_new_podcast_index() -> Result<(), Error> {
truncate_db()?;
// First insert
assert!(EXPECTED_INTERCEPTED.index().is_ok());
// Second identical, This should take the early return path
assert!(EXPECTED_INTERCEPTED.index().is_ok());
// Get the podcast
let old = dbqueries::get_podcast_from_source_id(42).unwrap();
let old = dbqueries::get_podcast_from_source_id(42)?;
// Assert that NewShow is equal to the Indexed one
assert_eq!(&*EXPECTED_INTERCEPTED, &old);
@@ -396,28 +402,30 @@ mod tests {
// Update the podcast
assert!(updated.index().is_ok());
// Get the new Show
let new = dbqueries::get_podcast_from_source_id(42).unwrap();
let new = dbqueries::get_podcast_from_source_id(42)?;
// Assert it's diff from the old one.
assert_ne!(new, old);
assert_eq!(new.id(), old.id());
assert_eq!(new.source_id(), old.source_id());
Ok(())
}
#[test]
fn test_to_podcast() {
fn test_to_podcast() -> Result<(), Error> {
// Assert insert() produces the same result that you would get with to_podcast()
truncate_db().unwrap();
EXPECTED_INTERCEPTED.insert().unwrap();
let old = dbqueries::get_podcast_from_source_id(42).unwrap();
let pd = EXPECTED_INTERCEPTED.to_podcast().unwrap();
truncate_db()?;
EXPECTED_INTERCEPTED.insert()?;
let old = dbqueries::get_podcast_from_source_id(42)?;
let pd = EXPECTED_INTERCEPTED.to_podcast()?;
assert_eq!(old, pd);
// Same as above, diff order
truncate_db().unwrap();
let pd = EXPECTED_INTERCEPTED.to_podcast().unwrap();
truncate_db()?;
let pd = EXPECTED_INTERCEPTED.to_podcast()?;
// This should error as a unique constrain violation
assert!(EXPECTED_INTERCEPTED.insert().is_err());
let old = dbqueries::get_podcast_from_source_id(42).unwrap();
let old = dbqueries::get_podcast_from_source_id(42)?;
assert_eq!(old, pd);
Ok(())
}
}
+1 -33
View File
@@ -1,8 +1,4 @@
use diesel::SaveChangesDsl;
use database::connection;
use errors::DataError;
use models::{Save, Source};
use models::Source;
use schema::shows;
#[derive(Queryable, Identifiable, AsChangeset, Associations, PartialEq)]
@@ -20,19 +16,6 @@ pub struct Show {
source_id: i32,
}
impl Save<Show> for Show {
type Error = DataError;
/// Helper method to easily save/"sync" current state of self to the
/// Database.
fn save(&self) -> Result<Show, Self::Error> {
let db = connection();
let tempdb = db.get()?;
self.save_changes::<Show>(&*tempdb).map_err(From::from)
}
}
impl Show {
/// Get the Feed `id`.
pub fn id(&self) -> i32 {
@@ -51,21 +34,11 @@ impl Show {
&self.link
}
/// Set the Show/Feed `link`.
pub fn set_link(&mut self, value: &str) {
self.link = value.to_string();
}
/// Get the `description`.
pub fn description(&self) -> &str {
&self.description
}
/// Set the `description`.
pub fn set_description(&mut self, value: &str) {
self.description = value.to_string();
}
/// Get the `image_uri`.
///
/// Represents the uri(url usually) that the Feed cover image is located at.
@@ -73,11 +46,6 @@ impl Show {
self.image_uri.as_ref().map(|s| s.as_str())
}
/// Set the `image_uri`.
pub fn set_image_uri(&mut self, value: Option<&str>) {
self.image_uri = value.map(|x| x.to_string());
}
/// `Source` table foreign key.
pub fn source_id(&self) -> i32 {
self.source_id
+96 -65
View File
@@ -4,12 +4,14 @@ use rss::Channel;
use url::Url;
use hyper::client::HttpConnector;
use hyper::header::{
ETag, EntityTag, HttpDate, IfModifiedSince, IfNoneMatch, LastModified, Location, UserAgent,
};
use hyper::{Client, Method, Request, Response, StatusCode, Uri};
use hyper::{Client, Body};
use hyper_tls::HttpsConnector;
use http::{Request, Response, StatusCode, Uri};
use http::header::{
ETAG, IF_MODIFIED_SINCE, IF_NONE_MATCH, LAST_MODIFIED, LOCATION, USER_AGENT as USER_AGENT_HEADER,
HeaderValue,
};
// use futures::future::ok;
use futures::future::{loop_fn, Future, Loop};
use futures::prelude::*;
@@ -91,19 +93,28 @@ impl Source {
/// Extract Etag and LastModifier from res, and update self and the
/// corresponding db row.
fn update_etag(&mut self, res: &Response) -> Result<(), DataError> {
fn update_etag(mut self, res: &Response<Body>) -> Result<Self, DataError> {
let headers = res.headers();
let etag = headers.get::<ETag>().map(|x| x.tag());
let lmod = headers.get::<LastModified>().map(|x| format!("{}", x));
let etag = headers.get(ETAG).and_then(|h| h.to_str().ok()).map(From::from);
let lmod = headers.get(LAST_MODIFIED).and_then(|h| h.to_str().ok()).map(From::from);
if (self.http_etag() != etag) || (self.last_modified != lmod) {
self.set_http_etag(etag);
self.set_last_modified(lmod);
self.save()?;
self = self.save()?;
}
Ok(())
Ok(self)
}
/// Clear the `HTTP` `Etag` and `Last-modified` headers.
/// This method does not sync the state of self in the database, call
/// .save() method explicitly
fn clear_etags(&mut self) {
debug!("Source etags before clear: {:#?}", &self);
self.http_etag = None;
self.last_modified = None;
}
fn make_err(self, context: &str, code: StatusCode) -> DataError {
@@ -120,41 +131,68 @@ impl Source {
// 408: Timeout
// 410: Feed deleted
// TODO: Rething this api,
fn match_status(mut self, res: Response) -> Result<Response, DataError> {
self.update_etag(&res)?;
fn match_status(mut self, res: Response<Body>) -> Result<Response<Body>, DataError> {
let code = res.status();
match code {
StatusCode::NotModified => return Err(self.make_err("304: skipping..", code)),
StatusCode::MovedPermanently => {
error!("Feed was moved permanently.");
self.handle_301(&res)?;
return Err(DataError::F301(self));
if code.is_success() {
// If request is succesful save the etag
self = self.update_etag(&res)?
} else {
match code.as_u16() {
// Save etags if it returns NotModified
304 => self = self.update_etag(&res)?,
// Clear the Etag/lmod else
_ => {
self.clear_etags();
self = self.save()?;
}
};
};
match code.as_u16() {
304 => {
info!("304: Source, (id: {}), is up to date", self.id());
return Err(DataError::FeedNotModified(self));
}
StatusCode::TemporaryRedirect => debug!("307: Temporary Redirect."),
StatusCode::PermanentRedirect => warn!("308: Permanent Redirect."),
StatusCode::Unauthorized => return Err(self.make_err("401: Unauthorized.", code)),
StatusCode::Forbidden => return Err(self.make_err("403: Forbidden.", code)),
StatusCode::NotFound => return Err(self.make_err("404: Not found.", code)),
StatusCode::RequestTimeout => return Err(self.make_err("408: Request Timeout.", code)),
StatusCode::Gone => return Err(self.make_err("410: Feed was deleted..", code)),
301 | 302 | 308 => {
warn!("Feed was moved permanently.");
self = self.update_url(&res)?;
return Err(DataError::FeedRedirect(self));
}
307 => {
warn!("307: Temporary Redirect.");
// FIXME: How is it actually handling the redirect?
return Err(DataError::FeedRedirect(self));
}
401 => return Err(self.make_err("401: Unauthorized.", code)),
403 => return Err(self.make_err("403: Forbidden.", code)),
404 => return Err(self.make_err("404: Not found.", code)),
408 => return Err(self.make_err("408: Request Timeout.", code)),
410 => return Err(self.make_err("410: Feed was deleted..", code)),
_ => info!("HTTP StatusCode: {}", code),
};
Ok(res)
}
fn handle_301(&mut self, res: &Response) -> Result<(), DataError> {
fn update_url(mut self, res: &Response<Body>) -> Result<Self, DataError> {
let code = res.status();
let headers = res.headers();
info!("HTTP StatusCode: {}", code);
debug!("Headers {:#?}", headers);
if let Some(url) = headers.get::<Location>() {
self.set_uri(url.to_string());
self.http_etag = None;
self.last_modified = None;
self.save()?;
info!("Feed url was updated succesfully.");
if let Some(url) = headers.get(LOCATION) {
debug!("Previous Source: {:#?}", &self);
self.set_uri(url.to_str()?.into());
self.clear_etags();
self = self.save()?;
debug!("Updated Source: {:#?}", &self);
info!("Feed url of Source {}, was updated succesfully.", self.id());
}
Ok(())
Ok(self)
}
/// Construct a new `Source` with the given `uri` and index it.
@@ -177,16 +215,15 @@ impl Source {
pub fn into_feed(
self,
client: Client<HttpsConnector<HttpConnector>>,
ignore_etags: bool,
) -> impl Future<Item = Feed, Error = DataError> {
let id = self.id();
let response = loop_fn(self, move |source| {
source
.request_constructor(&client.clone(), ignore_etags)
.request_constructor(&client.clone())
.then(|res| match res {
Ok(response) => Ok(Loop::Break(response)),
Err(err) => match err {
DataError::F301(s) => {
DataError::FeedRedirect(s) => {
info!("Following redirect...");
Ok(Loop::Continue(s))
}
@@ -206,31 +243,25 @@ impl Source {
})
}
// TODO: make ignore_etags an Enum for better ergonomics.
// #bools_are_just_2variant_enmus
fn request_constructor(
self,
client: &Client<HttpsConnector<HttpConnector>>,
ignore_etags: bool,
) -> impl Future<Item = Response, Error = DataError> {
) -> impl Future<Item = Response<Body>, Error = DataError> {
// FIXME: remove unwrap somehow
let uri = Uri::from_str(self.uri()).unwrap();
let mut req = Request::new(Method::Get, uri);
let mut req = Request::get(uri)
.body(Body::empty())
.unwrap();
// Set the UserAgent cause ppl still seem to check it for some reason...
req.headers_mut().set(UserAgent::new(USER_AGENT));
req.headers_mut().insert(USER_AGENT_HEADER, HeaderValue::from_static(USER_AGENT));
if !ignore_etags {
if let Some(etag) = self.http_etag() {
let tag = vec![EntityTag::new(true, etag.to_owned())];
req.headers_mut().set(IfNoneMatch::Items(tag));
}
if let Some(etag) = self.http_etag() {
req.headers_mut().insert(IF_NONE_MATCH, HeaderValue::from_str(etag).unwrap());
}
if let Some(lmod) = self.last_modified() {
if let Ok(date) = lmod.parse::<HttpDate>() {
req.headers_mut().set(IfModifiedSince(date));
}
}
if let Some(lmod) = self.last_modified() {
req.headers_mut().insert(IF_MODIFIED_SINCE, HeaderValue::from_str(lmod).unwrap());
}
client
@@ -240,9 +271,8 @@ impl Source {
}
}
#[allow(needless_pass_by_value)]
fn response_to_channel(res: Response) -> impl Future<Item = Channel, Error = DataError> + Send {
res.body()
fn response_to_channel(res: Response<Body>) -> impl Future<Item = Channel, Error = DataError> + Send {
res.into_body()
.concat2()
.map(|x| x.into_iter())
.map_err(From::from)
@@ -254,29 +284,30 @@ fn response_to_channel(res: Response) -> impl Future<Item = Channel, Error = Dat
#[cfg(test)]
mod tests {
use super::*;
use tokio_core::reactor::Core;
use failure::Error;
use num_cpus;
use tokio;
use database::truncate_db;
use utils::get_feed;
#[test]
fn test_into_feed() {
truncate_db().unwrap();
fn test_into_feed() -> Result<(), Error> {
truncate_db()?;
let mut core = Core::new().unwrap();
let client = Client::configure()
.connector(HttpsConnector::new(4, &core.handle()).unwrap())
.build(&core.handle());
let mut rt = tokio::runtime::Runtime::new()?;
let https = HttpsConnector::new(num_cpus::get())?;
let client = Client::builder().build::<_, Body>(https);
let url = "https://web.archive.org/web/20180120083840if_/https://feeds.feedburner.\
com/InterceptedWithJeremyScahill";
let source = Source::from_url(url).unwrap();
let source = Source::from_url(url)?;
let id = source.id();
let feed = source.into_feed(client, true);
let feed = core.run(feed).unwrap();
let feed = source.into_feed(client);
let feed = rt.block_on(feed)?;
let expected = get_feed("tests/feeds/2018-01-20-Intercepted.xml", id);
assert_eq!(expected, feed);
Ok(())
}
}
+6 -6
View File
@@ -42,8 +42,7 @@ pub fn import_to_db<R: Read>(reader: R) -> Result<Vec<Source>, reader::Error> {
}
s.ok()
})
.collect();
}).collect();
Ok(feeds)
}
@@ -92,8 +91,7 @@ pub fn extract_sources<R: Read>(reader: R) -> Result<HashSet<Opml>, reader::Erro
}
Err(err) => Err(err),
_ => Ok(()),
})
.collect::<Result<Vec<_>, reader::Error>>()?;
}).collect::<Result<Vec<_>, reader::Error>>()?;
Ok(list)
}
@@ -102,9 +100,10 @@ pub fn extract_sources<R: Read>(reader: R) -> Result<HashSet<Opml>, reader::Erro
mod tests {
use super::*;
use chrono::Local;
use failure::Error;
#[test]
fn test_extract() {
fn test_extract() -> Result<(), Error> {
let int_title = String::from("Intercepted with Jeremy Scahill");
let int_url = String::from("https://feeds.feedburner.com/InterceptedWithJeremyScahill");
let int_desc =
@@ -162,6 +161,7 @@ mod tests {
url: dec_url
},
];
assert_eq!(extract_sources(sample1.as_bytes()).unwrap(), map);
assert_eq!(extract_sources(sample1.as_bytes())?, map);
Ok(())
}
}
+46 -57
View File
@@ -1,42 +1,19 @@
// FIXME:
//! Docs.
use futures::future::*;
use futures::prelude::*;
use futures::stream::*;
use futures::{lazy, prelude::*, future::ok, stream::FuturesUnordered};
use tokio;
use hyper::client::HttpConnector;
use hyper::Client;
use hyper::{Client, Body};
use hyper_tls::HttpsConnector;
use tokio_core::reactor::Core;
use num_cpus;
use rayon;
use rayon_futures::ScopeFutureExt;
use errors::DataError;
use Source;
// use std::sync::{Arc, Mutex};
// http://gtk-rs.org/tuto/closures
#[macro_export]
macro_rules! clone {
(@param _) => ( _ );
(@param $x:ident) => ( $x );
($($n:ident),+ => move || $body:expr) => (
{
$( let $n = $n.clone(); )+
move || $body
}
);
($($n:ident),+ => move |$($p:tt),+| $body:expr) => (
{
$( let $n = $n.clone(); )+
move |$(clone!(@param $p),)+| $body
}
);
}
use std::iter::FromIterator;
type HttpsClient = Client<HttpsConnector<HttpConnector>>;
@@ -48,37 +25,47 @@ type HttpsClient = Client<HttpsConnector<HttpConnector>>;
/// Convert `rss::Channel` into `Feed` -> Index Podcast -> Index Episodes.
pub fn pipeline<'a, S>(
sources: S,
ignore_etags: bool,
client: &HttpsClient,
) -> impl Future<Item = Vec<()>, Error = DataError> + 'a
client: HttpsClient,
) -> impl Future<Item = (), Error = ()> + 'a
where
S: Stream<Item = Source, Error = DataError> + 'a,
S: Stream<Item = Source, Error = DataError> + Send + 'a,
{
sources
.and_then(clone!(client => move |s| s.into_feed(client.clone(), ignore_etags)))
.and_then(|feed| rayon::scope(|s| s.spawn_future(feed.index())))
// the stream will stop at the first error so
// we ensure that everything will succeded regardless.
.map_err(|err| error!("Error: {}", err))
.then(|_| ok::<(), DataError>(()))
.collect()
.and_then(move |s| s.into_feed(client.clone()))
.map_err(|err| {
match err {
// Avoid spamming the stderr when its not an eactual error
DataError::FeedNotModified(_) => (),
_ => error!("Error: {}", err),
}
})
.and_then(move |feed| {
let fut = lazy(|| feed.index().map_err(|err| error!("Error: {}", err)));
tokio::spawn(fut);
Ok(())
})
// For each terminates the stream at the first error so we make sure
// we pass good values regardless
.then(move |_| ok(()))
// Convert the stream into a Future to later execute as a tokio task
.for_each(move |_| ok(()))
}
/// Creates a tokio `reactor::Core`, and a `hyper::Client` and
/// runs the pipeline to completion. The `reactor::Core` is dropped afterwards.
pub fn run<S>(sources: S, ignore_etags: bool) -> Result<(), DataError>
pub fn run<S>(sources: S) -> Result<(), DataError>
where
S: IntoIterator<Item = Source>,
{
let mut core = Core::new()?;
let handle = core.handle();
let client = Client::configure()
.connector(HttpsConnector::new(num_cpus::get(), &handle)?)
.build(&handle);
let https = HttpsConnector::new(num_cpus::get())?;
let client = Client::builder().build::<_, Body>(https);
let stream = iter_ok::<_, DataError>(sources);
let p = pipeline(stream, ignore_etags, &client);
core.run(p).map(|_| ())
let foo = sources.into_iter().map(ok::<_, _>);
let stream = FuturesUnordered::from_iter(foo);
let p = pipeline(stream, client);
tokio::run(p);
Ok(())
}
#[cfg(test)]
@@ -86,6 +73,7 @@ mod tests {
use super::*;
use database::truncate_db;
use dbqueries;
use failure::Error;
use Source;
// (path, url) tuples.
@@ -101,28 +89,29 @@ mod tests {
#[test]
/// Insert feeds and update/index them.
fn test_pipeline() {
truncate_db().unwrap();
fn test_pipeline() -> Result<(), Error> {
truncate_db()?;
let bad_url = "https://gitlab.gnome.org/World/podcasts.atom";
// if a stream returns error/None it stops
// bad we want to parse all feeds regardless if one fails
Source::from_url(bad_url).unwrap();
Source::from_url(bad_url)?;
URLS.iter().for_each(|url| {
// Index the urls into the source table.
Source::from_url(url).unwrap();
});
let sources = dbqueries::get_sources().unwrap();
run(sources, true).unwrap();
let sources = dbqueries::get_sources()?;
run(sources)?;
let sources = dbqueries::get_sources().unwrap();
let sources = dbqueries::get_sources()?;
// Run again to cover Unique constrains erros.
run(sources, true).unwrap();
run(sources)?;
// Assert the index rows equal the controlled results
assert_eq!(dbqueries::get_sources().unwrap().len(), 6);
assert_eq!(dbqueries::get_podcasts().unwrap().len(), 5);
assert_eq!(dbqueries::get_episodes().unwrap().len(), 354);
assert_eq!(dbqueries::get_sources()?.len(), 6);
assert_eq!(dbqueries::get_podcasts()?.len(), 5);
assert_eq!(dbqueries::get_episodes()?.len(), 354);
Ok(())
}
}
+44 -40
View File
@@ -25,8 +25,7 @@ fn download_checker() -> Result<(), DataError> {
return Some(ep);
}
None
})
.for_each(|ep| {
}).for_each(|ep| {
ep.set_local_uri(None);
ep.save()
.map_err(|err| error!("{}", err))
@@ -163,6 +162,7 @@ mod tests {
use self::tempdir::TempDir;
use super::*;
use chrono::Duration;
use failure::Error;
use database::truncate_db;
use models::NewEpisodeBuilder;
@@ -170,15 +170,15 @@ mod tests {
use std::fs::File;
use std::io::Write;
fn helper_db() -> TempDir {
fn helper_db() -> Result<TempDir, Error> {
// Clean the db
truncate_db().unwrap();
truncate_db()?;
// Setup tmp file stuff
let tmp_dir = TempDir::new("podcasts_test").unwrap();
let tmp_dir = TempDir::new("podcasts_test")?;
let valid_path = tmp_dir.path().join("virtual_dl.mp3");
let bad_path = tmp_dir.path().join("invalid_thing.mp3");
let mut tmp_file = File::create(&valid_path).unwrap();
writeln!(tmp_file, "Foooo").unwrap();
let mut tmp_file = File::create(&valid_path)?;
writeln!(tmp_file, "Foooo")?;
// Setup episodes
let n1 = NewEpisodeBuilder::default()
@@ -186,33 +186,31 @@ mod tests {
.show_id(0)
.build()
.unwrap()
.to_episode()
.unwrap();
.to_episode()?;
let n2 = NewEpisodeBuilder::default()
.title("bar_baz".to_string())
.show_id(1)
.build()
.unwrap()
.to_episode()
.unwrap();
.to_episode()?;
let mut ep1 = dbqueries::get_episode_from_pk(n1.title(), n1.show_id()).unwrap();
let mut ep2 = dbqueries::get_episode_from_pk(n2.title(), n2.show_id()).unwrap();
let mut ep1 = dbqueries::get_episode_cleaner_from_pk(n1.title(), n1.show_id())?;
let mut ep2 = dbqueries::get_episode_cleaner_from_pk(n2.title(), n2.show_id())?;
ep1.set_local_uri(Some(valid_path.to_str().unwrap()));
ep2.set_local_uri(Some(bad_path.to_str().unwrap()));
ep1.save().unwrap();
ep2.save().unwrap();
ep1.save()?;
ep2.save()?;
tmp_dir
Ok(tmp_dir)
}
#[test]
fn test_download_checker() {
let tmp_dir = helper_db();
download_checker().unwrap();
let episodes = dbqueries::get_downloaded_episodes().unwrap();
fn test_download_checker() -> Result<(), Error> {
let tmp_dir = helper_db()?;
download_checker()?;
let episodes = dbqueries::get_downloaded_episodes()?;
let valid_path = tmp_dir.path().join("virtual_dl.mp3");
assert_eq!(episodes.len(), 1);
@@ -221,69 +219,75 @@ mod tests {
episodes.first().unwrap().local_uri()
);
let _tmp_dir = helper_db();
download_checker().unwrap();
let episode = dbqueries::get_episode_from_pk("bar_baz", 1).unwrap();
let _tmp_dir = helper_db()?;
download_checker()?;
let episode = dbqueries::get_episode_cleaner_from_pk("bar_baz", 1)?;
assert!(episode.local_uri().is_none());
Ok(())
}
#[test]
fn test_download_cleaner() {
let _tmp_dir = helper_db();
fn test_download_cleaner() -> Result<(), Error> {
let _tmp_dir = helper_db()?;
let mut episode: EpisodeCleanerModel =
dbqueries::get_episode_from_pk("foo_bar", 0).unwrap().into();
dbqueries::get_episode_cleaner_from_pk("foo_bar", 0)?.into();
let valid_path = episode.local_uri().unwrap().to_owned();
delete_local_content(&mut episode).unwrap();
delete_local_content(&mut episode)?;
assert_eq!(Path::new(&valid_path).exists(), false);
Ok(())
}
#[test]
fn test_played_cleaner_expired() {
let _tmp_dir = helper_db();
let mut episode = dbqueries::get_episode_from_pk("foo_bar", 0).unwrap();
fn test_played_cleaner_expired() -> Result<(), Error> {
let _tmp_dir = helper_db()?;
let mut episode = dbqueries::get_episode_cleaner_from_pk("foo_bar", 0)?;
let cleanup_date = Utc::now() - Duration::seconds(1000);
let epoch = cleanup_date.timestamp() as i32 - 1;
episode.set_played(Some(epoch));
episode.save().unwrap();
episode.save()?;
let valid_path = episode.local_uri().unwrap().to_owned();
// This should delete the file
played_cleaner(cleanup_date).unwrap();
played_cleaner(cleanup_date)?;
assert_eq!(Path::new(&valid_path).exists(), false);
Ok(())
}
#[test]
fn test_played_cleaner_none() {
let _tmp_dir = helper_db();
let mut episode = dbqueries::get_episode_from_pk("foo_bar", 0).unwrap();
fn test_played_cleaner_none() -> Result<(), Error> {
let _tmp_dir = helper_db()?;
let mut episode = dbqueries::get_episode_cleaner_from_pk("foo_bar", 0)?;
let cleanup_date = Utc::now() - Duration::seconds(1000);
let epoch = cleanup_date.timestamp() as i32 + 1;
episode.set_played(Some(epoch));
episode.save().unwrap();
episode.save()?;
let valid_path = episode.local_uri().unwrap().to_owned();
// This should not delete the file
played_cleaner(cleanup_date).unwrap();
played_cleaner(cleanup_date)?;
assert_eq!(Path::new(&valid_path).exists(), true);
Ok(())
}
#[test]
fn test_url_cleaner() {
fn test_url_cleaner() -> Result<(), Error> {
let good_url = "http://traffic.megaphone.fm/FL8608731318.mp3";
let bad_url = "http://traffic.megaphone.fm/FL8608731318.mp3?updated=1484685184";
assert_eq!(url_cleaner(bad_url), good_url);
assert_eq!(url_cleaner(good_url), good_url);
assert_eq!(url_cleaner(&format!(" {}\t\n", bad_url)), good_url);
Ok(())
}
#[test]
// This test needs access to local system so we ignore it by default.
#[ignore]
fn test_get_dl_folder() {
fn test_get_dl_folder() -> Result<(), Error> {
let foo_ = format!("{}/{}", DL_DIR.to_str().unwrap(), "foo");
assert_eq!(get_download_folder("foo").unwrap(), foo_);
assert_eq!(get_download_folder("foo")?, foo_);
let _ = fs::remove_dir_all(foo_);
Ok(())
}
}
+5 -7
View File
@@ -2,21 +2,19 @@
authors = ["Jordan Petridis <jordanpetridis@protonmail.com>"]
name = "podcasts-downloader"
version = "0.1.0"
workspace = "../"
[dependencies]
error-chain = "0.12.0"
hyper = "0.11.27"
log = "0.4.3"
log = "0.4.4"
mime_guess = "1.8.6"
reqwest = "0.8.6"
reqwest = "0.9.2"
tempdir = "0.3.7"
glob = "0.2.11"
failure = "0.1.1"
failure_derive = "0.1.1"
failure = "0.1.2"
failure_derive = "0.1.2"
[dependencies.podcasts-data]
path = "../podcasts-data"
[dev-dependencies]
pretty_assertions = "0.5.1"
pretty_assertions = "0.5.1"
+46 -27
View File
@@ -1,8 +1,8 @@
use glob::glob;
use hyper::header::*;
use mime_guess;
use reqwest;
use reqwest::RedirectPolicy;
use reqwest::header::*;
use tempdir::TempDir;
use std::fs;
@@ -22,9 +22,12 @@ use errors::DownloadError;
// with / or not.
pub trait DownloadProgress {
fn get_downloaded(&self) -> u64;
fn set_downloaded(&mut self, downloaded: u64);
fn get_size(&self) -> u64;
fn set_size(&mut self, bytes: u64);
fn should_cancel(&self) -> bool;
fn cancel(&mut self);
}
// Adapted from https://github.com/mattgathu/rget .
@@ -38,7 +41,7 @@ fn download_into(
dir: &str,
file_title: &str,
url: &str,
progress: Option<Arc<Mutex<DownloadProgress>>>,
progress: Option<Arc<Mutex<dyn DownloadProgress>>>,
) -> Result<String, DownloadError> {
info!("GET request to: {}", url);
// Haven't included the loop check as
@@ -63,16 +66,29 @@ fn download_into(
info!("Status Resp: {}", resp.status());
if !resp.status().is_success() {
if let Some(ref prog) = progress {
if let Ok(mut m) = prog.lock() {
m.cancel();
}
}
return Err(DownloadError::UnexpectedResponse(resp.status()));
}
let headers = resp.headers().clone();
let ct_len = headers.get::<ContentLength>().map(|ct_len| **ct_len);
let ct_type = headers.get::<ContentType>();
let ct_len = headers
.get(CONTENT_LENGTH)
.and_then(|h| h.to_str().ok())
.and_then(|len| len.parse().ok());
let ct_type = headers
.get(CONTENT_TYPE)
.and_then(|h| h.to_str().ok())
.map(From::from);
ct_len.map(|x| info!("File Lenght: {}", x));
ct_type.map(|x| info!("Content Type: {}", x));
let ext = get_ext(ct_type.cloned()).unwrap_or_else(|| String::from("unknown"));
let ext = get_ext(ct_type).unwrap_or_else(|| String::from("unknown"));
info!("Extension: {}", ext);
// Construct a temp file to save desired content.
@@ -82,9 +98,10 @@ fn download_into(
let out_file = format!("{}/temp.part", tempdir.path().to_str().unwrap(),);
ct_len.map(|x| {
if let Some(p) = progress.clone() {
let mut m = p.lock().unwrap();
m.set_size(x);
if let Some(ref p) = progress {
if let Ok(mut m) = p.lock() {
m.set_size(x);
}
}
});
@@ -100,17 +117,17 @@ fn download_into(
}
/// Determine the file extension from the http content-type header.
fn get_ext(content: Option<ContentType>) -> Option<String> {
let cont = content.clone()?;
content
.and_then(|c| mime_guess::get_extensions(c.type_().as_ref(), c.subtype().as_ref()))
.and_then(|c| {
if c.contains(&cont.subtype().as_ref()) {
Some(cont.subtype().as_ref().to_string())
} else {
Some(c.first().unwrap().to_string())
}
})
fn get_ext(content: Option<&str>) -> Option<String> {
let mut iter = content?.split("/");
let type_ = iter.next()?;
let subtype = iter.next()?;
mime_guess::get_extensions(type_, subtype).and_then(|c| {
if c.contains(&&subtype) {
Some(subtype.to_string())
} else {
Some(c.first()?.to_string())
}
})
}
// TODO: Write unit-tests.
@@ -122,7 +139,7 @@ fn save_io(
file: &str,
resp: &mut reqwest::Response,
content_lenght: Option<u64>,
progress: Option<Arc<Mutex<DownloadProgress>>>,
progress: Option<Arc<Mutex<dyn DownloadProgress>>>,
) -> Result<(), DownloadError> {
info!("Downloading into: {}", file);
let chunk_size = match content_lenght {
@@ -163,7 +180,7 @@ fn save_io(
pub fn get_episode(
ep: &mut EpisodeWidgetModel,
download_folder: &str,
progress: Option<Arc<Mutex<DownloadProgress>>>,
progress: Option<Arc<Mutex<dyn DownloadProgress>>>,
) -> Result<(), DownloadError> {
// Check if its alrdy downloaded
if ep.local_uri().is_some() {
@@ -236,6 +253,7 @@ pub fn cache_image(pd: &ShowCoverModel) -> Result<String, DownloadError> {
#[cfg(test)]
mod tests {
use super::*;
use failure::Error;
use podcasts_data::dbqueries;
use podcasts_data::pipeline;
use podcasts_data::Source;
@@ -246,17 +264,17 @@ mod tests {
// This test inserts an rss feed to your `XDG_DATA/podcasts/podcasts.db` so we make it explicit
// to run it.
#[ignore]
fn test_cache_image() {
fn test_cache_image() -> Result<(), Error> {
let url = "https://web.archive.org/web/20180120110727if_/https://rss.acast.com/thetipoff";
// Create and index a source
let source = Source::from_url(url).unwrap();
let source = Source::from_url(url)?;
// Copy it's id
let sid = source.id();
// Convert Source it into a future Feed and index it
pipeline::run(vec![source], true).unwrap();
pipeline::run(vec![source])?;
// Get the Podcast
let pd = dbqueries::get_podcast_from_source_id(sid).unwrap().into();
let pd = dbqueries::get_podcast_from_source_id(sid)?.into();
let img_path = cache_image(&pd);
let foo_ = format!(
@@ -264,7 +282,8 @@ mod tests {
PODCASTS_CACHE.to_str().unwrap(),
pd.title()
);
assert_eq!(img_path.unwrap(), foo_);
fs::remove_file(foo_).unwrap();
assert_eq!(img_path?, foo_);
fs::remove_file(foo_)?;
Ok(())
}
}
+2
View File
@@ -1,3 +1,5 @@
#![allow(bare_trait_objects)]
use podcasts_data::errors::DataError;
use reqwest;
use std::io;
-1
View File
@@ -44,7 +44,6 @@ extern crate log;
extern crate pretty_assertions;
extern crate glob;
extern crate hyper;
extern crate mime_guess;
extern crate podcasts_data;
extern crate reqwest;
+26 -21
View File
@@ -3,38 +3,43 @@ authors = ["Jordan Petridis <jordanpetridis@protonmail.com>"]
build = "build.rs"
name = "podcasts-gtk"
version = "0.1.0"
workspace = "../"
[dependencies]
chrono = "0.4.4"
crossbeam-channel = "0.2.3"
gdk = "0.8.0"
gdk-pixbuf = "0.4.0"
glib = "0.5.0"
gstreamer = "0.11.4"
gstreamer-player = "0.11.3"
chrono = "0.4.6"
crossbeam-channel = "0.2.6"
gdk = "0.9.0"
gdk-pixbuf = "0.5.0"
glib = "0.6.0"
gstreamer = "0.12.0"
gstreamer-player = "0.12.0"
humansize = "1.1.0"
lazy_static = "1.0.2"
log = "0.4.3"
lazy_static = "1.1.0"
log = "0.4.4"
loggerv = "0.7.1"
open = "1.2.1"
open = "1.2.2"
rayon = "1.0.2"
send-cell = "0.1.3"
url = "1.7.1"
failure = "0.1.1"
failure_derive = "0.1.1"
regex = "1.0.2"
reqwest = "0.8.6"
serde_json = "1.0.24"
html2text = "0.1.8"
failure = "0.1.2"
failure_derive = "0.1.2"
fragile = "0.3.0"
regex = "1.0.4"
reqwest = "0.9.2"
serde_json = "1.0.31"
# html2text = "0.1.8"
html2text = { git = "https://github.com/alatiera/rust-html2text" }
gettext-rs = { git = "https://github.com/danigm/gettext-rs", branch = "no-gettext", features = ["gettext-system"] }
mpris-player = "0.1.0"
[dependencies.gtk]
features = ["v3_22"]
version = "0.4.1"
version = "0.5.0"
[dependencies.gio]
features = ["v2_50"]
version = "0.4.1"
version = "0.5.0"
[dependencies.libhandy]
git = "https://gitlab.gnome.org/jsparber/libhandy-rs"
[dependencies.podcasts-data]
path = "../podcasts-data"
@@ -43,4 +48,4 @@ path = "../podcasts-data"
path = "../podcasts-downloader"
[dev-dependencies]
pretty_assertions = "0.5.1"
pretty_assertions = "0.5.1"
+3 -2
View File
@@ -5,9 +5,10 @@ fn main() {
println!("cargo:rerun-if-changed=resources");
println!("cargo:rerun-if-changed=resources/*");
Command::new("glib-compile-resources")
let out = Command::new("glib-compile-resources")
.args(&["--generate", "resources.xml"])
.current_dir("resources")
.status()
.unwrap();
.expect("failed to generate resources");
assert!(out.success());
}
+19
View File
@@ -0,0 +1,19 @@
# please keep this list sorted alphabetically
#
ca
cs
de
es
fi
fur
gl
hr
hu
id
it
ko
lv
pl
pt_BR
sv
tr
+46
View File
@@ -0,0 +1,46 @@
# List of source files containing translatable strings.
# Please keep this file sorted alphabetically.
podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml
podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in
podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in
# ui files
podcasts-gtk/resources/gtk/empty_view.ui
podcasts-gtk/resources/gtk/episode_widget.ui
podcasts-gtk/resources/gtk/hamburger.ui
podcasts-gtk/resources/gtk/headerbar.ui
podcasts-gtk/resources/gtk/help-overlay.ui
podcasts-gtk/resources/gtk/home_episode.ui
podcasts-gtk/resources/gtk/home_view.ui
podcasts-gtk/resources/gtk/inapp_notif.ui
podcasts-gtk/resources/gtk/player_toolbar.ui
podcasts-gtk/resources/gtk/prefs.ui
podcasts-gtk/resources/gtk/secondary_menu.ui
podcasts-gtk/resources/gtk/show_menu.ui
podcasts-gtk/resources/gtk/show_widget.ui
# rust files
podcasts-gtk/src/app.rs
podcasts-gtk/src/headerbar.rs
podcasts-gtk/src/main.rs
podcasts-gtk/src/manager.rs
podcasts-gtk/src/prefs.rs
podcasts-gtk/src/settings.rs
podcasts-gtk/src/stacks/content.rs
podcasts-gtk/src/stacks/home.rs
podcasts-gtk/src/stacks/mod.rs
podcasts-gtk/src/stacks/populated.rs
podcasts-gtk/src/stacks/show.rs
podcasts-gtk/src/static_resource.rs
podcasts-gtk/src/utils.rs
podcasts-gtk/src/widgets/aboutdialog.rs
podcasts-gtk/src/widgets/appnotif.rs
podcasts-gtk/src/widgets/base_view.rs
podcasts-gtk/src/widgets/empty.rs
podcasts-gtk/src/widgets/episode.rs
podcasts-gtk/src/widgets/home_view.rs
podcasts-gtk/src/widgets/mod.rs
podcasts-gtk/src/widgets/player.rs
podcasts-gtk/src/widgets/show_menu.rs
podcasts-gtk/src/widgets/show.rs
podcasts-gtk/src/widgets/shows_view.rs
+404
View File
@@ -0,0 +1,404 @@
# Catalan translation for podcasts.
# Copyright (C) 2018 podcasts's COPYRIGHT HOLDER
# This file is distributed under the same license as the podcasts package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: podcasts master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/podcasts/issues\n"
"POT-Creation-Date: 2018-09-23 15:55+0000\n"
"PO-Revision-Date: 2018-09-23 23:03-0500\n"
"Language-Team: Catalan <gnome@llistes.softcatala.org>\n"
"Language: ca\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Last-Translator: Adolfo Jayme Barrientos <fitojb@ubuntu.com>\n"
"X-Generator: Poedit 2.1.1\n"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:15
msgid "Top position of the last open main window"
msgstr "Posició superior de l'última finestra principal oberta"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:19
msgid "Left position of the last open main window"
msgstr "Posició esquerra de l'última finestra principal oberta"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:23
msgid "Height of the last open main window"
msgstr "Alçada de l'última finestra principal oberta"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:27
msgid "Width of the last open main window"
msgstr "Amplada de l'última finestra principal oberta"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:31
msgid "Maximized state of the last open main window"
msgstr "Estat de maximització de l'ultima finestra principal oberta"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:36
msgid "Enable or disable dark theme"
msgstr "Habilita o inhabilita el tema fosc"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:41
msgid "Whether to periodically refresh content"
msgstr "Si s'ha d'actualitzar periòdicament el contingut"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:46
msgid "How many periods of time to wait between automatic refreshes"
msgstr "Quants períodes de temps esperar entre les actualitzacions automàtiques"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:50
msgid "What period of time to wait between automatic refreshes"
msgstr "Quin període de temps esperar entre les actualitzacions automàtiques"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:54
msgid "Whether to refresh content after startup"
msgstr "Si s'ha d'actualitzar el contingut després d'iniciar"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:60
msgid "How many periods of time to wait between automatic cleanups"
msgstr "Quants períodes de temps esperar entre les neteges automàtiques"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:64
msgid "What period of time to wait between automatic cleanups"
msgstr "Quin període de temps esperar entre les neteges automàtiques"
#. Weird magic I copy-pasted that sets the Application Name in the Shell.
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:3
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:4
#: podcasts-gtk/src/app.rs:91 podcasts-gtk/src/app.rs:414
#: podcasts-gtk/src/widgets/aboutdialog.rs:31
msgid "Podcasts"
msgstr "Podcasts"
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:4
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:10
msgid "Listen to your favorite podcasts, right from your desktop."
msgstr "Escolteu els vostres podcasts predilectes, sense sortir del vostre escriptori."
#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:6
msgid "@icon@"
msgstr "@icon@"
#. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:13
msgid "Podcast;RSS;"
msgstr "Podcast;RSS;"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:8
msgid "Podcast app for GNOME"
msgstr "Aplicació de podcasts per al GNOME"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:51
msgid "Jordan Petridis"
msgstr "Jordan Petridis"
#: podcasts-gtk/resources/gtk/empty_view.ui:46
msgid "This show does not have episodes yet"
msgstr "Aquest programa no té cap episodi encara"
#: podcasts-gtk/resources/gtk/empty_view.ui:62
msgid "If you think this is an error, please consider writing a bug report."
msgstr "Si sospiteu que això és un error, considereu crear un informe."
#: podcasts-gtk/resources/gtk/empty_view.ui:106
msgid "Get some shows"
msgstr "Cerqueu-vos alguns programes"
#: podcasts-gtk/resources/gtk/empty_view.ui:143
msgid "Add new shows via feed URL"
msgstr "Afegeix programes nous mitjançant l'URL del canal"
#: podcasts-gtk/resources/gtk/empty_view.ui:172
msgid "Import shows from another device"
msgstr "Importa programes des d'un altre dispositiu"
#: podcasts-gtk/resources/gtk/episode_widget.ui:180
msgid "Calculating episode size…"
msgstr "S'està calculant la mida de l'episodi…"
#: podcasts-gtk/resources/gtk/episode_widget.ui:220
msgid "Play this episode"
msgstr "Reprodueix aquest episodi"
#: podcasts-gtk/resources/gtk/episode_widget.ui:241
msgid "Cancel the download proccess"
msgstr "Cancel·la el procés de baixada"
#: podcasts-gtk/resources/gtk/episode_widget.ui:264
msgid "Download this episode"
msgstr "Baixa aquest episodi"
#: podcasts-gtk/resources/gtk/hamburger.ui:7
msgid "_Check for New Episodes"
msgstr "_Comprova si hi ha episodis nous"
#: podcasts-gtk/resources/gtk/hamburger.ui:12
msgid "_Import Shows"
msgstr "_Importa programes"
#: podcasts-gtk/resources/gtk/hamburger.ui:22
msgid "_Preferences"
msgstr "_Preferències"
#: podcasts-gtk/resources/gtk/hamburger.ui:29
msgid "_Keyboard Shortcuts"
msgstr "_Dreceres de teclat"
#: podcasts-gtk/resources/gtk/hamburger.ui:37
msgid "_About"
msgstr "_Quant a"
#: podcasts-gtk/resources/gtk/headerbar.ui:35
#: podcasts-gtk/resources/gtk/headerbar.ui:189
msgid "Add a new feed"
msgstr "Afegeix un canal nou"
#: podcasts-gtk/resources/gtk/headerbar.ui:53
msgid "Enter feed address to add"
msgstr "Introduïu l'adreça del canal que s'ha d'afegir"
#: podcasts-gtk/resources/gtk/headerbar.ui:89
msgid "Add"
msgstr "Afegeix"
#: podcasts-gtk/resources/gtk/headerbar.ui:133
msgid "You are already subscribed to that feed!"
msgstr "Ja us heu subscrit a aquest canal."
#: podcasts-gtk/resources/gtk/headerbar.ui:169
msgid "Show Title"
msgstr "Títol del programa"
#: podcasts-gtk/resources/gtk/headerbar.ui:210
msgid "Back"
msgstr "Enrere"
#: podcasts-gtk/resources/gtk/help-overlay.ui:12
msgid "General"
msgstr "General"
#: podcasts-gtk/resources/gtk/help-overlay.ui:18
msgctxt "shortcut window"
msgid "Check for new episodes"
msgstr "Comprova si hi ha episodis nous"
#: podcasts-gtk/resources/gtk/help-overlay.ui:25
msgctxt "shortcut window"
msgid "Preferences"
msgstr "Preferències"
#: podcasts-gtk/resources/gtk/help-overlay.ui:32
msgctxt "shortcut window"
msgid "Quit the application"
msgstr "Surt de l'aplicació"
#: podcasts-gtk/resources/gtk/home_view.ui:56
msgid "Today"
msgstr "Avui"
#: podcasts-gtk/resources/gtk/home_view.ui:112
msgid "Yesterday"
msgstr "Ahir"
#: podcasts-gtk/resources/gtk/home_view.ui:168
msgid "This Week"
msgstr "Aquesta setmana"
#: podcasts-gtk/resources/gtk/home_view.ui:224
msgid "This Month"
msgstr "Aquest mes"
#: podcasts-gtk/resources/gtk/home_view.ui:281
msgid "Older"
msgstr "Més antics"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:101
msgid "An in-app action notification"
msgstr "Una notificació d'acció dins l'aplicació"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:112
msgid "Undo"
msgstr "Desfés"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:72
msgid "Rewind 10 seconds"
msgstr "Rebobina 10 segons"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:87
msgid "Play"
msgstr "Reprodueix"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:103
msgid "Pause"
msgstr "Posa en pausa"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:119
msgid "Fast forward 10 seconds"
msgstr "Avança 10 segons"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:285
msgid "Change the playback speed"
msgstr "Canvia la velocitat de la reproducció"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:300
#: podcasts-gtk/resources/gtk/player_toolbar.ui:380
msgid "1.00×"
msgstr "1,00×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:344
msgid "1.50×"
msgstr "1,50×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:348
msgid "1.5 speed rate"
msgstr "Velocitat d'1,5"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:362
msgid "1.25×"
msgstr "1,25×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:366
msgid "1.25 speed rate"
msgstr "Velocitat d'1,25"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:384
msgid "Normal speed"
msgstr "Velocitat normal"
#: podcasts-gtk/resources/gtk/prefs.ui:42
#: podcasts-gtk/resources/gtk/prefs.ui:295
msgid "Preferences"
msgstr "Preferències"
#: podcasts-gtk/resources/gtk/prefs.ui:76
msgid "Appearance"
msgstr "Aparença"
#: podcasts-gtk/resources/gtk/prefs.ui:120
msgid "Dark Theme"
msgstr "Tema fosc"
#: podcasts-gtk/resources/gtk/prefs.ui:166
msgid "Delete played episodes"
msgstr "Suprimeix els episodis reproduïts"
#: podcasts-gtk/resources/gtk/prefs.ui:211
msgid "After"
msgstr "Després"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:7
msgid "_Mark All Episodes as Played"
msgstr "_Marca tots els episodis com a reproduïts"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:11
msgid "_Website"
msgstr "_Lloc web"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:15
msgid "_Unsubscribe"
msgstr "_Cancel·la la subscripció"
#: podcasts-gtk/resources/gtk/show_menu.ui:51
msgid "Open Website"
msgstr "Obre el lloc web"
#: podcasts-gtk/resources/gtk/show_menu.ui:64
msgid "Mark All as Played"
msgstr "Marca'ls tots com a reproduïts"
#: podcasts-gtk/resources/gtk/show_menu.ui:89
msgid "Unsubscribe"
msgstr "Cancel·la la subscripció"
#: podcasts-gtk/src/app.rs:332
msgid "Fetching new episodes"
msgstr "S'estan recuperant els episodis nous"
#: podcasts-gtk/src/headerbar.rs:98
msgid "You are already subscribed to this show"
msgstr "Ja us heu subscrit a aquest programa"
#: podcasts-gtk/src/headerbar.rs:106
msgid "Invalid URL"
msgstr "L'URL no és vàlid"
#: podcasts-gtk/src/prefs.rs:59
msgid "Seconds"
msgstr "Segons"
#: podcasts-gtk/src/prefs.rs:60
msgid "Minutes"
msgstr "Minuts"
#: podcasts-gtk/src/prefs.rs:61
msgid "Hours"
msgstr "Hores"
#: podcasts-gtk/src/prefs.rs:62
msgid "Days"
msgstr "Dies"
#: podcasts-gtk/src/prefs.rs:63
msgid "Weeks"
msgstr "Setmanes"
#: podcasts-gtk/src/stacks/content.rs:35
msgid "New"
msgstr "Nous"
#: podcasts-gtk/src/stacks/content.rs:36
msgid "Shows"
msgstr "Programes"
#: podcasts-gtk/src/utils.rs:357
msgid "Select the file from which to you want to import shows."
msgstr "Seleccioneu el fitxer des del qual voleu importar els programes."
#: podcasts-gtk/src/utils.rs:360
msgid "_Import"
msgstr "_Importa"
#: podcasts-gtk/src/utils.rs:369
msgid "OPML file"
msgstr "Fitxer OPML"
#: podcasts-gtk/src/utils.rs:386
msgid "Failed to parse the imported file"
msgstr "No s'ha pogut analitzar el fitxer importat"
#: podcasts-gtk/src/utils.rs:391
msgid "Selected file could not be accessed."
msgstr "No s'ha pogut accedir al fitxer seleccionat."
#: podcasts-gtk/src/widgets/aboutdialog.rs:26
msgid "Podcast Client for the GNOME Desktop."
msgstr "Client de podcasts per a l'escriptori GNOME."
#: podcasts-gtk/src/widgets/aboutdialog.rs:33
msgid "Learn more about GNOME Podcasts"
msgstr "Més informació sobre el GNOME Podcasts"
#: podcasts-gtk/src/widgets/aboutdialog.rs:38
msgid "translator-credits"
msgstr "Adolfo Jayme Barrientos <fitojb@ubuntu.com>, 2018"
#: podcasts-gtk/src/widgets/episode.rs:130
msgid "{} min"
msgstr "{} m"
#. sender.send(Action::ErrorNotification(format!("Player Error: {}", error)));
#: podcasts-gtk/src/widgets/player.rs:301
msgid "The media player was unable to execute an action."
msgstr "El reproductor multimèdia ha sigut incapaç d'executar cap acció."
#: podcasts-gtk/src/widgets/show_menu.rs:150
msgid "Marked all episodes as listened"
msgstr "S'han marcat tots els episodis com a escoltats"
#: podcasts-gtk/src/widgets/show_menu.rs:155
msgid "Unsubscribed from {}"
msgstr "Heu cancel·lat la subscripció a {}"
+405
View File
@@ -0,0 +1,405 @@
# Czech translation for podcasts.
# Copyright (C) 2018 podcasts's COPYRIGHT HOLDER
# This file is distributed under the same license as the podcasts package.
# Marek Černocký <marek@manet.cz>, 2018.
#
msgid ""
msgstr ""
"Project-Id-Version: podcasts master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/podcasts/issues\n"
"POT-Creation-Date: 2018-09-28 23:51+0000\n"
"PO-Revision-Date: 2018-09-29 21:28+0200\n"
"Last-Translator: Marek Černocký <marek@manet.cz>\n"
"Language-Team: čeština <gnome-cs-list@gnome.org>\n"
"Language: cs\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
"X-Generator: Gtranslator 2.91.7\n"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:15
msgid "Top position of the last open main window"
msgstr "Horní souřadnice naposledy otevřeného hlavního okna"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:19
msgid "Left position of the last open main window"
msgstr "Levá souřadnice naposledy otevřeného hlavního okna"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:23
msgid "Height of the last open main window"
msgstr "Výška naposledy otevřeného hlavního okna"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:27
msgid "Width of the last open main window"
msgstr "Šířka naposledy otevřeného hlavního okna"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:31
msgid "Maximized state of the last open main window"
msgstr "Stav maximalizace naposledy otevřeného hlavního okna"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:36
msgid "Enable or disable dark theme"
msgstr "Vypnutý nebo zapnutý tmavý motiv"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:41
msgid "Whether to periodically refresh content"
msgstr "Zda pravidelně aktualizovat obsah"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:46
msgid "How many periods of time to wait between automatic refreshes"
msgstr "Kolik časových jednotek čekat mezi automatickými aktualizacemi"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:50
msgid "What period of time to wait between automatic refreshes"
msgstr "Jaká je časová jednotka pro čekání mezi automatickými aktualizacemi"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:54
msgid "Whether to refresh content after startup"
msgstr "Zda aktualizovat obsah po spuštění"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:60
msgid "How many periods of time to wait between automatic cleanups"
msgstr "Kolik časových jednotek čekat mezi automatickými vymazáními"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:64
msgid "What period of time to wait between automatic cleanups"
msgstr "Jaká je časová jednotka pro čekání mezi automatickými vymazáními"
#. Weird magic I copy-pasted that sets the Application Name in the Shell.
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:3
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:4
#: podcasts-gtk/src/app.rs:91 podcasts-gtk/src/app.rs:414
#: podcasts-gtk/src/widgets/aboutdialog.rs:31
msgid "Podcasts"
msgstr "Podcasty"
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:4
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:10
msgid "Listen to your favorite podcasts, right from your desktop."
msgstr "Poslouchejte své oblíbené podcasty přímo z počítače"
#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:6
msgid "@icon@"
msgstr "@icon@"
#. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:13
msgid "Podcast;RSS;"
msgstr "podcast;RSS;pořad;"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:8
msgid "Podcast app for GNOME"
msgstr "Podcastová aplikace pro GNOME"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:51
msgid "Jordan Petridis"
msgstr "Jordan Petridis"
#: podcasts-gtk/resources/gtk/empty_view.ui:46
msgid "This show does not have episodes yet"
msgstr "Pořad zatím nemá žádné epizody"
#: podcasts-gtk/resources/gtk/empty_view.ui:62
msgid "If you think this is an error, please consider writing a bug report."
msgstr "Pokud si myslíte, že se jedná o chybu, zvažte její nahlášení."
#: podcasts-gtk/resources/gtk/empty_view.ui:106
msgid "Get some shows"
msgstr "Doplňte si pořady"
#: podcasts-gtk/resources/gtk/empty_view.ui:143
msgid "Add new shows via feed URL"
msgstr "Přidat nové pořady pomocí URL kanálu"
#: podcasts-gtk/resources/gtk/empty_view.ui:172
msgid "Import shows from another device"
msgstr "Naimportovat pořady z jiného zařízení"
#: podcasts-gtk/resources/gtk/episode_widget.ui:180
msgid "Calculating episode size…"
msgstr "Počítá se velikost epizody…"
#: podcasts-gtk/resources/gtk/episode_widget.ui:220
msgid "Play this episode"
msgstr "Přehrát tuto epizodu"
#: podcasts-gtk/resources/gtk/episode_widget.ui:241
msgid "Cancel the download proccess"
msgstr "Zrušit probíhající stahování"
#: podcasts-gtk/resources/gtk/episode_widget.ui:264
msgid "Download this episode"
msgstr "Stáhnout tuto epizodu"
#: podcasts-gtk/resources/gtk/hamburger.ui:7
msgid "_Check for New Episodes"
msgstr "Vy_hledat nové epizody"
#: podcasts-gtk/resources/gtk/hamburger.ui:12
msgid "_Import Shows"
msgstr "Na_importovat pořady"
#: podcasts-gtk/resources/gtk/hamburger.ui:22
msgid "_Preferences"
msgstr "_Předvolby"
#: podcasts-gtk/resources/gtk/hamburger.ui:27
msgid "_Keyboard Shortcuts"
msgstr "_Klávesové zkratky"
#: podcasts-gtk/resources/gtk/hamburger.ui:35
msgid "_About Podcasts"
msgstr "O _aplikaci Podcasty"
#: podcasts-gtk/resources/gtk/headerbar.ui:35
#: podcasts-gtk/resources/gtk/headerbar.ui:189
msgid "Add a new feed"
msgstr "Přidat nový kanál"
#: podcasts-gtk/resources/gtk/headerbar.ui:53
msgid "Enter feed address to add"
msgstr "Zadejte adresu kanálu, který chcete přidat"
#: podcasts-gtk/resources/gtk/headerbar.ui:89
msgid "Add"
msgstr "Přidat"
#: podcasts-gtk/resources/gtk/headerbar.ui:133
msgid "You are already subscribed to that feed!"
msgstr "Z tohoto kanálu již odebíráte!"
#: podcasts-gtk/resources/gtk/headerbar.ui:169
msgid "Show Title"
msgstr "Název pořadu"
#: podcasts-gtk/resources/gtk/headerbar.ui:210
msgid "Back"
msgstr "Zpět"
#: podcasts-gtk/resources/gtk/help-overlay.ui:12
msgid "General"
msgstr "Obecné"
#: podcasts-gtk/resources/gtk/help-overlay.ui:18
msgctxt "shortcut window"
msgid "Check for new episodes"
msgstr "Vyhledat nové epizody"
#: podcasts-gtk/resources/gtk/help-overlay.ui:25
msgctxt "shortcut window"
msgid "Preferences"
msgstr "Předvolby"
#: podcasts-gtk/resources/gtk/help-overlay.ui:32
msgctxt "shortcut window"
msgid "Quit the application"
msgstr "Ukončit aplikaci"
#: podcasts-gtk/resources/gtk/home_view.ui:56
msgid "Today"
msgstr "Dnes"
#: podcasts-gtk/resources/gtk/home_view.ui:112
msgid "Yesterday"
msgstr "Včera"
#: podcasts-gtk/resources/gtk/home_view.ui:168
msgid "This Week"
msgstr "Tento týden"
#: podcasts-gtk/resources/gtk/home_view.ui:224
msgid "This Month"
msgstr "Tento měsíc"
#: podcasts-gtk/resources/gtk/home_view.ui:281
msgid "Older"
msgstr "Starší"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:101
msgid "An in-app action notification"
msgstr "Oznámení o činnosti v aplikaci"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:112
msgid "Undo"
msgstr "Zpět"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:72
msgid "Rewind 10 seconds"
msgstr "Převinout zpět o 10 sekund"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:87
msgid "Play"
msgstr "Přehrát"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:103
msgid "Pause"
msgstr "Pozastavit"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:119
msgid "Fast forward 10 seconds"
msgstr "Posunou rychle vpřed o 10 sekund"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:285
msgid "Change the playback speed"
msgstr "Změnit rychlost přehrávání"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:300
#: podcasts-gtk/resources/gtk/player_toolbar.ui:380
msgid "1.00×"
msgstr "1,00×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:344
msgid "1.50×"
msgstr "1,50×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:348
msgid "1.5 speed rate"
msgstr "1,5 násobek rychlosti"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:362
msgid "1.25×"
msgstr "1,25×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:366
msgid "1.25 speed rate"
msgstr "1,25 násobek rychlosti"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:384
msgid "Normal speed"
msgstr "Normální rychlost"
#: podcasts-gtk/resources/gtk/prefs.ui:42
#: podcasts-gtk/resources/gtk/prefs.ui:295
msgid "Preferences"
msgstr "Předvolby"
#: podcasts-gtk/resources/gtk/prefs.ui:76
msgid "Appearance"
msgstr "Vzhled"
#: podcasts-gtk/resources/gtk/prefs.ui:120
msgid "Dark Theme"
msgstr "Tmavý motiv"
#: podcasts-gtk/resources/gtk/prefs.ui:166
msgid "Delete played episodes"
msgstr "Smazat přehrané epizody"
#: podcasts-gtk/resources/gtk/prefs.ui:211
msgid "After"
msgstr "Po uplynutí"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:7
msgid "_Mark All Episodes as Played"
msgstr "Oz_načit všechny epizody jako přehrané"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:11
msgid "_Website"
msgstr "_Webové stránky"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:15
msgid "_Unsubscribe"
msgstr "O_dhlásit odběr"
#: podcasts-gtk/resources/gtk/show_menu.ui:51
msgid "Open Website"
msgstr "Otevřít webové stránky"
#: podcasts-gtk/resources/gtk/show_menu.ui:64
msgid "Mark All as Played"
msgstr "Označit vše jako přehrané"
#: podcasts-gtk/resources/gtk/show_menu.ui:89
msgid "Unsubscribe"
msgstr "Odhlásit odběr"
#: podcasts-gtk/src/app.rs:332
msgid "Fetching new episodes"
msgstr "Stahují se nové epizody"
#: podcasts-gtk/src/headerbar.rs:98
msgid "You are already subscribed to this show"
msgstr "Již jste přihlášeni k odběru tohoto pořadu"
#: podcasts-gtk/src/headerbar.rs:106
msgid "Invalid URL"
msgstr "Neplatná adresa URL"
#: podcasts-gtk/src/prefs.rs:59
msgid "Seconds"
msgstr "sekund"
#: podcasts-gtk/src/prefs.rs:60
msgid "Minutes"
msgstr "minut"
#: podcasts-gtk/src/prefs.rs:61
msgid "Hours"
msgstr "hodin"
#: podcasts-gtk/src/prefs.rs:62
msgid "Days"
msgstr "dnů"
#: podcasts-gtk/src/prefs.rs:63
msgid "Weeks"
msgstr "týdnů"
#: podcasts-gtk/src/stacks/content.rs:35
msgid "New"
msgstr "Nový"
#: podcasts-gtk/src/stacks/content.rs:36
msgid "Shows"
msgstr "Pořady"
#: podcasts-gtk/src/utils.rs:357
msgid "Select the file from which to you want to import shows."
msgstr "Výběr soubor pro import pořadu"
#: podcasts-gtk/src/utils.rs:360
msgid "_Import"
msgstr "Na_importovat"
#: podcasts-gtk/src/utils.rs:369
msgid "OPML file"
msgstr "Soubor OPML"
#: podcasts-gtk/src/utils.rs:386
msgid "Failed to parse the imported file"
msgstr "Selhalo zpracování naimportovaného souboru"
#: podcasts-gtk/src/utils.rs:391
msgid "Selected file could not be accessed."
msgstr "K vybranému souboru není přístup"
#: podcasts-gtk/src/widgets/aboutdialog.rs:26
msgid "Podcast Client for the GNOME Desktop."
msgstr "Podcastový klient pro uživatelské prostředí GNOME"
#: podcasts-gtk/src/widgets/aboutdialog.rs:33
msgid "Learn more about GNOME Podcasts"
msgstr "Dozvědět se více o Podcastech GNOME"
#: podcasts-gtk/src/widgets/aboutdialog.rs:38
msgid "translator-credits"
msgstr "Marek Černocký <marek@manet.cz>"
#: podcasts-gtk/src/widgets/episode.rs:130
msgid "{} min"
msgstr "{} min"
#. sender.send(Action::ErrorNotification(format!("Player Error: {}", error)));
#: podcasts-gtk/src/widgets/player.rs:301
msgid "The media player was unable to execute an action."
msgstr "Multimediální přehrávač nebyl schopen provést žádnou činnost."
#: podcasts-gtk/src/widgets/show_menu.rs:150
msgid "Marked all episodes as listened"
msgstr "Všechny epizody byly označené jak přehrané"
#: podcasts-gtk/src/widgets/show_menu.rs:155
msgid "Unsubscribed from {}"
msgstr "Byl odhlášen odběr z {}"
+417
View File
@@ -0,0 +1,417 @@
# German translation for podcasts.
# Copyright (C) 2018 podcasts's COPYRIGHT HOLDER
# This file is distributed under the same license as the podcasts package.
# Mario Blättermann <mario.blaettermann@gmail.com>, 2018.
# Tim Sabsch <tim@sabsch.com>, 2018.
#
msgid ""
msgstr ""
"Project-Id-Version: podcasts master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/podcasts/issues\n"
"POT-Creation-Date: 2018-09-28 23:51+0000\n"
"PO-Revision-Date: 2018-10-04 21:57-0400\n"
"Last-Translator: Florian Heiser <gnu.l10n.de@gmail.com>\n"
"Language-Team: Deutsch <gnome-de@gnome.org>\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 2.1.1\n"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:15
msgid "Top position of the last open main window"
msgstr "Obere Position des zuletzt geöffneten Hauptfensters"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:19
msgid "Left position of the last open main window"
msgstr "Linke Position des zuletzt geöffneten Hauptfensters"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:23
msgid "Height of the last open main window"
msgstr "Höhe des zuletzt geöffneten Hauptfensters"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:27
msgid "Width of the last open main window"
msgstr "Breite des zuletzt geöffneten Hauptfensters"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:31
msgid "Maximized state of the last open main window"
msgstr "Maximierungsstatus des zuletzt geöffneten Hauptfensters"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:36
msgid "Enable or disable dark theme"
msgstr "Dunkles Thema aktivieren oder deaktivieren"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:41
msgid "Whether to periodically refresh content"
msgstr ""
"Legt fest, ob Inhalte in regelmäßigen Abständen aktualisiert werden sollen"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:46
msgid "How many periods of time to wait between automatic refreshes"
msgstr "Zeitintervall zwischen automatischen Aktualisierungsvorgängen"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:50
msgid "What period of time to wait between automatic refreshes"
msgstr "Zeitintervall zwischen automatischen Aktualisierungsvorgängen"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:54
msgid "Whether to refresh content after startup"
msgstr "Legt fest, ob Inhalte beim Programmstart aktualisiert werden sollen"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:60
msgid "How many periods of time to wait between automatic cleanups"
msgstr "Zeitintervall zwischen automatischen Löschvorgängen"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:64
msgid "What period of time to wait between automatic cleanups"
msgstr "Zeitintervall zwischen automatischen Löschvorgängen"
#. Weird magic I copy-pasted that sets the Application Name in the Shell.
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:3
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:4
#: podcasts-gtk/src/app.rs:91 podcasts-gtk/src/app.rs:414
#: podcasts-gtk/src/widgets/aboutdialog.rs:31
msgid "Podcasts"
msgstr "Podcasts"
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:4
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:10
msgid "Listen to your favorite podcasts, right from your desktop."
msgstr "Hören Sie Ihre Lieblings-Podcasts direkt in Ihrer Arbeitsumgebung."
#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:6
msgid "@icon@"
msgstr "@icon@"
#. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:13
msgid "Podcast;RSS;"
msgstr "Podcast;RSS;"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:8
msgid "Podcast app for GNOME"
msgstr "Podcast-Anwendung für GNOME"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:51
msgid "Jordan Petridis"
msgstr "Jordan Petridis"
#: podcasts-gtk/resources/gtk/empty_view.ui:46
msgid "This show does not have episodes yet"
msgstr "Dieser Podcast hat noch keine Folgen"
#: podcasts-gtk/resources/gtk/empty_view.ui:62
msgid "If you think this is an error, please consider writing a bug report."
msgstr ""
"Falls Sie meinen, dass es sich um einen Fehler handeln könnte, schreiben Sie "
"bitte einen Fehlerbericht."
#: podcasts-gtk/resources/gtk/empty_view.ui:106
msgid "Get some shows"
msgstr "Podcasts abonnieren"
#: podcasts-gtk/resources/gtk/empty_view.ui:143
msgid "Add new shows via feed URL"
msgstr "Neue Podcasts mittels Quellen-Adresse hinzufügen"
#: podcasts-gtk/resources/gtk/empty_view.ui:172
msgid "Import shows from another device"
msgstr "Podcasts von einem anderen Gerät importieren"
#: podcasts-gtk/resources/gtk/episode_widget.ui:180
msgid "Calculating episode size…"
msgstr "Größe der Folge wird ermittelt …"
#: podcasts-gtk/resources/gtk/episode_widget.ui:220
msgid "Play this episode"
msgstr "Diese Folge abspielen"
#: podcasts-gtk/resources/gtk/episode_widget.ui:241
msgid "Cancel the download proccess"
msgstr "Herunterladen abbrechen"
#: podcasts-gtk/resources/gtk/episode_widget.ui:264
msgid "Download this episode"
msgstr "Diese Folge herunterladen"
#: podcasts-gtk/resources/gtk/hamburger.ui:7
msgid "_Check for New Episodes"
msgstr "Auf neue Folgen _prüfen"
#: podcasts-gtk/resources/gtk/hamburger.ui:12
msgid "_Import Shows"
msgstr "Podcasts _importieren"
#: podcasts-gtk/resources/gtk/hamburger.ui:22
msgid "_Preferences"
msgstr "_Einstellungen"
#: podcasts-gtk/resources/gtk/hamburger.ui:27
msgid "_Keyboard Shortcuts"
msgstr "Tasten_kürzel"
#: podcasts-gtk/resources/gtk/hamburger.ui:35
msgid "_About Podcasts"
msgstr "Info zu _Podcasts"
#: podcasts-gtk/resources/gtk/headerbar.ui:35
#: podcasts-gtk/resources/gtk/headerbar.ui:189
msgid "Add a new feed"
msgstr "Neue Quelle hinzufügen"
#: podcasts-gtk/resources/gtk/headerbar.ui:53
msgid "Enter feed address to add"
msgstr "Hinzuzufügende Quellen-Adresse eingeben"
#: podcasts-gtk/resources/gtk/headerbar.ui:89
msgid "Add"
msgstr "Hinzufügen"
#: podcasts-gtk/resources/gtk/headerbar.ui:133
msgid "You are already subscribed to that feed!"
msgstr "Sie haben diese Quelle bereits abonniert!"
#: podcasts-gtk/resources/gtk/headerbar.ui:169
msgid "Show Title"
msgstr "Titel anzeigen"
#: podcasts-gtk/resources/gtk/headerbar.ui:210
msgid "Back"
msgstr "Zurück"
#: podcasts-gtk/resources/gtk/help-overlay.ui:12
msgid "General"
msgstr "Allgemein"
#: podcasts-gtk/resources/gtk/help-overlay.ui:18
msgctxt "shortcut window"
msgid "Check for new episodes"
msgstr "Auf neue Folgen prüfen"
#: podcasts-gtk/resources/gtk/help-overlay.ui:25
msgctxt "shortcut window"
msgid "Preferences"
msgstr "Einstellungen"
#: podcasts-gtk/resources/gtk/help-overlay.ui:32
msgctxt "shortcut window"
msgid "Quit the application"
msgstr "Die Anwendung beenden"
#: podcasts-gtk/resources/gtk/home_view.ui:56
msgid "Today"
msgstr "Heute"
#: podcasts-gtk/resources/gtk/home_view.ui:112
msgid "Yesterday"
msgstr "Gestern"
#: podcasts-gtk/resources/gtk/home_view.ui:168
msgid "This Week"
msgstr "Diese Woche"
#: podcasts-gtk/resources/gtk/home_view.ui:224
msgid "This Month"
msgstr "Diesen Monat"
#: podcasts-gtk/resources/gtk/home_view.ui:281
msgid "Older"
msgstr "Älter"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:101
msgid "An in-app action notification"
msgstr "Eine anwendungsinterne Aktionsmeldung"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:112
msgid "Undo"
msgstr "Rückgängig"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:72
msgid "Rewind 10 seconds"
msgstr "10 Sekunden zurückspulen"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:87
msgid "Play"
msgstr "Wiedergeben"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:103
msgid "Pause"
msgstr "Pause"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:119
msgid "Fast forward 10 seconds"
msgstr "10 Sekunden vorspulen"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:285
msgid "Change the playback speed"
msgstr "Die Wiedergabegeschwindigkeit ändern"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:300
#: podcasts-gtk/resources/gtk/player_toolbar.ui:380
msgid "1.00×"
msgstr "1,00×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:344
msgid "1.50×"
msgstr "1,50×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:348
msgid "1.5 speed rate"
msgstr "1,5-fache Geschwindigkeit"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:362
msgid "1.25×"
msgstr "1,25×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:366
msgid "1.25 speed rate"
msgstr "1,25-fache Geschwindigkeit"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:384
msgid "Normal speed"
msgstr "Normale Geschwidigkeit"
#: podcasts-gtk/resources/gtk/prefs.ui:42
#: podcasts-gtk/resources/gtk/prefs.ui:295
msgid "Preferences"
msgstr "Einstellungen"
#: podcasts-gtk/resources/gtk/prefs.ui:76
msgid "Appearance"
msgstr "Erscheinungsbild"
#: podcasts-gtk/resources/gtk/prefs.ui:120
msgid "Dark Theme"
msgstr "Dunkles Thema"
#: podcasts-gtk/resources/gtk/prefs.ui:166
msgid "Delete played episodes"
msgstr "Angehörte Folgen löschen"
#: podcasts-gtk/resources/gtk/prefs.ui:211
msgid "After"
msgstr "Nach"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:7
msgid "_Mark All Episodes as Played"
msgstr "Alle Folgen als angehört _markieren"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:11
msgid "_Website"
msgstr "_Internetseite"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:15
msgid "_Unsubscribe"
msgstr "Ab_bestellen"
#: podcasts-gtk/resources/gtk/show_menu.ui:51
msgid "Open Website"
msgstr "Internetseite öffnen"
#: podcasts-gtk/resources/gtk/show_menu.ui:64
msgid "Mark All as Played"
msgstr "Alle als angehört markieren"
#: podcasts-gtk/resources/gtk/show_menu.ui:89
msgid "Unsubscribe"
msgstr "Abbestellen"
#: podcasts-gtk/src/app.rs:332
msgid "Fetching new episodes"
msgstr "Neue Folgen werden abgerufen"
#: podcasts-gtk/src/headerbar.rs:98
msgid "You are already subscribed to this show"
msgstr "Sie haben diese Show nun abonniert"
#: podcasts-gtk/src/headerbar.rs:106
msgid "Invalid URL"
msgstr "Ungültige Adresse"
#: podcasts-gtk/src/prefs.rs:59
msgid "Seconds"
msgstr "Sekunden"
#: podcasts-gtk/src/prefs.rs:60
msgid "Minutes"
msgstr "Minuten"
#: podcasts-gtk/src/prefs.rs:61
msgid "Hours"
msgstr "Stunden"
#: podcasts-gtk/src/prefs.rs:62
msgid "Days"
msgstr "Tage"
#: podcasts-gtk/src/prefs.rs:63
msgid "Weeks"
msgstr "Wochen"
#: podcasts-gtk/src/stacks/content.rs:35
msgid "New"
msgstr "Neu"
#: podcasts-gtk/src/stacks/content.rs:36
msgid "Shows"
msgstr "Podcasts"
#: podcasts-gtk/src/utils.rs:357
msgid "Select the file from which to you want to import shows."
msgstr "Wählen Sie die Datei aus, aus der Sie Shows importieren wollen."
#: podcasts-gtk/src/utils.rs:360
msgid "_Import"
msgstr "_Importieren"
#: podcasts-gtk/src/utils.rs:369
msgid "OPML file"
msgstr "OPML-Datei"
#: podcasts-gtk/src/utils.rs:386
msgid "Failed to parse the imported file"
msgstr "Die importierte Datei konnte nicht ausgewertet werden"
#: podcasts-gtk/src/utils.rs:391
msgid "Selected file could not be accessed."
msgstr "Auf die gewählte Datei konnte nicht zugegriffen werden."
#: podcasts-gtk/src/widgets/aboutdialog.rs:26
msgid "Podcast Client for the GNOME Desktop."
msgstr "Podcast-Client für die GNOME-Arbeitsumgebung."
#: podcasts-gtk/src/widgets/aboutdialog.rs:33
msgid "Learn more about GNOME Podcasts"
msgstr "Erfahren Sie mehr über GNOME Podcasts"
#: podcasts-gtk/src/widgets/aboutdialog.rs:38
msgid "translator-credits"
msgstr ""
"Mario Blättermann <mario.blaettermann@gmail.com>\n"
"Tim Sabsch <tim@sabsch.com>"
#: podcasts-gtk/src/widgets/episode.rs:130
msgid "{} min"
msgstr "{} min"
#. sender.send(Action::ErrorNotification(format!("Player Error: {}", error)));
#: podcasts-gtk/src/widgets/player.rs:301
msgid "The media player was unable to execute an action."
msgstr "Die Medienwiedergabe konnte eine Aktion nicht ausführen."
#: podcasts-gtk/src/widgets/show_menu.rs:150
msgid "Marked all episodes as listened"
msgstr "Alle Folgen als angehört markieren"
#: podcasts-gtk/src/widgets/show_menu.rs:155
msgid "Unsubscribed from {}"
msgstr "Abonnement von {} gekündigt"
#~ msgid "_About"
#~ msgstr "I_nfo"
#~ msgid "Cancel"
#~ msgstr "Abbrechen"
+442
View File
@@ -0,0 +1,442 @@
# Spanish translations for gnome-podcasts package.
# Copyright (C) 2018 THE gnome-podcasts'S COPYRIGHT HOLDER
# This file is distributed under the same license as the gnome-podcasts package.
# Automatically generated, 2018.
# Daniel Garcia Moreno <danigm@wadobo.com>, 2018.
# Daniel Mustieles <daniel.mustieles@gmail.com>, 2018.
# Rodrigo <rodhos92@gmail.com>, 2018.
#
msgid ""
msgstr ""
"Project-Id-Version: gnome-podcasts\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/podcasts/issues\n"
"POT-Creation-Date: 2018-09-08 17:18+0000\n"
"PO-Revision-Date: 2018-09-21 19:03+0200\n"
"Last-Translator: Rodrigo <rodhos92@gmail.com>\n"
"Language-Team: Español <gnome-es-list@gnome.org>\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Gtranslator 2.91.7\n"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:15
msgid "Top position of the last open main window"
msgstr "Posición superior de la última ventana abierta"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:19
msgid "Left position of the last open main window"
msgstr "Posición izquierda de la última ventana abierta"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:23
msgid "Height of the last open main window"
msgstr "Altura de la última ventana abierta"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:27
msgid "Width of the last open main window"
msgstr "Anchura de la última ventana abierta"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:31
msgid "Maximized state of the last open main window"
msgstr "Estado de maximizado de la última ventana abierta"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:36
msgid "Enable or disable dark theme"
msgstr "Activar o desactivar el tema oscuro"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:41
msgid "Whether to periodically refresh content"
msgstr "Indica si se debe actualizar el contenido periódicamente"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:46
msgid "How many periods of time to wait between automatic refreshes"
msgstr ""
"Cuántos periodos de tiempo hay que esperar entre actualizaciones automáticas"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:50
msgid "What period of time to wait between automatic refreshes"
msgstr ""
"Qué periodo de tiempo hay que esperar entre actualizaciones automáticas"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:54
msgid "Whether to refresh content after startup"
msgstr "Indica si se debe actualizar el contenido al iniciar"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:60
msgid "How many periods of time to wait between automatic cleanups"
msgstr "Cuántos periodos de tiempo hay que esperar entre limpiezas automáticas"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:64
msgid "What period of time to wait between automatic cleanups"
msgstr "Cuántos periodos de tiempo hay que esperar entre limpiezas automáticas"
#. Weird magic I copy-pasted that sets the Application Name in the Shell.
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:3
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:4
#: podcasts-gtk/src/app.rs:91 podcasts-gtk/src/app.rs:414
#: podcasts-gtk/src/widgets/aboutdialog.rs:31
msgid "Podcasts"
msgstr "Podcasts"
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:4
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:10
msgid "Listen to your favorite podcasts, right from your desktop."
msgstr "Escuche sus podcasts favoritos, directamente desde su escritorio."
#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:6
msgid "@icon@"
msgstr "@icon@"
#. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:13
msgid "Podcast;RSS;"
msgstr "Podcast;RSS;"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:8
msgid "Podcast app for GNOME"
msgstr "Aplicación de Podcast para GNOME"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:51
msgid "Jordan Petridis"
msgstr "Jordan Petridis"
#: podcasts-gtk/resources/gtk/empty_view.ui:46
msgid "This show does not have episodes yet"
msgstr "Este programa no tiene episodios todavía"
#: podcasts-gtk/resources/gtk/empty_view.ui:62
msgid "If you think this is an error, please consider writing a bug report."
msgstr "Si cree que esto es un error considere abrir un informe de error."
#: podcasts-gtk/resources/gtk/empty_view.ui:106
msgid "Get some shows"
msgstr "Consiga algún programa"
#: podcasts-gtk/resources/gtk/empty_view.ui:143
msgid "Add new shows via feed URL"
msgstr "Añadir un programa nuevo a través de canal URL"
#: podcasts-gtk/resources/gtk/empty_view.ui:172
msgid "Import shows from another device"
msgstr "Importar programas de otro dispositivo"
#: podcasts-gtk/resources/gtk/episode_widget.ui:180
msgid "Calculating episode size…"
msgstr "Calculando tamaño del episodio…"
#: podcasts-gtk/resources/gtk/episode_widget.ui:220
msgid "Play this episode"
msgstr "Reproducir este episodio"
#: podcasts-gtk/resources/gtk/episode_widget.ui:241
msgid "Cancel the download proccess"
msgstr "Cancelar el proceso de descarga"
#: podcasts-gtk/resources/gtk/episode_widget.ui:264
msgid "Download this episode"
msgstr "Descargar este episodio"
#: podcasts-gtk/resources/gtk/hamburger.ui:7
msgid "_Check for New Episodes"
msgstr "Comprobar si hay nuevos episodios"
#: podcasts-gtk/resources/gtk/hamburger.ui:12
msgid "_Import Shows"
msgstr "_Importar programas"
#: podcasts-gtk/resources/gtk/hamburger.ui:22
msgid "_Preferences"
msgstr "_Preferencias"
#: podcasts-gtk/resources/gtk/hamburger.ui:29
msgid "_Keyboard Shortcuts"
msgstr "Atajos de _teclado"
#: podcasts-gtk/resources/gtk/hamburger.ui:37
msgid "_About"
msgstr "_Acerca de"
#: podcasts-gtk/resources/gtk/headerbar.ui:35
#: podcasts-gtk/resources/gtk/headerbar.ui:189
msgid "Add a new feed"
msgstr "Añadir un canal nuevo"
#: podcasts-gtk/resources/gtk/headerbar.ui:53
msgid "Enter feed address to add"
msgstr "Introduzca la dirección del canal que añadir"
#: podcasts-gtk/resources/gtk/headerbar.ui:89
msgid "Add"
msgstr "Añadir"
#: podcasts-gtk/resources/gtk/headerbar.ui:133
msgid "You are already subscribed to that feed!"
msgstr "¡Ya está suscrito a este canal!"
#: podcasts-gtk/resources/gtk/headerbar.ui:169
msgid "Show Title"
msgstr "Título del programa"
#: podcasts-gtk/resources/gtk/headerbar.ui:210
msgid "Back"
msgstr "Atrás"
#: podcasts-gtk/resources/gtk/help-overlay.ui:12
msgid "General"
msgstr "General"
#: podcasts-gtk/resources/gtk/help-overlay.ui:18
msgctxt "shortcut window"
msgid "Check for new episodes"
msgstr "Comprobar si hay episodios nuevos"
#: podcasts-gtk/resources/gtk/help-overlay.ui:25
msgctxt "shortcut window"
msgid "Preferences"
msgstr "Preferencias"
#: podcasts-gtk/resources/gtk/help-overlay.ui:32
msgctxt "shortcut window"
msgid "Quit the application"
msgstr "Salir de la aplicación"
#: podcasts-gtk/resources/gtk/home_view.ui:56
msgid "Today"
msgstr "Hoy"
#: podcasts-gtk/resources/gtk/home_view.ui:112
msgid "Yesterday"
msgstr "Ayer"
#: podcasts-gtk/resources/gtk/home_view.ui:168
msgid "This Week"
msgstr "Esta semana"
#: podcasts-gtk/resources/gtk/home_view.ui:224
msgid "This Month"
msgstr "Este mes"
#: podcasts-gtk/resources/gtk/home_view.ui:281
msgid "Older"
msgstr "Antiguo"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:101
msgid "An in-app action notification"
msgstr "Una notificación de acción dentro de la aplicación"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:112
msgid "Undo"
msgstr "Deshacer"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:72
msgid "Rewind 10 seconds"
msgstr "Retroceder 10 segundos"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:87
msgid "Play"
msgstr "Reproducir"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:103
msgid "Pause"
msgstr "Pausa"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:119
msgid "Fast forward 10 seconds"
msgstr "Avanzar 10 segundos"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:285
msgid "Change the playback speed"
msgstr "Cambiar velocidad de reproducción"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:300
#: podcasts-gtk/resources/gtk/player_toolbar.ui:380
msgid "1.00×"
msgstr "1.00×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:344
msgid "1.50×"
msgstr "1.50×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:348
msgid "1.5 speed rate"
msgstr "velocidad 1.5"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:362
msgid "1.25×"
msgstr "1.25×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:366
msgid "1.25 speed rate"
msgstr "velocidad 1.25"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:384
msgid "Normal speed"
msgstr "Velocidad normal"
#: podcasts-gtk/resources/gtk/prefs.ui:42
#: podcasts-gtk/resources/gtk/prefs.ui:295
msgid "Preferences"
msgstr "Preferencias"
#: podcasts-gtk/resources/gtk/prefs.ui:76
msgid "Appearance"
msgstr "Aspecto"
#: podcasts-gtk/resources/gtk/prefs.ui:120
msgid "Dark Theme"
msgstr "Tema oscuro"
#: podcasts-gtk/resources/gtk/prefs.ui:166
msgid "Delete played episodes"
msgstr "Eliminar episodios reproducidos"
#: podcasts-gtk/resources/gtk/prefs.ui:211
msgid "After"
msgstr "Después"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:7
msgid "_Mark All Episodes as Played"
msgstr "_Marcar todos los episodios como reproducidos"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:11
msgid "_Website"
msgstr "Página _Web"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:15
msgid "_Unsubscribe"
msgstr "_Cancelar suscripción"
#: podcasts-gtk/resources/gtk/show_menu.ui:51
msgid "Open Website"
msgstr "Abrir Web"
#: podcasts-gtk/resources/gtk/show_menu.ui:64
msgid "Mark All as Played"
msgstr "Marcar todo como reproducido"
#: podcasts-gtk/resources/gtk/show_menu.ui:89
msgid "Unsubscribe"
msgstr "Cancelar suscripción"
#: podcasts-gtk/src/app.rs:332
msgid "Fetching new episodes"
msgstr "Obteniendo nuevos episodios"
#: podcasts-gtk/src/headerbar.rs:98
msgid "You are already subscribed to this show"
msgstr "Ya está suscrito a este programa"
#: podcasts-gtk/src/headerbar.rs:106
msgid "Invalid URL"
msgstr "URL no válido"
#: podcasts-gtk/src/prefs.rs:59
msgid "Seconds"
msgstr "Segundos"
#: podcasts-gtk/src/prefs.rs:60
msgid "Minutes"
msgstr "Minutos"
#: podcasts-gtk/src/prefs.rs:61
msgid "Hours"
msgstr "Horas"
#: podcasts-gtk/src/prefs.rs:62
msgid "Days"
msgstr "Días"
#: podcasts-gtk/src/prefs.rs:63
msgid "Weeks"
msgstr "Semanas"
#: podcasts-gtk/src/stacks/content.rs:35
msgid "New"
msgstr "Nuevo"
#: podcasts-gtk/src/stacks/content.rs:36
msgid "Shows"
msgstr "Programas"
#: podcasts-gtk/src/utils.rs:357
msgid "Select the file from which to you want to import shows."
msgstr "Selecciona el fichero desde el que quiere importar programas."
#: podcasts-gtk/src/utils.rs:360
msgid "_Import"
msgstr "_Importar"
#: podcasts-gtk/src/utils.rs:369
msgid "OPML file"
msgstr "fichero OPML"
#: podcasts-gtk/src/utils.rs:386
msgid "Failed to parse the imported file"
msgstr "Fallo al analizar el fichero importado"
#: podcasts-gtk/src/utils.rs:391
msgid "Selected file could not be accessed."
msgstr "No se ha podido acceder al archivo seleccionado."
#: podcasts-gtk/src/widgets/aboutdialog.rs:26
msgid "Podcast Client for the GNOME Desktop."
msgstr "Aplicación de Podcast para el escritorio GNOME."
#: podcasts-gtk/src/widgets/aboutdialog.rs:33
msgid "Learn more about GNOME Podcasts"
msgstr "Aprenda más sobre GNOME Podcasts"
#: podcasts-gtk/src/widgets/aboutdialog.rs:38
msgid "translator-credits"
msgstr ""
"Daniel Mustieles <daniel.musiteles@gmail.com>, 2018\n"
"Daniel García Moreno <danigm@wadobo.com>"
#: podcasts-gtk/src/widgets/episode.rs:130
msgid "{} min"
msgstr "{} min"
#. sender.send(Action::ErrorNotification(format!("Player Error: {}", error)));
#: podcasts-gtk/src/widgets/player.rs:301
msgid "The media player was unable to execute an action."
msgstr "El reproductor no ha podido reproducir una acción."
#: podcasts-gtk/src/widgets/show_menu.rs:150
msgid "Marked all episodes as listened"
msgstr "Marcar todos los episodios como escuchados"
#: podcasts-gtk/src/widgets/show_menu.rs:155
msgid "Unsubscribed from {}"
msgstr "Suscripción cancelada para {}"
#~ msgid "Cancel"
#~ msgstr "Cancelar"
#~ msgid "org.gnome.Podcasts"
#~ msgstr "org.gnome.Podcasts"
#~ msgid "Mark all episodes as listened"
#~ msgstr "Marcar todos los episodios como escuchados"
#~ msgid "Episode Title"
#~ msgstr "Título del episodio"
#~ msgid "3 Jan"
#~ msgstr "3 Jan"
#~ msgid "·"
#~ msgstr "·"
#~ msgid "42 min"
#~ msgstr "42 min"
#~ msgid "0 MB"
#~ msgstr "0 MB"
#~ msgid "/"
#~ msgstr "/"
#~ msgid "© 2017, 2018 Jordan Petridis"
#~ msgstr "© 2017, 2018 Jordan Petridis"
+411
View File
@@ -0,0 +1,411 @@
# Finnish translation for podcasts.
# Copyright (C) 2018 podcasts's COPYRIGHT HOLDER
# This file is distributed under the same license as the podcasts package.
# Jiri Grönroos <jiri.gronroos@iki.fi>, 2018.
#
msgid ""
msgstr ""
"Project-Id-Version: podcasts master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/podcasts/issues\n"
"POT-Creation-Date: 2018-08-28 20:54+0000\n"
"PO-Revision-Date: 2018-09-01 19:58+0300\n"
"Last-Translator: Jiri Grönroos <jiri.gronroos+l10n@iki.fi>\n"
"Language-Team: Finnish <lokalisointi-lista@googlegroups.com>\n"
"Language: fi\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 2.0.6\n"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:15
msgid "Top position of the last open main window"
msgstr ""
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:19
msgid "Left position of the last open main window"
msgstr ""
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:23
msgid "Height of the last open main window"
msgstr ""
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:27
msgid "Width of the last open main window"
msgstr ""
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:31
msgid "Maximized state of the last open main window"
msgstr ""
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:36
msgid "Enable or disable dark theme"
msgstr "Ota käyttöön tai poista käytöstä tumma teema"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:41
msgid "Whether to periodically refresh content"
msgstr "Päivitetäänkö sisältö säännöllisesti"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:46
msgid "How many periods of time to wait between automatic refreshes"
msgstr ""
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:50
msgid "What period of time to wait between automatic refreshes"
msgstr ""
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:54
msgid "Whether to refresh content after startup"
msgstr "Päivitetäänkö sisältö sovelluksen käynnistyttyä"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:60
msgid "How many periods of time to wait between automatic cleanups"
msgstr ""
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:64
msgid "What period of time to wait between automatic cleanups"
msgstr ""
#. Weird magic I copy-pasted that sets the Application Name in the Shell.
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:3
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:4
#: podcasts-gtk/src/app.rs:91 podcasts-gtk/src/app.rs:414
#: podcasts-gtk/src/widgets/aboutdialog.rs:31
msgid "Podcasts"
msgstr "Podcastit"
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:4
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:10
msgid "Listen to your favorite podcasts, right from your desktop."
msgstr "Kuuntele suosikkipodcastejasi suoraan työpöydältäsi."
#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:6
msgid "@icon@"
msgstr "@icon@"
#. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:13
msgid "Podcast;RSS;"
msgstr "Podcast;RSS;"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:8
msgid "Podcast app for GNOME"
msgstr "Podcast-sovellus Gnomelle"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:31
msgid "Jordan Petridis"
msgstr "Jordan Petridis"
#: podcasts-gtk/resources/gtk/empty_view.ui:46
msgid "This show does not have episodes yet"
msgstr "Tässä ohjelmassa ei ole vielä yhtäkään jaksoa"
#: podcasts-gtk/resources/gtk/empty_view.ui:62
msgid "If you think this is an error, please consider writing a bug report."
msgstr "Jos tämä on mielestäsi virhe, teethän bugiraportin."
#: podcasts-gtk/resources/gtk/empty_view.ui:106
msgid "Get some shows"
msgstr "Hanki ohjelmia"
#: podcasts-gtk/resources/gtk/empty_view.ui:143
msgid "Add new shows via feed URL"
msgstr "Lisää uusia ohjelmia syötteen osoitteen avulla"
#: podcasts-gtk/resources/gtk/empty_view.ui:172
msgid "Import shows from another device"
msgstr "Tuo ohjelmia toiselta laitteelta"
#: podcasts-gtk/resources/gtk/episode_widget.ui:177
msgid "Calculating episode size…"
msgstr "Lasketaan jakson kokoa…"
#: podcasts-gtk/resources/gtk/episode_widget.ui:212
msgid "Cancel"
msgstr "Peru"
#: podcasts-gtk/resources/gtk/episode_widget.ui:230
msgid "Download this episode"
msgstr "Lataa tämä jakso"
#: podcasts-gtk/resources/gtk/episode_widget.ui:254
msgid "Play this episode"
msgstr "Toista tämä jakso"
#: podcasts-gtk/resources/gtk/hamburger.ui:7
msgid "_Check for New Episodes"
msgstr "Ta_rkista uudet jaksot"
#: podcasts-gtk/resources/gtk/hamburger.ui:12
msgid "_Import Shows"
msgstr "Tu_o jaksoja"
#: podcasts-gtk/resources/gtk/hamburger.ui:22
msgid "_Preferences"
msgstr "_Asetukset"
#: podcasts-gtk/resources/gtk/hamburger.ui:29
msgid "_Keyboard Shortcuts"
msgstr "_Pikanäppäimet"
#: podcasts-gtk/resources/gtk/hamburger.ui:37
msgid "_About"
msgstr "_Tietoja"
#: podcasts-gtk/resources/gtk/headerbar.ui:35
#: podcasts-gtk/resources/gtk/headerbar.ui:189
msgid "Add a new feed"
msgstr "Lisää uusi syöte"
#: podcasts-gtk/resources/gtk/headerbar.ui:53
msgid "Enter feed address to add"
msgstr "Anna lisättävän syötteen osoite"
#: podcasts-gtk/resources/gtk/headerbar.ui:89
msgid "Add"
msgstr "Lisää"
#: podcasts-gtk/resources/gtk/headerbar.ui:133
msgid "You are already subscribed to that feed!"
msgstr "Olet jo tilannut kyseisen syötteen!"
#: podcasts-gtk/resources/gtk/headerbar.ui:169
msgid "Show Title"
msgstr "Ohjelman nimi"
#: podcasts-gtk/resources/gtk/headerbar.ui:210
msgid "Back"
msgstr "Takaisin"
#: podcasts-gtk/resources/gtk/help-overlay.ui:12
msgid "General"
msgstr "Yleiset"
#: podcasts-gtk/resources/gtk/help-overlay.ui:18
msgctxt "shortcut window"
msgid "Check for new episodes"
msgstr "Tarkista uudet jaksot"
#: podcasts-gtk/resources/gtk/help-overlay.ui:25
msgctxt "shortcut window"
msgid "Preferences"
msgstr "Asetukset"
#: podcasts-gtk/resources/gtk/help-overlay.ui:32
msgctxt "shortcut window"
msgid "Quit the application"
msgstr "Lopeta sovellus"
#: podcasts-gtk/resources/gtk/home_view.ui:56
msgid "Today"
msgstr "Tänään"
#: podcasts-gtk/resources/gtk/home_view.ui:112
msgid "Yesterday"
msgstr "Eilen"
#: podcasts-gtk/resources/gtk/home_view.ui:168
msgid "This Week"
msgstr "Tällä viikolla"
#: podcasts-gtk/resources/gtk/home_view.ui:224
msgid "This Month"
msgstr "Tässä kuussa"
#: podcasts-gtk/resources/gtk/home_view.ui:281
msgid "Older"
msgstr "Vanhemmat"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:101
msgid "An in-app action notification"
msgstr "Sovelluksen sisäinen ilmoitus"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:112
msgid "Undo"
msgstr "Kumoa"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:72
msgid "Rewind 10 seconds"
msgstr "Siirry taakse 10 sekuntia"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:87
msgid "Play"
msgstr "Toista"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:103
msgid "Pause"
msgstr "Keskeytä"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:119
msgid "Fast forward 10 seconds"
msgstr "Siirry eteen 10 sekuntia"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:285
msgid "Change the playback speed"
msgstr "Vaihda toistonopeutta"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:300
#: podcasts-gtk/resources/gtk/player_toolbar.ui:380
msgid "1.00×"
msgstr "1.00×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:344
msgid "1.50×"
msgstr "1.50×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:348
msgid "1.5 speed rate"
msgstr "1,5-kertainen nopeus"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:362
msgid "1.25×"
msgstr "1.25×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:366
msgid "1.25 speed rate"
msgstr "1,25-kertainen nopeus"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:384
msgid "Normal speed"
msgstr "Tavallinen nopeus"
#: podcasts-gtk/resources/gtk/prefs.ui:42
#: podcasts-gtk/resources/gtk/prefs.ui:295
msgid "Preferences"
msgstr "Asetukset"
#: podcasts-gtk/resources/gtk/prefs.ui:76
msgid "Appearance"
msgstr "Ulkoasu"
#: podcasts-gtk/resources/gtk/prefs.ui:120
msgid "Dark Theme"
msgstr "Tumma teema"
#: podcasts-gtk/resources/gtk/prefs.ui:166
msgid "Delete played episodes"
msgstr "Poista toistetut jaksot"
#: podcasts-gtk/resources/gtk/prefs.ui:211
msgid "After"
msgstr "Viive"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:7
msgid "_Mark All Episodes as Played"
msgstr "_Merkitse kaikki jaksot toistetuiksi"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:11
msgid "_Website"
msgstr "_Verkkosivusto"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:15
msgid "_Unsubscribe"
msgstr "_Lopeta tilaus"
#: podcasts-gtk/resources/gtk/show_menu.ui:51
msgid "Open Website"
msgstr "Avaa verkkosivusto"
#: podcasts-gtk/resources/gtk/show_menu.ui:64
msgid "Mark All as Played"
msgstr "Merkitse kaikki toistetuiksi"
#: podcasts-gtk/resources/gtk/show_menu.ui:89
msgid "Unsubscribe"
msgstr "Lopeta tilaus"
#: podcasts-gtk/src/app.rs:332
msgid "Fetching new episodes"
msgstr "Noudetaan uusia jaksoja"
#: podcasts-gtk/src/headerbar.rs:98
msgid "You are already subscribed to this show"
msgstr "Olet jo tilannut tämän ohjelman"
#: podcasts-gtk/src/headerbar.rs:106
msgid "Invalid URL"
msgstr "Virheellinen osoite"
#: podcasts-gtk/src/prefs.rs:59
msgid "Seconds"
msgstr "sekuntia"
#: podcasts-gtk/src/prefs.rs:60
msgid "Minutes"
msgstr "minuuttia"
#: podcasts-gtk/src/prefs.rs:61
msgid "Hours"
msgstr "tuntia"
#: podcasts-gtk/src/prefs.rs:62
msgid "Days"
msgstr "päivää"
#: podcasts-gtk/src/prefs.rs:63
msgid "Weeks"
msgstr "viikkoa"
#: podcasts-gtk/src/stacks/content.rs:35
msgid "New"
msgstr "Uudet"
#: podcasts-gtk/src/stacks/content.rs:36
msgid "Shows"
msgstr "Ohjelmat"
#: podcasts-gtk/src/utils.rs:350
msgid "Select the file from which to you want to import shows."
msgstr "Valitse tiedosto, josta haluat tuoda ohjelmia."
#: podcasts-gtk/src/utils.rs:353
msgid "_Import"
msgstr "_Tuo"
#: podcasts-gtk/src/utils.rs:362
msgid "OPML file"
msgstr "OPML-tiedosto"
#: podcasts-gtk/src/utils.rs:379
msgid "Failed to parse the imported file"
msgstr "Tuotua tiedostoa ei voitu jäsentää"
#: podcasts-gtk/src/utils.rs:384
msgid "Selected file could not be accessed."
msgstr "Valittua tiedostoa ei voitu käyttää."
#: podcasts-gtk/src/widgets/aboutdialog.rs:26
msgid "Podcast Client for the GNOME Desktop."
msgstr "Podcast-sovellus Gnome-työpöydlle."
#: podcasts-gtk/src/widgets/aboutdialog.rs:33
msgid "Learn more about GNOME Podcasts"
msgstr ""
#: podcasts-gtk/src/widgets/aboutdialog.rs:38
msgid "translator-credits"
msgstr "Jiri Grönroos"
#: podcasts-gtk/src/widgets/episode.rs:129
msgid "{} min"
msgstr "{} min"
#. sender.send(Action::ErrorNotification(format!("Player Error: {}", error)));
#: podcasts-gtk/src/widgets/player.rs:301
msgid "The media player was unable to execute an action."
msgstr "Mediasoitin ei kyennyt suorittamaan toimintoa."
#: podcasts-gtk/src/widgets/show_menu.rs:150
msgid "Marked all episodes as listened"
msgstr "Merkitse kaikki jaksot toistetuiksi"
#: podcasts-gtk/src/widgets/show_menu.rs:155
msgid "Unsubscribed from {}"
msgstr "Lopeta syötteen {} tilaus"
#~ msgid "org.gnome.Podcasts"
#~ msgstr "org.gnome.Podcasts"
#~ msgid "Episode Title"
#~ msgstr "Jakson nimi"
+404
View File
@@ -0,0 +1,404 @@
# Friulian translation for podcasts.
# Copyright (C) 2018 podcasts's COPYRIGHT HOLDER
# This file is distributed under the same license as the podcasts package.
# Fabio Tomat <f.t.public@gmail.com>, 2018.
#
msgid ""
msgstr ""
"Project-Id-Version: podcasts master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/podcasts/issues\n"
"POT-Creation-Date: 2018-09-03 13:42+0000\n"
"PO-Revision-Date: 2018-09-04 20:17+0200\n"
"Language-Team: Friulian <fur@li.org>\n"
"Language: fur\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Last-Translator: \n"
"X-Generator: Poedit 2.0.7\n"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:15
msgid "Top position of the last open main window"
msgstr "Posizion superiôr dal ultin barcon principâl viert"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:19
msgid "Left position of the last open main window"
msgstr "Posizion de çampe dal ultin barcon principâl viert"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:23
msgid "Height of the last open main window"
msgstr "Altece dal ultin barcon principâl viert"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:27
msgid "Width of the last open main window"
msgstr "Largjece dal ultin barcon principâl viert"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:31
msgid "Maximized state of the last open main window"
msgstr "Stât slargjât dal ultin barcon principâl viert"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:36
msgid "Enable or disable dark theme"
msgstr "Abilite o disabilite il teme scûr"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:41
msgid "Whether to periodically refresh content"
msgstr "Indiche se inzornâ in maniere periodiche il contignût"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:46
msgid "How many periods of time to wait between automatic refreshes"
msgstr "Trops periodis di timp di spietâ tra i inzornaments automatics"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:50
msgid "What period of time to wait between automatic refreshes"
msgstr "Ce periodi di timp di spietâ tra i inzornaments automatics"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:54
msgid "Whether to refresh content after startup"
msgstr "Indiche se inzornâ il contignût dopo dal inviament"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:60
msgid "How many periods of time to wait between automatic cleanups"
msgstr "Trops periodis di timp di spietâ tra lis netisiis automatichis"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:64
msgid "What period of time to wait between automatic cleanups"
msgstr "Ce periodi di timp di spietâ tra lis netisiis automatichis"
#. Weird magic I copy-pasted that sets the Application Name in the Shell.
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:3
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:4
#: podcasts-gtk/src/app.rs:91 podcasts-gtk/src/app.rs:414
#: podcasts-gtk/src/widgets/aboutdialog.rs:31
msgid "Podcasts"
msgstr "Podcasts"
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:4
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:10
msgid "Listen to your favorite podcasts, right from your desktop."
msgstr "Scolte i tiei podcast preferîts, daurman dal to scritori."
#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:6
msgid "@icon@"
msgstr "@icon@"
#. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:13
msgid "Podcast;RSS;"
msgstr "Podcast;RSS;Regjistrazion;Trasmission;"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:8
msgid "Podcast app for GNOME"
msgstr "Aplicazion podcast par GNOME"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:51
msgid "Jordan Petridis"
msgstr "Jordan Petridis"
#: podcasts-gtk/resources/gtk/empty_view.ui:46
msgid "This show does not have episodes yet"
msgstr "Chest spetacul nol à ancjemò episodis"
#: podcasts-gtk/resources/gtk/empty_view.ui:62
msgid "If you think this is an error, please consider writing a bug report."
msgstr "Se tu pensis che chest al sedi un erôr, considere di scrivi une segnalazion di erôr."
#: podcasts-gtk/resources/gtk/empty_view.ui:106
msgid "Get some shows"
msgstr "Oten cualchi spetacul"
#: podcasts-gtk/resources/gtk/empty_view.ui:143
msgid "Add new shows via feed URL"
msgstr "Zonte gnûfs spetacui vie URL di feed"
#: podcasts-gtk/resources/gtk/empty_view.ui:172
msgid "Import shows from another device"
msgstr "Impuarte i spetacui di un altri dispositîf"
#: podcasts-gtk/resources/gtk/episode_widget.ui:178
msgid "Calculating episode size…"
msgstr "Calcul de dimension dal episodi…"
#: podcasts-gtk/resources/gtk/episode_widget.ui:214
msgid "Cancel"
msgstr "Anule"
#: podcasts-gtk/resources/gtk/episode_widget.ui:232
msgid "Download this episode"
msgstr "Discjarie chest episodi"
#: podcasts-gtk/resources/gtk/episode_widget.ui:256
msgid "Play this episode"
msgstr "Viôt chest episodi"
#: podcasts-gtk/resources/gtk/hamburger.ui:7
msgid "_Check for New Episodes"
msgstr "_Controle par gnûfs episodis"
#: podcasts-gtk/resources/gtk/hamburger.ui:12
msgid "_Import Shows"
msgstr "_Impuarte spetacui"
#: podcasts-gtk/resources/gtk/hamburger.ui:22
msgid "_Preferences"
msgstr "_Preferencis"
#: podcasts-gtk/resources/gtk/hamburger.ui:29
msgid "_Keyboard Shortcuts"
msgstr "_Scurtis tastiere"
#: podcasts-gtk/resources/gtk/hamburger.ui:37
msgid "_About"
msgstr "_Informazions"
#: podcasts-gtk/resources/gtk/headerbar.ui:35
#: podcasts-gtk/resources/gtk/headerbar.ui:189
msgid "Add a new feed"
msgstr "Zonte un gnûf feed"
#: podcasts-gtk/resources/gtk/headerbar.ui:53
msgid "Enter feed address to add"
msgstr "Inserìs la direzion dal feed di zontâ"
#: podcasts-gtk/resources/gtk/headerbar.ui:89
msgid "Add"
msgstr "Zonte"
#: podcasts-gtk/resources/gtk/headerbar.ui:133
msgid "You are already subscribed to that feed!"
msgstr "Tu sês za sotscrit a chest feed!"
#: podcasts-gtk/resources/gtk/headerbar.ui:169
msgid "Show Title"
msgstr "Titul dal spetacul"
#: podcasts-gtk/resources/gtk/headerbar.ui:210
msgid "Back"
msgstr "Indaûr"
#: podcasts-gtk/resources/gtk/help-overlay.ui:12
msgid "General"
msgstr "Gjenerâl"
#: podcasts-gtk/resources/gtk/help-overlay.ui:18
msgctxt "shortcut window"
msgid "Check for new episodes"
msgstr "Controle se a son gnûfs episodis"
#: podcasts-gtk/resources/gtk/help-overlay.ui:25
msgctxt "shortcut window"
msgid "Preferences"
msgstr "Preferencis"
#: podcasts-gtk/resources/gtk/help-overlay.ui:32
msgctxt "shortcut window"
msgid "Quit the application"
msgstr "Jes de aplicazion"
#: podcasts-gtk/resources/gtk/home_view.ui:56
msgid "Today"
msgstr "Vuê"
#: podcasts-gtk/resources/gtk/home_view.ui:112
msgid "Yesterday"
msgstr "Îr"
#: podcasts-gtk/resources/gtk/home_view.ui:168
msgid "This Week"
msgstr "Cheste setemane"
#: podcasts-gtk/resources/gtk/home_view.ui:224
msgid "This Month"
msgstr "Chest mês"
#: podcasts-gtk/resources/gtk/home_view.ui:281
msgid "Older"
msgstr "Plui vecjo"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:101
msgid "An in-app action notification"
msgstr "Une notifiche di aplicazion in-app"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:112
msgid "Undo"
msgstr "Disfe"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:72
msgid "Rewind 10 seconds"
msgstr "Torne indaûr di 10 seconts"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:87
msgid "Play"
msgstr "Riprodûs"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:103
msgid "Pause"
msgstr "Pause"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:119
msgid "Fast forward 10 seconds"
msgstr "Va indenant di 10 minûts"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:285
msgid "Change the playback speed"
msgstr "Cambie la velocitât di riproduzion"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:300
#: podcasts-gtk/resources/gtk/player_toolbar.ui:380
msgid "1.00×"
msgstr "1.00×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:344
msgid "1.50×"
msgstr "1.50×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:348
msgid "1.5 speed rate"
msgstr "rapuart di velocitât 1.5"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:362
msgid "1.25×"
msgstr "1.25×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:366
msgid "1.25 speed rate"
msgstr "rapuart di velocitât 1.25"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:384
msgid "Normal speed"
msgstr "Velocitât normâl"
#: podcasts-gtk/resources/gtk/prefs.ui:42
#: podcasts-gtk/resources/gtk/prefs.ui:295
msgid "Preferences"
msgstr "Preferencis"
#: podcasts-gtk/resources/gtk/prefs.ui:76
msgid "Appearance"
msgstr "Aspiet"
#: podcasts-gtk/resources/gtk/prefs.ui:120
msgid "Dark Theme"
msgstr "Teme scûr"
#: podcasts-gtk/resources/gtk/prefs.ui:166
msgid "Delete played episodes"
msgstr "Elimine i episodis viodûts"
#: podcasts-gtk/resources/gtk/prefs.ui:211
msgid "After"
msgstr "Dopo"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:7
msgid "_Mark All Episodes as Played"
msgstr "_Segne ducj i episodis come viodûts"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:11
msgid "_Website"
msgstr "Sît _web"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:15
msgid "_Unsubscribe"
msgstr "_Anule sotscrizion"
#: podcasts-gtk/resources/gtk/show_menu.ui:51
msgid "Open Website"
msgstr "Vierç sît web"
#: podcasts-gtk/resources/gtk/show_menu.ui:64
msgid "Mark All as Played"
msgstr "Segne dut come viodût"
#: podcasts-gtk/resources/gtk/show_menu.ui:89
msgid "Unsubscribe"
msgstr "Anule sotscrizion"
#: podcasts-gtk/src/app.rs:332
msgid "Fetching new episodes"
msgstr "Daûr a recuperâ i gnûfs episodis"
#: podcasts-gtk/src/headerbar.rs:98
msgid "You are already subscribed to this show"
msgstr "Tu sês za sotscrit a chest spetacul"
#: podcasts-gtk/src/headerbar.rs:106
msgid "Invalid URL"
msgstr "URL no valit"
#: podcasts-gtk/src/prefs.rs:59
msgid "Seconds"
msgstr "Seconts"
#: podcasts-gtk/src/prefs.rs:60
msgid "Minutes"
msgstr "Minûts"
#: podcasts-gtk/src/prefs.rs:61
msgid "Hours"
msgstr "Oris"
#: podcasts-gtk/src/prefs.rs:62
msgid "Days"
msgstr "Diis"
#: podcasts-gtk/src/prefs.rs:63
msgid "Weeks"
msgstr "Setemanis"
#: podcasts-gtk/src/stacks/content.rs:35
msgid "New"
msgstr "Gnûf"
#: podcasts-gtk/src/stacks/content.rs:36
msgid "Shows"
msgstr "Spetacui"
#: podcasts-gtk/src/utils.rs:357
msgid "Select the file from which to you want to import shows."
msgstr "Selezione il file che di chei si desidere impuartâ i spetacui."
#: podcasts-gtk/src/utils.rs:360
msgid "_Import"
msgstr "_Impuarte"
#: podcasts-gtk/src/utils.rs:369
msgid "OPML file"
msgstr "File OPML"
#: podcasts-gtk/src/utils.rs:386
msgid "Failed to parse the imported file"
msgstr "No si è rivâts a analizâ il file impuartât"
#: podcasts-gtk/src/utils.rs:391
msgid "Selected file could not be accessed."
msgstr "Il file selezionât nol pues jessi doprât."
#: podcasts-gtk/src/widgets/aboutdialog.rs:26
msgid "Podcast Client for the GNOME Desktop."
msgstr "Client podcast pal scritori GNOME."
#: podcasts-gtk/src/widgets/aboutdialog.rs:33
msgid "Learn more about GNOME Podcasts"
msgstr "Plui informazions su GNOME Podcasts"
#: podcasts-gtk/src/widgets/aboutdialog.rs:38
msgid "translator-credits"
msgstr "Fabio Tomat <f.t.public@gmail.com>"
#: podcasts-gtk/src/widgets/episode.rs:129
msgid "{} min"
msgstr "{} min"
#. sender.send(Action::ErrorNotification(format!("Player Error: {}", error)));
#: podcasts-gtk/src/widgets/player.rs:301
msgid "The media player was unable to execute an action."
msgstr "Il riprodutôr multimediâl nol è stât bon di eseguî une azion."
#: podcasts-gtk/src/widgets/show_menu.rs:150
msgid "Marked all episodes as listened"
msgstr "Segnât ducj i episodis come scoltâts"
#: podcasts-gtk/src/widgets/show_menu.rs:155
msgid "Unsubscribed from {}"
msgstr "Sotscrizion anulade di {}"
+407
View File
@@ -0,0 +1,407 @@
# Spanish translations for gnome-podcasts package.
# Copyright (C) 2018 THE gnome-podcasts'S COPYRIGHT HOLDER
# This file is distributed under the same license as the gnome-podcasts package.
# Automatically generated, 2018.
# Fran Dieguez <frandieguez@gnome.org>, 2018.
msgid ""
msgstr ""
"Project-Id-Version: gnome-podcasts\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-08-22 06:50+0300\n"
"PO-Revision-Date: 2018-08-30 10:39+0200\n"
"Last-Translator: Fran Dieguez <frandieguez@gnome.org>\n"
"Language-Team: Galician\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Virtaal 0.7.1\n"
"X-Project-Style: gnome\n"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:15
msgid "Top position of the last open main window"
msgstr "Posición superior da última xanela aberta"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:19
msgid "Left position of the last open main window"
msgstr "Posición esquerda da última xanela aberta"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:23
msgid "Height of the last open main window"
msgstr "Alto da última xanela aberta"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:27
msgid "Width of the last open main window"
msgstr "Anchura da última xanela aberta"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:31
msgid "Maximized state of the last open main window"
msgstr "Estado de maximizado da última xanela aberta"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:36
msgid "Enable or disable dark theme"
msgstr "Activar o desactivar el tema oscuro"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:41
msgid "Whether to periodically refresh content"
msgstr "Se refrescar o contido periodicamente"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:46
msgid "How many periods of time to wait between automatic refreshes"
msgstr "Cantos periodos de tempo a esperar entre refrescos automáticos"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:50
msgid "What period of time to wait between automatic refreshes"
msgstr "Qué periodo de tempo hai que esperar entre refrescos automáticos"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:54
msgid "Whether to refresh content after startup"
msgstr "Se refrescar o contido ao iniciar"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:60
msgid "How many periods of time to wait between automatic cleanups"
msgstr "Cantos periodos de tempo hai que esperar entre limpados automáticos"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:64
msgid "What period of time to wait between automatic cleanups"
msgstr "Cantos periodos de tiempo hai que esperar entre limpiados automáticos"
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop:3
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml:4
#: podcasts-gtk/src/app.rs:92 podcasts-gtk/src/app.rs:406
#: podcasts-gtk/src/widgets/aboutdialog.rs:33
msgid "Podcasts"
msgstr "Podcasts"
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop:4
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml:10
msgid "Listen to your favorite podcasts, right from your desktop."
msgstr ""
"Escoita os teus podcasts favoritos, directamente desde o teu escritorio."
#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop:6
msgid "org.gnome.Podcasts"
msgstr "org.gnome.Podcasts"
#. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop:13
msgid "Podcast;RSS;"
msgstr "Podcast;RSS;"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml:8
msgid "Podcast app for GNOME"
msgstr "Aplicativo de Podcast para GNOME"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml:31
msgid "Jordan Petridis"
msgstr "Jordan Petridis"
#: podcasts-gtk/resources/gtk/empty_view.ui:46
msgid "This show does not have episodes yet"
msgstr "Este programa aínda non ten episodios"
#: podcasts-gtk/resources/gtk/empty_view.ui:62
msgid "If you think this is an error, please consider writing a bug report."
msgstr ""
"Se cres que isto é un erro, por favor, considera abrir un informe de erro."
#: podcasts-gtk/resources/gtk/empty_view.ui:107
msgid "Get some shows"
msgstr "Obter algúns programas"
#: podcasts-gtk/resources/gtk/empty_view.ui:144
msgid "Add new shows via feed URL"
msgstr "Engadir novo programa a través do URL da canle"
#: podcasts-gtk/resources/gtk/empty_view.ui:173
msgid "Import shows from another device"
msgstr "Importar programas desde outro dispositivo"
#: podcasts-gtk/resources/gtk/episode_widget.ui:177
msgid "Calculating episode size…"
msgstr "Calculando tamaño do episodio…"
#: podcasts-gtk/resources/gtk/episode_widget.ui:212
msgid "Cancel"
msgstr "Cancelar"
#: podcasts-gtk/resources/gtk/episode_widget.ui:230
msgid "Download this episode"
msgstr "Descargar este episodio"
#: podcasts-gtk/resources/gtk/episode_widget.ui:254
msgid "Play this episode"
msgstr "Reproducir este episodio"
#: podcasts-gtk/resources/gtk/hamburger.ui:7
msgid "_Check for New Episodes"
msgstr "_Comprobar se hai novos episodios"
#: podcasts-gtk/resources/gtk/hamburger.ui:12
msgid "_Import Shows"
msgstr "_Importar programas"
#: podcasts-gtk/resources/gtk/hamburger.ui:22
msgid "_Preferences"
msgstr "_Preferencias"
#: podcasts-gtk/resources/gtk/hamburger.ui:29
msgid "_Keyboard Shortcuts"
msgstr "Atallos de _teclado"
#: podcasts-gtk/resources/gtk/hamburger.ui:37
msgid "_About"
msgstr "_Sobre"
#: podcasts-gtk/resources/gtk/headerbar.ui:35
#: podcasts-gtk/resources/gtk/headerbar.ui:189
msgid "Add a new feed"
msgstr "Engadir unha nova canle"
#: podcasts-gtk/resources/gtk/headerbar.ui:53
msgid "Enter feed address to add"
msgstr "Escriba o enderezo da canle a engadir"
#: podcasts-gtk/resources/gtk/headerbar.ui:89
msgid "Add"
msgstr "Engadir"
#: podcasts-gtk/resources/gtk/headerbar.ui:133
msgid "You are already subscribed to that feed!"
msgstr "Xa estás suscrito a esta canle!"
#: podcasts-gtk/resources/gtk/headerbar.ui:169
msgid "Show Title"
msgstr "Título do Programa"
#: podcasts-gtk/resources/gtk/headerbar.ui:210
msgid "Back"
msgstr "Atrás"
#: podcasts-gtk/resources/gtk/help-overlay.ui:12
msgid "General"
msgstr "Xeral"
#: podcasts-gtk/resources/gtk/help-overlay.ui:18
msgctxt "shortcut window"
msgid "Check for new episodes"
msgstr "Comprobar se hai novos episodios"
#: podcasts-gtk/resources/gtk/help-overlay.ui:25
msgctxt "shortcut window"
msgid "Preferences"
msgstr "Preferencias"
#: podcasts-gtk/resources/gtk/help-overlay.ui:32
msgctxt "shortcut window"
msgid "Quit the application"
msgstr "Saír do aplicativo"
#: podcasts-gtk/resources/gtk/home_view.ui:56
msgid "Today"
msgstr "Hoxe"
#: podcasts-gtk/resources/gtk/home_view.ui:112
msgid "Yesterday"
msgstr "Onte"
#: podcasts-gtk/resources/gtk/home_view.ui:168
msgid "This Week"
msgstr "Esta semana"
#: podcasts-gtk/resources/gtk/home_view.ui:224
msgid "This Month"
msgstr "Este mes"
#: podcasts-gtk/resources/gtk/home_view.ui:281
msgid "Older"
msgstr "Antigo"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:101
msgid "An in-app action notification"
msgstr "Unha notificación de acción en aplicativo"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:112
msgid "Undo"
msgstr "Desfacer"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:72
msgid "Rewind 10 seconds"
msgstr "Atrás 10 segundos"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:87
msgid "Play"
msgstr "Reproducir"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:103
msgid "Pause"
msgstr "Pausa"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:119
msgid "Fast forward 10 seconds"
msgstr "Adiante 10 segundos"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:285
msgid "Change the playback speed"
msgstr "Cambiar velocidade de reprodución"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:300
#: podcasts-gtk/resources/gtk/player_toolbar.ui:380
msgid "1.00×"
msgstr "1.00×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:344
msgid "1.50×"
msgstr "1.50×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:348
msgid "1.5 speed rate"
msgstr "velocidade 1.5"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:362
msgid "1.25×"
msgstr "1.25×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:366
msgid "1.25 speed rate"
msgstr "velocidade 1.25"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:384
msgid "Normal speed"
msgstr "Velocidade normal"
#: podcasts-gtk/resources/gtk/prefs.ui:42
#: podcasts-gtk/resources/gtk/prefs.ui:295
msgid "Preferences"
msgstr "Preferencias"
#: podcasts-gtk/resources/gtk/prefs.ui:76
msgid "Appearance"
msgstr "Aspecto"
#: podcasts-gtk/resources/gtk/prefs.ui:120
msgid "Dark Theme"
msgstr "Tema escuro"
#: podcasts-gtk/resources/gtk/prefs.ui:166
msgid "Delete played episodes"
msgstr "Eliminar episodios reproducidos"
#: podcasts-gtk/resources/gtk/prefs.ui:211
msgid "After"
msgstr "Despois"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:7
msgid "_Mark All Episodes as Played"
msgstr "_Marcar todos os episodios como reproducidos"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:11
msgid "_Website"
msgstr "Páxina _Web"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:15
msgid "_Unsubscribe"
msgstr "_Cancelar subscrición"
#: podcasts-gtk/resources/gtk/show_menu.ui:51
msgid "Open Website"
msgstr "Abrir sitio web"
#: podcasts-gtk/resources/gtk/show_menu.ui:64
msgid "Mark All as Played"
msgstr "Marcar todo como reproducido"
#: podcasts-gtk/resources/gtk/show_menu.ui:89
msgid "Unsubscribe"
msgstr "Cancelar subscrición"
#: podcasts-gtk/src/app.rs:331
msgid "Fetching new episodes"
msgstr "Obtendo novos episodios"
#: podcasts-gtk/src/headerbar.rs:98
msgid "You are already subscribed to this show"
msgstr "Xa estás subscrito a este programa"
#: podcasts-gtk/src/headerbar.rs:106
msgid "Invalid URL"
msgstr "URL non válida"
#: podcasts-gtk/src/prefs.rs:59
msgid "Seconds"
msgstr "Segundos"
#: podcasts-gtk/src/prefs.rs:60
msgid "Minutes"
msgstr "Minutos"
#: podcasts-gtk/src/prefs.rs:61
msgid "Hours"
msgstr "Horas"
#: podcasts-gtk/src/prefs.rs:62
msgid "Days"
msgstr "Días"
#: podcasts-gtk/src/prefs.rs:63
msgid "Weeks"
msgstr "Semanas"
#: podcasts-gtk/src/stacks/content.rs:35
msgid "New"
msgstr "Novo"
#: podcasts-gtk/src/stacks/content.rs:36
msgid "Shows"
msgstr "Programas"
#: podcasts-gtk/src/utils.rs:350
msgid "Select the file from which to you want to import shows."
msgstr "Seleccione o fichero desde o cal queres importar programas."
#: podcasts-gtk/src/utils.rs:353
msgid "_Import"
msgstr "_Importar"
#: podcasts-gtk/src/utils.rs:362
msgid "OPML file"
msgstr "Fichero OPML"
#: podcasts-gtk/src/utils.rs:379
msgid "Failed to parse the imported file"
msgstr "Produciuse un fallo ao leer o fichero importado"
#: podcasts-gtk/src/utils.rs:384
msgid "Selected file could not be accessed."
msgstr "O fichero seleccionado non é accesíbel."
#: podcasts-gtk/src/widgets/aboutdialog.rs:26
msgid "Podcast Client for the GNOME Desktop."
msgstr "Aplicativo de Podcast para o ambiente GNOME."
#: podcasts-gtk/src/widgets/aboutdialog.rs:35
msgid "Learn more about GNOME Podcasts"
msgstr "Saber máis sobre Podcasts de GNOME"
#: podcasts-gtk/src/widgets/aboutdialog.rs:40
msgid "translator-credits"
msgstr "Fran Dieguez <frandieguez@gnome.org>, 2018"
#: podcasts-gtk/src/widgets/episode.rs:129
msgid "{} min"
msgstr "{} min"
#. sender.send(Action::ErrorNotification(format!("Player Error: {}", error)));
#: podcasts-gtk/src/widgets/player.rs:301
msgid "The media player was unable to execute an action."
msgstr "O reprodutor non puido reproducir unha acción."
#: podcasts-gtk/src/widgets/show_menu.rs:150
msgid "Marked all episodes as listened"
msgstr "Marcar todos os episodios como escoitados"
#: podcasts-gtk/src/widgets/show_menu.rs:155
msgid "Unsubscribed from {}"
msgstr "Subscrición cancelada para {}"
+414
View File
@@ -0,0 +1,414 @@
# Croatian translation for podcasts.
# Copyright (C) 2018 podcasts's COPYRIGHT HOLDER
# This file is distributed under the same license as the podcasts package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: podcasts master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/podcasts/issues\n"
"POT-Creation-Date: 2018-09-08 17:18+0000\n"
"PO-Revision-Date: 2018-09-19 14:24+0200\n"
"Last-Translator: \n"
"Language-Team: Croatian <hr@li.org>\n"
"Language: hr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
"X-Generator: Poedit 2.1.1\n"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:15
msgid "Top position of the last open main window"
msgstr "Gornji položaj posljednjeg otvorenog prozora"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:19
msgid "Left position of the last open main window"
msgstr "Lijevi položaj posljednjeg otvorenog prozora"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:23
msgid "Height of the last open main window"
msgstr "Visina posljednjeg otvorenog prozora"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:27
msgid "Width of the last open main window"
msgstr "Širina posljednjeg otvorenog prozora"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:31
msgid "Maximized state of the last open main window"
msgstr "Stanje uvećanja posljednjeg otvorenog prozora"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:36
msgid "Enable or disable dark theme"
msgstr "Omogući ili onemogući tamnu temu"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:41
msgid "Whether to periodically refresh content"
msgstr "Treba li povremeno osvježiti sadržaj"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:46
msgid "How many periods of time to wait between automatic refreshes"
msgstr ""
"Koliko vremenskih razdoblja treba pričekati između automatskih osvježavanja"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:50
msgid "What period of time to wait between automatic refreshes"
msgstr ""
"Koliko vremensko razdoblje treba pričekati između automatskih osvježavanja"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:54
msgid "Whether to refresh content after startup"
msgstr "Treba li osvježiti sadržaj nakon pokretanja"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:60
msgid "How many periods of time to wait between automatic cleanups"
msgstr ""
"Koliko vremenskih razdoblja treba pričekati između automatskih čišćenja"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:64
msgid "What period of time to wait between automatic cleanups"
msgstr "Koliko vremensko razdoblje treba pričekati između automatskih čišćenja"
#. Weird magic I copy-pasted that sets the Application Name in the Shell.
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:3
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:4
#: podcasts-gtk/src/app.rs:91 podcasts-gtk/src/app.rs:414
#: podcasts-gtk/src/widgets/aboutdialog.rs:31
msgid "Podcasts"
msgstr "Podcasti"
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:4
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:10
msgid "Listen to your favorite podcasts, right from your desktop."
msgstr "Slušajte svoje omiljene podcaste."
#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:6
msgid "@icon@"
msgstr "@icon@"
#. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:13
msgid "Podcast;RSS;"
msgstr "Podcast;RSS;"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:8
msgid "Podcast app for GNOME"
msgstr "Podcast aplikacija za GNOME"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:51
msgid "Jordan Petridis"
msgstr "Jordan Petridis"
#: podcasts-gtk/resources/gtk/empty_view.ui:46
msgid "This show does not have episodes yet"
msgstr "Ova emisjia još nema epizoda"
#: podcasts-gtk/resources/gtk/empty_view.ui:62
msgid "If you think this is an error, please consider writing a bug report."
msgstr "Ako mislite da je ovo greška, prijavite izvještaj greške."
#: podcasts-gtk/resources/gtk/empty_view.ui:106
msgid "Get some shows"
msgstr "Nabavite nove emisije"
#: podcasts-gtk/resources/gtk/empty_view.ui:143
msgid "Add new shows via feed URL"
msgstr "Dodaj novu emisiju putem kanala ili URL-a"
#: podcasts-gtk/resources/gtk/empty_view.ui:172
msgid "Import shows from another device"
msgstr "Uvezi emisije iz drugog uređaja"
#: podcasts-gtk/resources/gtk/episode_widget.ui:180
msgid "Calculating episode size…"
msgstr "Izračunavanje veličine emisjie…"
#: podcasts-gtk/resources/gtk/episode_widget.ui:220
msgid "Play this episode"
msgstr "Slušaj ovu epizodu"
#: podcasts-gtk/resources/gtk/episode_widget.ui:241
msgid "Cancel the download proccess"
msgstr "Prekini preuzimanja"
#: podcasts-gtk/resources/gtk/episode_widget.ui:264
msgid "Download this episode"
msgstr "Preuzmi ovu epizodu"
#: podcasts-gtk/resources/gtk/hamburger.ui:7
msgid "_Check for New Episodes"
msgstr "_Provjeri ima li novih epizoda"
#: podcasts-gtk/resources/gtk/hamburger.ui:12
msgid "_Import Shows"
msgstr "_Uvezi emisije"
#: podcasts-gtk/resources/gtk/hamburger.ui:22
msgid "_Preferences"
msgstr "_Osobitosti"
#: podcasts-gtk/resources/gtk/hamburger.ui:29
msgid "_Keyboard Shortcuts"
msgstr "_Prečaci tipkovnice"
#: podcasts-gtk/resources/gtk/hamburger.ui:37
msgid "_About"
msgstr "_O programu"
#: podcasts-gtk/resources/gtk/headerbar.ui:35
#: podcasts-gtk/resources/gtk/headerbar.ui:189
msgid "Add a new feed"
msgstr "Dodaj novi kanal"
#: podcasts-gtk/resources/gtk/headerbar.ui:53
msgid "Enter feed address to add"
msgstr "Upišite adresu kanala za dodavanje"
#: podcasts-gtk/resources/gtk/headerbar.ui:89
msgid "Add"
msgstr "Dodaj"
#: podcasts-gtk/resources/gtk/headerbar.ui:133
msgid "You are already subscribed to that feed!"
msgstr "Već ste pretplaćeni na ovaj kanal!"
#: podcasts-gtk/resources/gtk/headerbar.ui:169
msgid "Show Title"
msgstr "Prikaži naslov"
#: podcasts-gtk/resources/gtk/headerbar.ui:210
msgid "Back"
msgstr "Natrag"
#: podcasts-gtk/resources/gtk/help-overlay.ui:12
msgid "General"
msgstr "Općenito"
#: podcasts-gtk/resources/gtk/help-overlay.ui:18
msgctxt "shortcut window"
msgid "Check for new episodes"
msgstr "Provjeri ima li novih epizoda"
#: podcasts-gtk/resources/gtk/help-overlay.ui:25
msgctxt "shortcut window"
msgid "Preferences"
msgstr "Osobitosti"
#: podcasts-gtk/resources/gtk/help-overlay.ui:32
msgctxt "shortcut window"
msgid "Quit the application"
msgstr "Zatvori aplikaciju"
#: podcasts-gtk/resources/gtk/home_view.ui:56
msgid "Today"
msgstr "Danas"
#: podcasts-gtk/resources/gtk/home_view.ui:112
msgid "Yesterday"
msgstr "Jučer"
#: podcasts-gtk/resources/gtk/home_view.ui:168
msgid "This Week"
msgstr "Ovaj tjedan"
#: podcasts-gtk/resources/gtk/home_view.ui:224
msgid "This Month"
msgstr "Ovaj mjesec"
#: podcasts-gtk/resources/gtk/home_view.ui:281
msgid "Older"
msgstr "Starije"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:101
msgid "An in-app action notification"
msgstr "Obavijesti radnja iz aplikacije"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:112
msgid "Undo"
msgstr "Vrati"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:72
msgid "Rewind 10 seconds"
msgstr "Premotaj 10 sekundi"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:87
msgid "Play"
msgstr "Reproduciraj"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:103
msgid "Pause"
msgstr "Pauziraj"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:119
msgid "Fast forward 10 seconds"
msgstr "Brzo premotaj 10 sekundi"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:285
msgid "Change the playback speed"
msgstr "Promijeni brzinu reprodukcije"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:300
#: podcasts-gtk/resources/gtk/player_toolbar.ui:380
msgid "1.00×"
msgstr "1.00×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:344
msgid "1.50×"
msgstr "1.50×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:348
msgid "1.5 speed rate"
msgstr "1.5 brže"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:362
msgid "1.25×"
msgstr "1.25×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:366
msgid "1.25 speed rate"
msgstr "1.25 brže"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:384
msgid "Normal speed"
msgstr "Normalna brzina"
#: podcasts-gtk/resources/gtk/prefs.ui:42
#: podcasts-gtk/resources/gtk/prefs.ui:295
msgid "Preferences"
msgstr "Osobitosti"
#: podcasts-gtk/resources/gtk/prefs.ui:76
msgid "Appearance"
msgstr "Izgled"
#: podcasts-gtk/resources/gtk/prefs.ui:120
msgid "Dark Theme"
msgstr "Tamna tema"
#: podcasts-gtk/resources/gtk/prefs.ui:166
msgid "Delete played episodes"
msgstr "Obriši poslušane epizode"
#: podcasts-gtk/resources/gtk/prefs.ui:211
msgid "After"
msgstr "Poslije"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:7
msgid "_Mark All Episodes as Played"
msgstr "_Označi se epizode kao odslušane"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:11
msgid "_Website"
msgstr "_Web stranica"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:15
msgid "_Unsubscribe"
msgstr "_Ukini pretplatu"
#: podcasts-gtk/resources/gtk/show_menu.ui:51
msgid "Open Website"
msgstr "Otvori web stranicu"
#: podcasts-gtk/resources/gtk/show_menu.ui:64
msgid "Mark All as Played"
msgstr "Označi sve kao odslušano"
#: podcasts-gtk/resources/gtk/show_menu.ui:89
msgid "Unsubscribe"
msgstr "Ukini pretplatu"
#: podcasts-gtk/src/app.rs:332
msgid "Fetching new episodes"
msgstr "Preuzimanje novi epizoda"
#: podcasts-gtk/src/headerbar.rs:98
msgid "You are already subscribed to this show"
msgstr "Već ste pretplaćeni na ovu emisiju"
#: podcasts-gtk/src/headerbar.rs:106
msgid "Invalid URL"
msgstr "Nevažeći URL"
#: podcasts-gtk/src/prefs.rs:59
msgid "Seconds"
msgstr "Sekunda"
#: podcasts-gtk/src/prefs.rs:60
msgid "Minutes"
msgstr "Minuta"
#: podcasts-gtk/src/prefs.rs:61
msgid "Hours"
msgstr "Sata"
#: podcasts-gtk/src/prefs.rs:62
msgid "Days"
msgstr "Dana"
#: podcasts-gtk/src/prefs.rs:63
msgid "Weeks"
msgstr "Tjedana"
#: podcasts-gtk/src/stacks/content.rs:35
msgid "New"
msgstr "Novo"
#: podcasts-gtk/src/stacks/content.rs:36
msgid "Shows"
msgstr "Emisije"
#: podcasts-gtk/src/utils.rs:357
msgid "Select the file from which to you want to import shows."
msgstr "Odaberite datoteku iz koje želite uvesti emisije."
#: podcasts-gtk/src/utils.rs:360
msgid "_Import"
msgstr "_Uvoz"
#: podcasts-gtk/src/utils.rs:369
msgid "OPML file"
msgstr "OPML datoteka"
#: podcasts-gtk/src/utils.rs:386
msgid "Failed to parse the imported file"
msgstr "Neuspjela obrada uvezene datoteke"
#: podcasts-gtk/src/utils.rs:391
msgid "Selected file could not be accessed."
msgstr "Nemoguć pristup dabranoj datoteci."
#: podcasts-gtk/src/widgets/aboutdialog.rs:26
msgid "Podcast Client for the GNOME Desktop."
msgstr "Podcast klijent za GNOME radnu površinu."
#: podcasts-gtk/src/widgets/aboutdialog.rs:33
msgid "Learn more about GNOME Podcasts"
msgstr "Saznajte više o GNOME Podcasti"
#: podcasts-gtk/src/widgets/aboutdialog.rs:38
msgid "translator-credits"
msgstr ""
"Launchpad Contributions:\n"
" gogo https://launchpad.net/~trebelnik-stefina"
#: podcasts-gtk/src/widgets/episode.rs:130
msgid "{} min"
msgstr "{} min"
#. sender.send(Action::ErrorNotification(format!("Player Error: {}", error)));
#: podcasts-gtk/src/widgets/player.rs:301
msgid "The media player was unable to execute an action."
msgstr "Medijski reproduktor nije uspio izvršiti radnju."
#: podcasts-gtk/src/widgets/show_menu.rs:150
msgid "Marked all episodes as listened"
msgstr "Označi sve epizodae kao odslušane"
#: podcasts-gtk/src/widgets/show_menu.rs:155
msgid "Unsubscribed from {}"
msgstr "Ukini pretplatu za {}"
#~ msgid "Cancel"
#~ msgstr "Odustani"
+407
View File
@@ -0,0 +1,407 @@
# Hungarian translation for podcasts.
# Copyright (C) 2018. Free Software Foundation, Inc.
# This file is distributed under the same license as the podcasts package.
#
# Meskó Balázs <mesko.balazs@fsf.hu>, 2018.
msgid ""
msgstr ""
"Project-Id-Version: podcasts master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/podcasts/issues\n"
"POT-Creation-Date: 2018-09-03 11:31+0000\n"
"PO-Revision-Date: 2018-09-06 00:08+0100\n"
"Last-Translator: Meskó Balázs <mesko.balazs@fsf.hu>\n"
"Language-Team: Hungarian <openscope at googlegroups dot com>\n"
"Language: hu\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Lokalize 2.0\n"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:15
msgid "Top position of the last open main window"
msgstr "A legutóbbi nyitott főablak fenti pozíciója"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:19
msgid "Left position of the last open main window"
msgstr "A legutóbbi nyitott főablak bal oldali pozíciója"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:23
msgid "Height of the last open main window"
msgstr "A legutóbbi nyitott főablak magassága"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:27
msgid "Width of the last open main window"
msgstr "A legutóbbi nyitott főablak szélessége"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:31
msgid "Maximized state of the last open main window"
msgstr "A legutóbbi nyitott főablak maximalizálási állapota"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:36
msgid "Enable or disable dark theme"
msgstr "Sötét téma engedélyezése vagy letiltása"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:41
msgid "Whether to periodically refresh content"
msgstr "Időközönként frissüljön-e a tartalom"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:46
msgid "How many periods of time to wait between automatic refreshes"
msgstr "Mennyi időközt várjon az automatikus frissítések között"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:50
msgid "What period of time to wait between automatic refreshes"
msgstr "Milyen időközöket várjon az automatikus frissítések között"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:54
msgid "Whether to refresh content after startup"
msgstr "Indításkor frissüljön-e a tartalom"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:60
msgid "How many periods of time to wait between automatic cleanups"
msgstr "Mennyi időközt várjon az automatikus tisztítások között"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:64
msgid "What period of time to wait between automatic cleanups"
msgstr "Milyen időközöket várjon az automatikus tisztítások között"
#. Weird magic I copy-pasted that sets the Application Name in the Shell.
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:3
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:4
#: podcasts-gtk/src/app.rs:91 podcasts-gtk/src/app.rs:414
#: podcasts-gtk/src/widgets/aboutdialog.rs:31
msgid "Podcasts"
msgstr "Podcastok"
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:4
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:10
msgid "Listen to your favorite podcasts, right from your desktop."
msgstr "Hallgassa kedvenc podcastjait, közvetlenül az asztalán."
#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:6
msgid "@icon@"
msgstr "@icon@"
#. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:13
msgid "Podcast;RSS;"
msgstr "Podcast;RSS;"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:8
msgid "Podcast app for GNOME"
msgstr "Podcast alkalmazás a GNOME-hoz"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:51
msgid "Jordan Petridis"
msgstr "Jordan Petridis"
#: podcasts-gtk/resources/gtk/empty_view.ui:46
msgid "This show does not have episodes yet"
msgstr "Ennek a műsornak még nincsenek epizódjai"
#: podcasts-gtk/resources/gtk/empty_view.ui:62
msgid "If you think this is an error, please consider writing a bug report."
msgstr "Ha úgy gondolja hogy ez hiba, akkor írjon egy hibajelentést."
#: podcasts-gtk/resources/gtk/empty_view.ui:106
msgid "Get some shows"
msgstr "Szerezzen be néhány műsort"
#: podcasts-gtk/resources/gtk/empty_view.ui:143
msgid "Add new shows via feed URL"
msgstr "Új műsorok hozzáadása csatorna URL-ből"
#: podcasts-gtk/resources/gtk/empty_view.ui:172
msgid "Import shows from another device"
msgstr "Műsorok importálása egy másik eszközről"
#: podcasts-gtk/resources/gtk/episode_widget.ui:178
msgid "Calculating episode size…"
msgstr "Epizódméret kiszámítása…"
#: podcasts-gtk/resources/gtk/episode_widget.ui:214
msgid "Cancel"
msgstr "Mégse"
#: podcasts-gtk/resources/gtk/episode_widget.ui:232
msgid "Download this episode"
msgstr "Ezen epizód letöltése"
#: podcasts-gtk/resources/gtk/episode_widget.ui:256
msgid "Play this episode"
msgstr "Ezen epizód lejátszása"
#: podcasts-gtk/resources/gtk/hamburger.ui:7
msgid "_Check for New Episodes"
msgstr "Új epizódok _keresése"
#: podcasts-gtk/resources/gtk/hamburger.ui:12
msgid "_Import Shows"
msgstr "Műsorok _importálása"
#: podcasts-gtk/resources/gtk/hamburger.ui:22
msgid "_Preferences"
msgstr "_Beállítások"
#: podcasts-gtk/resources/gtk/hamburger.ui:29
msgid "_Keyboard Shortcuts"
msgstr "Gyorsbille_ntyűk"
#: podcasts-gtk/resources/gtk/hamburger.ui:37
msgid "_About"
msgstr "_Névjegy"
#: podcasts-gtk/resources/gtk/headerbar.ui:35
#: podcasts-gtk/resources/gtk/headerbar.ui:189
msgid "Add a new feed"
msgstr "Új csatorna hozzáadása"
#: podcasts-gtk/resources/gtk/headerbar.ui:53
msgid "Enter feed address to add"
msgstr "Adja meg a hozzáadandó csatorna címét"
#: podcasts-gtk/resources/gtk/headerbar.ui:89
msgid "Add"
msgstr "Hozzáadás"
#: podcasts-gtk/resources/gtk/headerbar.ui:133
msgid "You are already subscribed to that feed!"
msgstr "Már feliratkozott erre a csatornára."
#: podcasts-gtk/resources/gtk/headerbar.ui:169
msgid "Show Title"
msgstr "Műsor címe"
#: podcasts-gtk/resources/gtk/headerbar.ui:210
msgid "Back"
msgstr "Vissza"
#: podcasts-gtk/resources/gtk/help-overlay.ui:12
msgid "General"
msgstr "Általános"
#: podcasts-gtk/resources/gtk/help-overlay.ui:18
msgctxt "shortcut window"
msgid "Check for new episodes"
msgstr "Új epizódok keresése"
#: podcasts-gtk/resources/gtk/help-overlay.ui:25
msgctxt "shortcut window"
msgid "Preferences"
msgstr "Beállítások"
#: podcasts-gtk/resources/gtk/help-overlay.ui:32
msgctxt "shortcut window"
msgid "Quit the application"
msgstr "Kilépés az alkalmazásból"
#: podcasts-gtk/resources/gtk/home_view.ui:56
msgid "Today"
msgstr "Ma"
#: podcasts-gtk/resources/gtk/home_view.ui:112
msgid "Yesterday"
msgstr "Tegnap"
#: podcasts-gtk/resources/gtk/home_view.ui:168
msgid "This Week"
msgstr "Ezen a héten"
#: podcasts-gtk/resources/gtk/home_view.ui:224
msgid "This Month"
msgstr "Ebben a hónapban"
#: podcasts-gtk/resources/gtk/home_view.ui:281
msgid "Older"
msgstr "Régebbi"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:101
msgid "An in-app action notification"
msgstr "Alkalmazáson belüli műveletértesítések"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:112
msgid "Undo"
msgstr "Visszavonás"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:72
msgid "Rewind 10 seconds"
msgstr "Visszatekerés 10 másodperccel"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:87
msgid "Play"
msgstr "Lejátszás"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:103
msgid "Pause"
msgstr "Szünet"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:119
msgid "Fast forward 10 seconds"
msgstr "Előretekerés 10 másodperccel"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:285
msgid "Change the playback speed"
msgstr "Lejátszási sebesség módosítása"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:300
#: podcasts-gtk/resources/gtk/player_toolbar.ui:380
msgid "1.00×"
msgstr "1,00×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:344
msgid "1.50×"
msgstr "1,50×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:348
msgid "1.5 speed rate"
msgstr "1,5-szeres sebesség"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:362
msgid "1.25×"
msgstr "1,25×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:366
msgid "1.25 speed rate"
msgstr "1,25-szörös sebesség"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:384
msgid "Normal speed"
msgstr "Normál sebesség"
#: podcasts-gtk/resources/gtk/prefs.ui:42
#: podcasts-gtk/resources/gtk/prefs.ui:295
msgid "Preferences"
msgstr "Beállítások"
#: podcasts-gtk/resources/gtk/prefs.ui:76
msgid "Appearance"
msgstr "Megjelenés"
#: podcasts-gtk/resources/gtk/prefs.ui:120
msgid "Dark Theme"
msgstr "Sötét téma"
#: podcasts-gtk/resources/gtk/prefs.ui:166
msgid "Delete played episodes"
msgstr "Lejátszott epizódok törlése"
#: podcasts-gtk/resources/gtk/prefs.ui:211
msgid "After"
msgstr "Később"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:7
msgid "_Mark All Episodes as Played"
msgstr "Összes epizód meg_jelölése lejátszottként"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:11
msgid "_Website"
msgstr "_Webhely"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:15
msgid "_Unsubscribe"
msgstr "_Leiratkozás"
#: podcasts-gtk/resources/gtk/show_menu.ui:51
msgid "Open Website"
msgstr "Webhely megnyitása"
#: podcasts-gtk/resources/gtk/show_menu.ui:64
msgid "Mark All as Played"
msgstr "Összes megjelölése lejátszottként"
#: podcasts-gtk/resources/gtk/show_menu.ui:89
msgid "Unsubscribe"
msgstr "Leiratkozás"
#: podcasts-gtk/src/app.rs:332
msgid "Fetching new episodes"
msgstr "Új epizódok lekérése"
#: podcasts-gtk/src/headerbar.rs:98
msgid "You are already subscribed to this show"
msgstr "Már feliratkozott erre a műsorra"
#: podcasts-gtk/src/headerbar.rs:106
msgid "Invalid URL"
msgstr "Érvénytelen URL"
#: podcasts-gtk/src/prefs.rs:59
msgid "Seconds"
msgstr "Másodperc"
#: podcasts-gtk/src/prefs.rs:60
msgid "Minutes"
msgstr "Perc"
#: podcasts-gtk/src/prefs.rs:61
msgid "Hours"
msgstr "Óra"
#: podcasts-gtk/src/prefs.rs:62
msgid "Days"
msgstr "Nap"
#: podcasts-gtk/src/prefs.rs:63
msgid "Weeks"
msgstr "Hét"
#: podcasts-gtk/src/stacks/content.rs:35
msgid "New"
msgstr "Új"
#: podcasts-gtk/src/stacks/content.rs:36
msgid "Shows"
msgstr "Műsorok"
#: podcasts-gtk/src/utils.rs:357
msgid "Select the file from which to you want to import shows."
msgstr "Válasszon egy fájlt, amelyből műsorokat akar importálni."
#: podcasts-gtk/src/utils.rs:360
msgid "_Import"
msgstr "_Importálás"
#: podcasts-gtk/src/utils.rs:369
msgid "OPML file"
msgstr "OPML fájl"
#: podcasts-gtk/src/utils.rs:386
msgid "Failed to parse the imported file"
msgstr "Az importált fájl feldolgozása meghiúsult"
#: podcasts-gtk/src/utils.rs:391
msgid "Selected file could not be accessed."
msgstr "A kiválasztott fájlok nem érhetőek el."
#: podcasts-gtk/src/widgets/aboutdialog.rs:26
msgid "Podcast Client for the GNOME Desktop."
msgstr "Podcast-kliens a GNOME asztali környezethez."
#: podcasts-gtk/src/widgets/aboutdialog.rs:33
msgid "Learn more about GNOME Podcasts"
msgstr "További tudnivalók a GNOME Podcastokról"
#: podcasts-gtk/src/widgets/aboutdialog.rs:38
msgid "translator-credits"
msgstr "Meskó Balázs <mesko dot balazs at fsf dot hu>"
#: podcasts-gtk/src/widgets/episode.rs:129
msgid "{} min"
msgstr "{} perc"
#. sender.send(Action::ErrorNotification(format!("Player Error: {}", error)));
#: podcasts-gtk/src/widgets/player.rs:301
msgid "The media player was unable to execute an action."
msgstr "A médialejátszó nem tudott végrehajtani egy műveletet."
#: podcasts-gtk/src/widgets/show_menu.rs:150
msgid "Marked all episodes as listened"
msgstr "Összes epizód megjelölve meghallgatottként"
#: podcasts-gtk/src/widgets/show_menu.rs:155
msgid "Unsubscribed from {}"
msgstr "Leiratkozott innen: {}"
+412
View File
@@ -0,0 +1,412 @@
# Indonesian translation for podcasts.
# Copyright (C) 2018 podcasts's COPYRIGHT HOLDER
# This file is distributed under the same license as the podcasts package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: podcasts master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/podcasts/issues\n"
"POT-Creation-Date: 2018-09-28 23:51+0000\n"
"PO-Revision-Date: 2018-10-02 20:34+0700\n"
"Last-Translator: Kukuh Syafaat <kukuhsyafaat@gnome.org>\n"
"Language-Team: Indonesian <gnome-l10n-id@googlegroups.com>\n"
"Language: id\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 2.0.6\n"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:15
msgid "Top position of the last open main window"
msgstr "Posisi atas dari jendela utama terbuka terakhir"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:19
msgid "Left position of the last open main window"
msgstr "Posisi kiri dari jendela utama terbuka terakhir"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:23
msgid "Height of the last open main window"
msgstr "Tinggi dari jendela utama terbuka terakhir"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:27
msgid "Width of the last open main window"
msgstr "Lebar dari jendela utama terbuka terakhir"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:31
msgid "Maximized state of the last open main window"
msgstr "Keadaan dimaksimalkan dari jendela utama terbuka terakhir"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:36
msgid "Enable or disable dark theme"
msgstr "Aktifkan atau nonaktifkan tema gelap"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:41
msgid "Whether to periodically refresh content"
msgstr "Apakah akan menyegarkan konten secara berkala"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:46
msgid "How many periods of time to wait between automatic refreshes"
msgstr "Berapa banyak periode waktu untuk menunggu antara penyegaran otomatis"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:50
msgid "What period of time to wait between automatic refreshes"
msgstr "Berapa lama waktu untuk menunggu antara penyegaran otomatis"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:54
msgid "Whether to refresh content after startup"
msgstr "Apakah akan menyegarkan konten setelah awal mula"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:60
msgid "How many periods of time to wait between automatic cleanups"
msgstr "Berapa lama waktu menunggu antara pembersihan otomatis"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:64
msgid "What period of time to wait between automatic cleanups"
msgstr "Berapa lama waktu untuk menunggu di antara pembersihan otomatis"
#. Weird magic I copy-pasted that sets the Application Name in the Shell.
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:3
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:4
#: podcasts-gtk/src/app.rs:91 podcasts-gtk/src/app.rs:414
#: podcasts-gtk/src/widgets/aboutdialog.rs:31
msgid "Podcasts"
msgstr "Podcast"
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:4
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:10
msgid "Listen to your favorite podcasts, right from your desktop."
msgstr "Dengarkan podcast favorit Anda, langsung dari destop Anda."
#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:6
msgid "@icon@"
msgstr "@icon@"
#. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:13
msgid "Podcast;RSS;"
msgstr "Podcast;RSS;"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:8
msgid "Podcast app for GNOME"
msgstr "Aplikasi Podcast untuk GNOME"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:51
msgid "Jordan Petridis"
msgstr "Jordan Petridis"
#: podcasts-gtk/resources/gtk/empty_view.ui:46
msgid "This show does not have episodes yet"
msgstr "Acara ini belum memiliki episode"
#: podcasts-gtk/resources/gtk/empty_view.ui:62
msgid "If you think this is an error, please consider writing a bug report."
msgstr ""
"Jika Anda pikir ini adalah kesalahan, silakan mempertimbangkan untuk menulis "
"laporan kutu."
#: podcasts-gtk/resources/gtk/empty_view.ui:106
msgid "Get some shows"
msgstr "Dapatkan beberapa acara"
#: podcasts-gtk/resources/gtk/empty_view.ui:143
msgid "Add new shows via feed URL"
msgstr "Tambahkan acara baru melalui URL feed"
#: podcasts-gtk/resources/gtk/empty_view.ui:172
msgid "Import shows from another device"
msgstr "Impor acara dari perangkat lain"
#: podcasts-gtk/resources/gtk/episode_widget.ui:180
msgid "Calculating episode size…"
msgstr "Menghitung ukuran episode…"
#: podcasts-gtk/resources/gtk/episode_widget.ui:220
msgid "Play this episode"
msgstr "Putar episode ini"
#: podcasts-gtk/resources/gtk/episode_widget.ui:241
msgid "Cancel the download proccess"
msgstr "Batalkan proses pengunduhan"
#: podcasts-gtk/resources/gtk/episode_widget.ui:264
msgid "Download this episode"
msgstr "Unduh episode ini"
#: podcasts-gtk/resources/gtk/hamburger.ui:7
msgid "_Check for New Episodes"
msgstr "Periksa Episode _Baru"
#: podcasts-gtk/resources/gtk/hamburger.ui:12
msgid "_Import Shows"
msgstr "_Impor Acara"
#: podcasts-gtk/resources/gtk/hamburger.ui:22
msgid "_Preferences"
msgstr "_Preferensi"
#: podcasts-gtk/resources/gtk/hamburger.ui:27
msgid "_Keyboard Shortcuts"
msgstr "Pintasan Papan _Ketik"
#: podcasts-gtk/resources/gtk/hamburger.ui:35
msgid "_About Podcasts"
msgstr "Tent_ang Podcast"
#: podcasts-gtk/resources/gtk/headerbar.ui:35
#: podcasts-gtk/resources/gtk/headerbar.ui:189
msgid "Add a new feed"
msgstr "Tambahkan feed baru"
#: podcasts-gtk/resources/gtk/headerbar.ui:53
msgid "Enter feed address to add"
msgstr "Masukkan alamat feed untuk ditambahkan"
#: podcasts-gtk/resources/gtk/headerbar.ui:89
msgid "Add"
msgstr "Tambah"
#: podcasts-gtk/resources/gtk/headerbar.ui:133
msgid "You are already subscribed to that feed!"
msgstr "Anda sudah berlangganan feed itu!"
#: podcasts-gtk/resources/gtk/headerbar.ui:169
msgid "Show Title"
msgstr "Tampilkan Judul"
#: podcasts-gtk/resources/gtk/headerbar.ui:210
msgid "Back"
msgstr "Kembali"
#: podcasts-gtk/resources/gtk/help-overlay.ui:12
msgid "General"
msgstr "Umum"
#: podcasts-gtk/resources/gtk/help-overlay.ui:18
msgctxt "shortcut window"
msgid "Check for new episodes"
msgstr "Periksa episode baru"
#: podcasts-gtk/resources/gtk/help-overlay.ui:25
msgctxt "shortcut window"
msgid "Preferences"
msgstr "Preferensi"
#: podcasts-gtk/resources/gtk/help-overlay.ui:32
msgctxt "shortcut window"
msgid "Quit the application"
msgstr "Keluar dari aplikasi"
#: podcasts-gtk/resources/gtk/home_view.ui:56
msgid "Today"
msgstr "Hari ini"
#: podcasts-gtk/resources/gtk/home_view.ui:112
msgid "Yesterday"
msgstr "Kemarin"
#: podcasts-gtk/resources/gtk/home_view.ui:168
msgid "This Week"
msgstr "Minggu Ini"
#: podcasts-gtk/resources/gtk/home_view.ui:224
msgid "This Month"
msgstr "Bulan ini"
#: podcasts-gtk/resources/gtk/home_view.ui:281
msgid "Older"
msgstr "Lebih lama"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:101
msgid "An in-app action notification"
msgstr "Notifikasi aksi dalam aplikasi"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:112
msgid "Undo"
msgstr "Tak Jadi"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:72
msgid "Rewind 10 seconds"
msgstr "Putar mundur 10 detik"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:87
msgid "Play"
msgstr "Putar"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:103
msgid "Pause"
msgstr "Jeda"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:119
msgid "Fast forward 10 seconds"
msgstr "Maju cepat 10 detik"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:285
msgid "Change the playback speed"
msgstr "Ubah kecepatan pemutaran"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:300
#: podcasts-gtk/resources/gtk/player_toolbar.ui:380
msgid "1.00×"
msgstr "Ubah kecepatan pemutaran"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:344
msgid "1.50×"
msgstr "1.50×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:348
msgid "1.5 speed rate"
msgstr "tingkat kecepatan 1.5"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:362
msgid "1.25×"
msgstr "1.25×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:366
msgid "1.25 speed rate"
msgstr "tingkat kecepatan 1.25"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:384
msgid "Normal speed"
msgstr "Kecepatan normal"
#: podcasts-gtk/resources/gtk/prefs.ui:42
#: podcasts-gtk/resources/gtk/prefs.ui:295
msgid "Preferences"
msgstr "Preferensi"
#: podcasts-gtk/resources/gtk/prefs.ui:76
msgid "Appearance"
msgstr "Penampilan"
#: podcasts-gtk/resources/gtk/prefs.ui:120
msgid "Dark Theme"
msgstr "Tema Gelap"
#: podcasts-gtk/resources/gtk/prefs.ui:166
msgid "Delete played episodes"
msgstr "Hapus episode yang diputar"
#: podcasts-gtk/resources/gtk/prefs.ui:211
msgid "After"
msgstr "Sesudah"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:7
msgid "_Mark All Episodes as Played"
msgstr "Tandai Se_mua Episode sebagai Sudah Diputar"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:11
msgid "_Website"
msgstr "Situs _Web"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:15
msgid "_Unsubscribe"
msgstr "_Berhenti Berlangganan"
#: podcasts-gtk/resources/gtk/show_menu.ui:51
msgid "Open Website"
msgstr "Buka Situs Web"
#: podcasts-gtk/resources/gtk/show_menu.ui:64
msgid "Mark All as Played"
msgstr "Tandai Semua sebagai Sudah Diputar"
#: podcasts-gtk/resources/gtk/show_menu.ui:89
msgid "Unsubscribe"
msgstr "Berhenti Berlangganan"
#: podcasts-gtk/src/app.rs:332
msgid "Fetching new episodes"
msgstr "Mengambil episode baru"
#: podcasts-gtk/src/headerbar.rs:98
msgid "You are already subscribed to this show"
msgstr "Anda sudah berlangganan ke acara ini"
#: podcasts-gtk/src/headerbar.rs:106
msgid "Invalid URL"
msgstr "URL tak valid"
#: podcasts-gtk/src/prefs.rs:59
msgid "Seconds"
msgstr "Detik"
#: podcasts-gtk/src/prefs.rs:60
msgid "Minutes"
msgstr "Menit"
#: podcasts-gtk/src/prefs.rs:61
msgid "Hours"
msgstr "Jam"
#: podcasts-gtk/src/prefs.rs:62
msgid "Days"
msgstr "Hari"
#: podcasts-gtk/src/prefs.rs:63
msgid "Weeks"
msgstr "Minggu"
#: podcasts-gtk/src/stacks/content.rs:35
msgid "New"
msgstr "Baru"
#: podcasts-gtk/src/stacks/content.rs:36
msgid "Shows"
msgstr "Acara"
#: podcasts-gtk/src/utils.rs:357
msgid "Select the file from which to you want to import shows."
msgstr "Pilih berkas dari mana Anda ingin mengimpor acara."
#: podcasts-gtk/src/utils.rs:360
msgid "_Import"
msgstr "_Impor"
#: podcasts-gtk/src/utils.rs:369
msgid "OPML file"
msgstr "Berkas OPML"
#: podcasts-gtk/src/utils.rs:386
msgid "Failed to parse the imported file"
msgstr "Gagal mengurai berkas yang diimpor"
#: podcasts-gtk/src/utils.rs:391
msgid "Selected file could not be accessed."
msgstr "Berkas yang dipilih tidak dapat diakses."
#: podcasts-gtk/src/widgets/aboutdialog.rs:26
msgid "Podcast Client for the GNOME Desktop."
msgstr "Klien Podcast untuk Destop GNOME."
#: podcasts-gtk/src/widgets/aboutdialog.rs:33
msgid "Learn more about GNOME Podcasts"
msgstr "Pelajari lebih lanjut tentang GNOME Podcast"
#: podcasts-gtk/src/widgets/aboutdialog.rs:38
msgid "translator-credits"
msgstr "Kukuh Syafaat <kukuhsyafaat@gnome.org>, 2018."
#: podcasts-gtk/src/widgets/episode.rs:130
msgid "{} min"
msgstr "{} mnt"
#. sender.send(Action::ErrorNotification(format!("Player Error: {}", error)));
#: podcasts-gtk/src/widgets/player.rs:301
msgid "The media player was unable to execute an action."
msgstr "Pemutar media tidak dapat melakukan aksi."
#: podcasts-gtk/src/widgets/show_menu.rs:150
msgid "Marked all episodes as listened"
msgstr "Tandai semua episode sebagai sudah didengarkan"
#: podcasts-gtk/src/widgets/show_menu.rs:155
msgid "Unsubscribed from {}"
msgstr "Berhenti berlangganan dari {}"
#~ msgid "_About"
#~ msgstr "Tent_ang"
#~ msgid "Cancel"
#~ msgstr "Batal"
+405
View File
@@ -0,0 +1,405 @@
# Italian translation for podcasts.
# Copyright (C) 2018 podcasts's Free Software Foundation, Inc.
# This file is distributed under the same license as the podcasts package.
# Milo Casagrande <milo@milo.name>, 2018.
#
msgid ""
msgstr ""
"Project-Id-Version: podcasts master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/podcasts/issues\n"
"POT-Creation-Date: 2018-09-03 11:31+0000\n"
"PO-Revision-Date: 2018-09-03 15:41+0200\n"
"Language-Team: Italian <gnome-it-list@gnome.org>\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Last-Translator: Milo Casagrande <milo@milo.name>\n"
"X-Generator: Poedit 2.1.1\n"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:15
msgid "Top position of the last open main window"
msgstr "Posizione superiore dell'ultima finestra principale aperta"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:19
msgid "Left position of the last open main window"
msgstr "Posizione sinistra dell'ultima finestra principale aperta"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:23
msgid "Height of the last open main window"
msgstr "Altezza dell'ultima finestra principale aperta"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:27
msgid "Width of the last open main window"
msgstr "Larghezza dell'ultima finestra principale aperta"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:31
msgid "Maximized state of the last open main window"
msgstr "Stato di massimizzazione dell'ultima finestra principale aperta"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:36
msgid "Enable or disable dark theme"
msgstr "Abilita/Disabilita il tema scuro"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:41
msgid "Whether to periodically refresh content"
msgstr "Indica se aggiornare i contenuti periodicamente"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:46
msgid "How many periods of time to wait between automatic refreshes"
msgstr "Quanto tempo aspettare tra gli aggiornamenti automatici"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:50
msgid "What period of time to wait between automatic refreshes"
msgstr "Che periodo di tempo aspettare tra gli aggiornamenti automatici"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:54
msgid "Whether to refresh content after startup"
msgstr "Indica se aggiornare i contenuti dopo l'avvio"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:60
msgid "How many periods of time to wait between automatic cleanups"
msgstr "Quanto tempo aspettare tra la pulizia automatica"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:64
msgid "What period of time to wait between automatic cleanups"
msgstr "Che periodi di tempo aspettare tra la pulizia automatica"
#. Weird magic I copy-pasted that sets the Application Name in the Shell.
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:3
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:4
#: podcasts-gtk/src/app.rs:91 podcasts-gtk/src/app.rs:414
#: podcasts-gtk/src/widgets/aboutdialog.rs:31
msgid "Podcasts"
msgstr "Podcast"
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:4
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:10
msgid "Listen to your favorite podcasts, right from your desktop."
msgstr "Ascoltare i propri podcast preferiti, direttamente proprio computer."
#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:6
msgid "@icon@"
msgstr "@icon@"
#. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:13
msgid "Podcast;RSS;"
msgstr "Podcast;RSS;"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:8
msgid "Podcast app for GNOME"
msgstr "Applicazione per podcast per GNOME"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:51
msgid "Jordan Petridis"
msgstr "Jordan Petridis"
#: podcasts-gtk/resources/gtk/empty_view.ui:46
msgid "This show does not have episodes yet"
msgstr "Questo show non ha ancora episodi"
#: podcasts-gtk/resources/gtk/empty_view.ui:62
msgid "If you think this is an error, please consider writing a bug report."
msgstr "Se considerate questo evento un errore, inviare una segnalazione."
#: podcasts-gtk/resources/gtk/empty_view.ui:106
msgid "Get some shows"
msgstr "Ottieni show"
#: podcasts-gtk/resources/gtk/empty_view.ui:143
msgid "Add new shows via feed URL"
msgstr "Aggiungi nuovi show via URL"
#: podcasts-gtk/resources/gtk/empty_view.ui:172
msgid "Import shows from another device"
msgstr "Importa show da un altro dispositivo"
#: podcasts-gtk/resources/gtk/episode_widget.ui:178
msgid "Calculating episode size…"
msgstr "Calcolo dimensione episodio…"
#: podcasts-gtk/resources/gtk/episode_widget.ui:214
msgid "Cancel"
msgstr "Annulla"
#: podcasts-gtk/resources/gtk/episode_widget.ui:232
msgid "Download this episode"
msgstr "Scarica questo episodio"
#: podcasts-gtk/resources/gtk/episode_widget.ui:256
msgid "Play this episode"
msgstr "Riproduci questo episodio"
#: podcasts-gtk/resources/gtk/hamburger.ui:7
msgid "_Check for New Episodes"
msgstr "Controlla per _nuovi episodi"
#: podcasts-gtk/resources/gtk/hamburger.ui:12
msgid "_Import Shows"
msgstr "_Importa show"
#: podcasts-gtk/resources/gtk/hamburger.ui:22
msgid "_Preferences"
msgstr "Preferen_ze"
#: podcasts-gtk/resources/gtk/hamburger.ui:29
msgid "_Keyboard Shortcuts"
msgstr "_Scorciatoie da tastiera"
#: podcasts-gtk/resources/gtk/hamburger.ui:37
msgid "_About"
msgstr "I_nformazioni"
#: podcasts-gtk/resources/gtk/headerbar.ui:35
#: podcasts-gtk/resources/gtk/headerbar.ui:189
msgid "Add a new feed"
msgstr "Aggiungi nuovo feed"
#: podcasts-gtk/resources/gtk/headerbar.ui:53
msgid "Enter feed address to add"
msgstr "Inserire un indirizzo da aggiungere"
#: podcasts-gtk/resources/gtk/headerbar.ui:89
msgid "Add"
msgstr "Aggiungi"
#: podcasts-gtk/resources/gtk/headerbar.ui:133
msgid "You are already subscribed to that feed!"
msgstr "Si è già abbonati a questo feed."
#: podcasts-gtk/resources/gtk/headerbar.ui:169
msgid "Show Title"
msgstr "Titolo show"
#: podcasts-gtk/resources/gtk/headerbar.ui:210
msgid "Back"
msgstr "Indietro"
#: podcasts-gtk/resources/gtk/help-overlay.ui:12
msgid "General"
msgstr "Generale"
#: podcasts-gtk/resources/gtk/help-overlay.ui:18
msgctxt "shortcut window"
msgid "Check for new episodes"
msgstr "Controlla presenza nuovi episodi"
#: podcasts-gtk/resources/gtk/help-overlay.ui:25
msgctxt "shortcut window"
msgid "Preferences"
msgstr "Preferenze"
#: podcasts-gtk/resources/gtk/help-overlay.ui:32
msgctxt "shortcut window"
msgid "Quit the application"
msgstr "Esce dall'applicazione"
#: podcasts-gtk/resources/gtk/home_view.ui:56
msgid "Today"
msgstr "Oggi"
#: podcasts-gtk/resources/gtk/home_view.ui:112
msgid "Yesterday"
msgstr "Ieri"
#: podcasts-gtk/resources/gtk/home_view.ui:168
msgid "This Week"
msgstr "Questa settimana"
#: podcasts-gtk/resources/gtk/home_view.ui:224
msgid "This Month"
msgstr "Questo mese"
#: podcasts-gtk/resources/gtk/home_view.ui:281
msgid "Older"
msgstr "Più vecchi"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:101
msgid "An in-app action notification"
msgstr "Una notifica"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:112
msgid "Undo"
msgstr "Annulla"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:72
msgid "Rewind 10 seconds"
msgstr "Indietro 10 secondi"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:87
msgid "Play"
msgstr "Riproduci"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:103
msgid "Pause"
msgstr "Pausa"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:119
msgid "Fast forward 10 seconds"
msgstr "Avanti veloce 10 secondi"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:285
msgid "Change the playback speed"
msgstr "Modifica la velocità di riproduzione"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:300
#: podcasts-gtk/resources/gtk/player_toolbar.ui:380
msgid "1.00×"
msgstr "1,00×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:344
msgid "1.50×"
msgstr "1,50×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:348
msgid "1.5 speed rate"
msgstr "1,5 volte la velocità"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:362
msgid "1.25×"
msgstr "1,25×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:366
msgid "1.25 speed rate"
msgstr "1,25 volte la velocità"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:384
msgid "Normal speed"
msgstr "Velocità normale"
#: podcasts-gtk/resources/gtk/prefs.ui:42
#: podcasts-gtk/resources/gtk/prefs.ui:295
msgid "Preferences"
msgstr "Preferenze"
#: podcasts-gtk/resources/gtk/prefs.ui:76
msgid "Appearance"
msgstr "Aspetto"
#: podcasts-gtk/resources/gtk/prefs.ui:120
msgid "Dark Theme"
msgstr "Tema scuro"
#: podcasts-gtk/resources/gtk/prefs.ui:166
msgid "Delete played episodes"
msgstr "Eliminare episodi ascoltati"
#: podcasts-gtk/resources/gtk/prefs.ui:211
msgid "After"
msgstr "Dopo"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:7
msgid "_Mark All Episodes as Played"
msgstr "_Segna tutti gli episodi come ascoltati"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:11
msgid "_Website"
msgstr "Sito _web"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:15
msgid "_Unsubscribe"
msgstr "_Annulla abbonamento"
#: podcasts-gtk/resources/gtk/show_menu.ui:51
msgid "Open Website"
msgstr "Apri sito web"
#: podcasts-gtk/resources/gtk/show_menu.ui:64
msgid "Mark All as Played"
msgstr "Segna tutti ascoltati"
#: podcasts-gtk/resources/gtk/show_menu.ui:89
msgid "Unsubscribe"
msgstr "Annulla abbonamento"
#: podcasts-gtk/src/app.rs:332
msgid "Fetching new episodes"
msgstr "Scaricamento nuovi episodi"
#: podcasts-gtk/src/headerbar.rs:98
msgid "You are already subscribed to this show"
msgstr "Si è già abbonati a questo show"
#: podcasts-gtk/src/headerbar.rs:106
msgid "Invalid URL"
msgstr "URL non valido"
#: podcasts-gtk/src/prefs.rs:59
msgid "Seconds"
msgstr "secondi"
#: podcasts-gtk/src/prefs.rs:60
msgid "Minutes"
msgstr "Minuti"
#: podcasts-gtk/src/prefs.rs:61
msgid "Hours"
msgstr "Ore"
#: podcasts-gtk/src/prefs.rs:62
msgid "Days"
msgstr "Giorni"
#: podcasts-gtk/src/prefs.rs:63
msgid "Weeks"
msgstr "Settimane"
#: podcasts-gtk/src/stacks/content.rs:35
msgid "New"
msgstr "Nuovo"
#: podcasts-gtk/src/stacks/content.rs:36
msgid "Shows"
msgstr "Show"
#: podcasts-gtk/src/utils.rs:357
msgid "Select the file from which to you want to import shows."
msgstr "Selezionare il file da cui importare gli show."
#: podcasts-gtk/src/utils.rs:360
msgid "_Import"
msgstr "_Importa"
#: podcasts-gtk/src/utils.rs:369
msgid "OPML file"
msgstr "File OPML"
#: podcasts-gtk/src/utils.rs:386
msgid "Failed to parse the imported file"
msgstr "Analisi del file importato non riuscita"
#: podcasts-gtk/src/utils.rs:391
msgid "Selected file could not be accessed."
msgstr "Impossibile accedere al file selezionato."
#: podcasts-gtk/src/widgets/aboutdialog.rs:26
msgid "Podcast Client for the GNOME Desktop."
msgstr "Client podcast per l'ambiente grafico GNOME."
#: podcasts-gtk/src/widgets/aboutdialog.rs:33
msgid "Learn more about GNOME Podcasts"
msgstr "Maggiori informazioni su Podcast GNOME"
#: podcasts-gtk/src/widgets/aboutdialog.rs:38
msgid "translator-credits"
msgstr "Milo Casagrande <milo@milo.name>"
#: podcasts-gtk/src/widgets/episode.rs:129
msgid "{} min"
msgstr "{} min"
#. sender.send(Action::ErrorNotification(format!("Player Error: {}", error)));
#: podcasts-gtk/src/widgets/player.rs:301
msgid "The media player was unable to execute an action."
msgstr "Il riproduttore multimediale non può eseguire un'azione."
#: podcasts-gtk/src/widgets/show_menu.rs:150
msgid "Marked all episodes as listened"
msgstr "Episodi segnati come letti"
#: podcasts-gtk/src/widgets/show_menu.rs:155
msgid "Unsubscribed from {}"
msgstr "Abbonamento a {} annullato"
+405
View File
@@ -0,0 +1,405 @@
# Korean translation for podcasts.
# Copyright (C) 2018 podcasts's COPYRIGHT HOLDER
# This file is distributed under the same license as the podcasts package.
# Seong-ho Cho <shcho@gnome.org>, 2018.
#
msgid ""
msgstr ""
"Project-Id-Version: podcasts master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/podcasts/issues\n"
"POT-Creation-Date: 2018-08-30 10:43+0000\n"
"PO-Revision-Date: 2018-08-30 21:26+0900\n"
"Language-Team: Korean <gnome-kr@googlegroups.com>\n"
"Language: ko\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"Last-Translator: Seong-ho Cho <shcho@gnome.org>\n"
"X-Generator: Poedit 2.0.6\n"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:15
msgid "Top position of the last open main window"
msgstr "최근 연 메인 창 상단 위치"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:19
msgid "Left position of the last open main window"
msgstr "최근 연 메인 창 좌측 위치"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:23
msgid "Height of the last open main window"
msgstr "최근 연 메인 창 높이"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:27
msgid "Width of the last open main window"
msgstr "최근 연 메인 창 너비"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:31
msgid "Maximized state of the last open main window"
msgstr "최근 연 메인 창 최대화 상태"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:36
msgid "Enable or disable dark theme"
msgstr "어두움 테마 사용 함/안함"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:41
msgid "Whether to periodically refresh content"
msgstr "주기적으로 내용 새로 고침 여부"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:46
msgid "How many periods of time to wait between automatic refreshes"
msgstr "자동 새로 고침 기다림 주기 시간 간격"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:50
msgid "What period of time to wait between automatic refreshes"
msgstr "자동 새로 고침 기다림 주기 시간"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:54
msgid "Whether to refresh content after startup"
msgstr "시작 후 내용 새로 고침 여부"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:60
msgid "How many periods of time to wait between automatic cleanups"
msgstr "자동 정리 기다림 주기 시간 간격"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:64
msgid "What period of time to wait between automatic cleanups"
msgstr "자동 정리 기다림 주기 시간"
#. Weird magic I copy-pasted that sets the Application Name in the Shell.
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:3
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:4
#: podcasts-gtk/src/app.rs:91 podcasts-gtk/src/app.rs:414
#: podcasts-gtk/src/widgets/aboutdialog.rs:31
msgid "Podcasts"
msgstr "팟캐스트"
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:4
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:10
msgid "Listen to your favorite podcasts, right from your desktop."
msgstr "데스크톱에서 바로 원하는 팟캐스트를 듣습니다."
#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:6
msgid "@icon@"
msgstr "@icon@"
#. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:13
msgid "Podcast;RSS;"
msgstr "Podcast;팟캐스트;RSS;"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:8
msgid "Podcast app for GNOME"
msgstr "그놈용 팟캐스트 앱"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:31
msgid "Jordan Petridis"
msgstr "Jordan Petridis"
#: podcasts-gtk/resources/gtk/empty_view.ui:46
msgid "This show does not have episodes yet"
msgstr "아직 보여줄 에피소드가 없습니다"
#: podcasts-gtk/resources/gtk/empty_view.ui:62
msgid "If you think this is an error, please consider writing a bug report."
msgstr "오류인 것 같다면 오류 보고서 작성을 고려하십시오."
#: podcasts-gtk/resources/gtk/empty_view.ui:106
msgid "Get some shows"
msgstr "일부 쇼 가져오기"
#: podcasts-gtk/resources/gtk/empty_view.ui:143
msgid "Add new shows via feed URL"
msgstr "피드 URL 주소로 새 쇼 추가"
#: podcasts-gtk/resources/gtk/empty_view.ui:172
msgid "Import shows from another device"
msgstr "다른 장치에서 쇼 가져오기"
#: podcasts-gtk/resources/gtk/episode_widget.ui:177
msgid "Calculating episode size…"
msgstr "에피소드 용량 계산 중…"
#: podcasts-gtk/resources/gtk/episode_widget.ui:212
msgid "Cancel"
msgstr "취소"
#: podcasts-gtk/resources/gtk/episode_widget.ui:230
msgid "Download this episode"
msgstr "이 에피소드 다운로드"
#: podcasts-gtk/resources/gtk/episode_widget.ui:254
msgid "Play this episode"
msgstr "이 에피소드 재생"
#: podcasts-gtk/resources/gtk/hamburger.ui:7
msgid "_Check for New Episodes"
msgstr "새 에피소드 확인(_C)"
#: podcasts-gtk/resources/gtk/hamburger.ui:12
msgid "_Import Shows"
msgstr "쇼 가져오기(_I)"
#: podcasts-gtk/resources/gtk/hamburger.ui:22
msgid "_Preferences"
msgstr "기본 설정(_P)"
#: podcasts-gtk/resources/gtk/hamburger.ui:29
msgid "_Keyboard Shortcuts"
msgstr "키보드 바로 가기 키(_K)"
#: podcasts-gtk/resources/gtk/hamburger.ui:37
msgid "_About"
msgstr "정보(_A)"
#: podcasts-gtk/resources/gtk/headerbar.ui:35
#: podcasts-gtk/resources/gtk/headerbar.ui:189
msgid "Add a new feed"
msgstr "새 피드 추가"
#: podcasts-gtk/resources/gtk/headerbar.ui:53
msgid "Enter feed address to add"
msgstr "추가할 피드 주소를 입력하십시오"
#: podcasts-gtk/resources/gtk/headerbar.ui:89
msgid "Add"
msgstr "추가"
#: podcasts-gtk/resources/gtk/headerbar.ui:133
msgid "You are already subscribed to that feed!"
msgstr "해당 피드에 이미 가입했습니다!"
#: podcasts-gtk/resources/gtk/headerbar.ui:169
msgid "Show Title"
msgstr "제목 표시"
#: podcasts-gtk/resources/gtk/headerbar.ui:210
msgid "Back"
msgstr "뒤로"
#: podcasts-gtk/resources/gtk/help-overlay.ui:12
msgid "General"
msgstr "일반"
#: podcasts-gtk/resources/gtk/help-overlay.ui:18
msgctxt "shortcut window"
msgid "Check for new episodes"
msgstr "새 에피소드 확인"
#: podcasts-gtk/resources/gtk/help-overlay.ui:25
msgctxt "shortcut window"
msgid "Preferences"
msgstr "기본 설정"
#: podcasts-gtk/resources/gtk/help-overlay.ui:32
msgctxt "shortcut window"
msgid "Quit the application"
msgstr "프로그램 끝내기"
#: podcasts-gtk/resources/gtk/home_view.ui:56
msgid "Today"
msgstr "오늘"
#: podcasts-gtk/resources/gtk/home_view.ui:112
msgid "Yesterday"
msgstr "어제"
#: podcasts-gtk/resources/gtk/home_view.ui:168
msgid "This Week"
msgstr "금주"
#: podcasts-gtk/resources/gtk/home_view.ui:224
msgid "This Month"
msgstr "이번 달"
#: podcasts-gtk/resources/gtk/home_view.ui:281
msgid "Older"
msgstr "이전"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:101
msgid "An in-app action notification"
msgstr "앱 동작 알림"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:112
msgid "Undo"
msgstr "실행 취소"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:72
msgid "Rewind 10 seconds"
msgstr "10초 되감기"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:87
msgid "Play"
msgstr "재생"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:103
msgid "Pause"
msgstr "일시 정지"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:119
msgid "Fast forward 10 seconds"
msgstr "10초 빨리감기"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:285
msgid "Change the playback speed"
msgstr "재생 속도 조절"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:300
#: podcasts-gtk/resources/gtk/player_toolbar.ui:380
msgid "1.00×"
msgstr "1.00×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:344
msgid "1.50×"
msgstr "1.50×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:348
msgid "1.5 speed rate"
msgstr "1.5배 속도"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:362
msgid "1.25×"
msgstr "1.25×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:366
msgid "1.25 speed rate"
msgstr "1.25배 속도"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:384
msgid "Normal speed"
msgstr "일반 속도"
#: podcasts-gtk/resources/gtk/prefs.ui:42
#: podcasts-gtk/resources/gtk/prefs.ui:295
msgid "Preferences"
msgstr "기본 설정"
#: podcasts-gtk/resources/gtk/prefs.ui:76
msgid "Appearance"
msgstr "모양새"
#: podcasts-gtk/resources/gtk/prefs.ui:120
msgid "Dark Theme"
msgstr "어두움 테마"
#: podcasts-gtk/resources/gtk/prefs.ui:166
msgid "Delete played episodes"
msgstr "재생한 에피소드 삭제"
#: podcasts-gtk/resources/gtk/prefs.ui:211
msgid "After"
msgstr "다음"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:7
msgid "_Mark All Episodes as Played"
msgstr "모든 에피소드를 재생 상태로 표시(_M)"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:11
msgid "_Website"
msgstr "웹사이트(_W)"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:15
msgid "_Unsubscribe"
msgstr "가입 취소(_U)"
#: podcasts-gtk/resources/gtk/show_menu.ui:51
msgid "Open Website"
msgstr "웹사이트 열기"
#: podcasts-gtk/resources/gtk/show_menu.ui:64
msgid "Mark All as Played"
msgstr "모두 재생한 항목으로 표시"
#: podcasts-gtk/resources/gtk/show_menu.ui:89
msgid "Unsubscribe"
msgstr "가입 취소"
#: podcasts-gtk/src/app.rs:332
msgid "Fetching new episodes"
msgstr "새 에피소드 가져오는 중"
#: podcasts-gtk/src/headerbar.rs:98
msgid "You are already subscribed to this show"
msgstr "이미 이 쇼에 가입했습니다"
#: podcasts-gtk/src/headerbar.rs:106
msgid "Invalid URL"
msgstr "잘못된 URL 주소"
#: podcasts-gtk/src/prefs.rs:59
msgid "Seconds"
msgstr "초"
#: podcasts-gtk/src/prefs.rs:60
msgid "Minutes"
msgstr "분"
#: podcasts-gtk/src/prefs.rs:61
msgid "Hours"
msgstr "시간"
#: podcasts-gtk/src/prefs.rs:62
msgid "Days"
msgstr "일"
#: podcasts-gtk/src/prefs.rs:63
msgid "Weeks"
msgstr "주"
#: podcasts-gtk/src/stacks/content.rs:35
msgid "New"
msgstr "새 항목"
#: podcasts-gtk/src/stacks/content.rs:36
msgid "Shows"
msgstr "쇼"
#: podcasts-gtk/src/utils.rs:357
msgid "Select the file from which to you want to import shows."
msgstr "가져올 쇼의 파일을 선택하십시오."
#: podcasts-gtk/src/utils.rs:360
msgid "_Import"
msgstr "가져오기(_I)"
#: podcasts-gtk/src/utils.rs:369
msgid "OPML file"
msgstr "OPML 파일"
#: podcasts-gtk/src/utils.rs:386
msgid "Failed to parse the imported file"
msgstr "가져온 파일 해석에 실패했습니다"
#: podcasts-gtk/src/utils.rs:391
msgid "Selected file could not be accessed."
msgstr "선택한 파일에 접근할 수 없습니다."
#: podcasts-gtk/src/widgets/aboutdialog.rs:26
msgid "Podcast Client for the GNOME Desktop."
msgstr "그놈 데스크톱용 팟캐스트 클라이언트."
#: podcasts-gtk/src/widgets/aboutdialog.rs:33
msgid "Learn more about GNOME Podcasts"
msgstr "그놈 팟캐스트에 대해 더 알아보세요"
#: podcasts-gtk/src/widgets/aboutdialog.rs:38
msgid "translator-credits"
msgstr "조성호 <shcho@gnome.org>, 2018."
#: podcasts-gtk/src/widgets/episode.rs:129
msgid "{} min"
msgstr "{}분"
#. sender.send(Action::ErrorNotification(format!("Player Error: {}", error)));
#: podcasts-gtk/src/widgets/player.rs:301
msgid "The media player was unable to execute an action."
msgstr "미디어 재생기에서 이 동작을 수행할 수 없습니다."
#: podcasts-gtk/src/widgets/show_menu.rs:150
msgid "Marked all episodes as listened"
msgstr "모든 에피소드를 감청 상태로 표시했습니다"
#: podcasts-gtk/src/widgets/show_menu.rs:155
msgid "Unsubscribed from {}"
msgstr "{}에서 가입 취소했습니다"
+407
View File
@@ -0,0 +1,407 @@
# Latvian translation for podcasts.
# Copyright (C) 2018 podcasts's COPYRIGHT HOLDER
# This file is distributed under the same license as the podcasts package.
#
# Rūdolfs Mazurs <rudolfs.mazurs@gmail.com>, 2018.
msgid ""
msgstr ""
"Project-Id-Version: podcasts master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/podcasts/issues\n"
"POT-Creation-Date: 2018-09-10 07:51+0000\n"
"PO-Revision-Date: 2018-09-10 20:15+0200\n"
"Last-Translator: Rūdolfs Mazurs <rudolfs.mazurs@gmail.com>\n"
"Language-Team: Latvian <lata-l10n@googlegroups.com>\n"
"Language: lv\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 :"
" 2);\n"
"X-Generator: Lokalize 2.0\n"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:15
msgid "Top position of the last open main window"
msgstr "Augšējais novietojums pēdējam atvērtajam galvenajam logam"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:19
msgid "Left position of the last open main window"
msgstr "Kreisais novietojums pēdējam atvērtajam galvenajam logam"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:23
msgid "Height of the last open main window"
msgstr "Pēdējā atvērtā galvenā loga augstums"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:27
msgid "Width of the last open main window"
msgstr "Pēdējā atvērtā galvenā loga platums"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:31
msgid "Maximized state of the last open main window"
msgstr "Pēdējā atvērtā galvenā loga maksimizēšanas stāvoklis"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:36
msgid "Enable or disable dark theme"
msgstr "Ieslēgt vai izslēgt tumšo motīvu"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:41
msgid "Whether to periodically refresh content"
msgstr "Vai ik pa laikam atsvaidzināt saturu"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:46
msgid "How many periods of time to wait between automatic refreshes"
msgstr "Cik laika periodus gaidīt starp automātisko atsvaidzināšanu"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:50
msgid "What period of time to wait between automatic refreshes"
msgstr "Cik ilgi gaidīt starp automātisko atsvaidzināšanu"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:54
msgid "Whether to refresh content after startup"
msgstr "Vai atsvaidzināt saturu pēc palaišanas"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:60
msgid "How many periods of time to wait between automatic cleanups"
msgstr "Cik laika periodus gaidīt starp automātisko uzkopšanu"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:64
msgid "What period of time to wait between automatic cleanups"
msgstr "Cik ilgi gaidīt starp automātisko uzkopšanu"
#. Weird magic I copy-pasted that sets the Application Name in the Shell.
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:3
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:4
#: podcasts-gtk/src/app.rs:91 podcasts-gtk/src/app.rs:414
#: podcasts-gtk/src/widgets/aboutdialog.rs:31
msgid "Podcasts"
msgstr "Podraides"
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:4
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:10
msgid "Listen to your favorite podcasts, right from your desktop."
msgstr "Klausieties savas iecienītās podraides savā darbvirsmā."
#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:6
msgid "@icon@"
msgstr "@icon@"
#. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:13
msgid "Podcast;RSS;"
msgstr "Aplāde;Podraide;Podkāsts;RSS;"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:8
msgid "Podcast app for GNOME"
msgstr "Podraižu lietotne GNOME videi"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:51
msgid "Jordan Petridis"
msgstr "Jordan Petridis"
#: podcasts-gtk/resources/gtk/empty_view.ui:46
msgid "This show does not have episodes yet"
msgstr "Šim raidījumam vēl nav epizožu"
#: podcasts-gtk/resources/gtk/empty_view.ui:62
msgid "If you think this is an error, please consider writing a bug report."
msgstr "Ja domājat, ka šī ir kļūda, apsveriet sūtīt kļūdas ziņojumu."
#: podcasts-gtk/resources/gtk/empty_view.ui:106
msgid "Get some shows"
msgstr "Tikt pie dažiem raidījumiem"
#: podcasts-gtk/resources/gtk/empty_view.ui:143
msgid "Add new shows via feed URL"
msgstr "Pievienojiet jaunus raidījumus ar plūsmu URL"
#: podcasts-gtk/resources/gtk/empty_view.ui:172
msgid "Import shows from another device"
msgstr "Importēt raidījumus no citas ierīces"
#: podcasts-gtk/resources/gtk/episode_widget.ui:180
msgid "Calculating episode size…"
msgstr "Aprēķina epizodes izmēru…"
#: podcasts-gtk/resources/gtk/episode_widget.ui:220
msgid "Play this episode"
msgstr "Atskaņot šo epizodi"
#: podcasts-gtk/resources/gtk/episode_widget.ui:241
msgid "Cancel the download proccess"
msgstr "Atcelt lejupielādēšanu"
#: podcasts-gtk/resources/gtk/episode_widget.ui:264
msgid "Download this episode"
msgstr "Lejupielādēt šo epizodi"
#: podcasts-gtk/resources/gtk/hamburger.ui:7
msgid "_Check for New Episodes"
msgstr "Pārbaudīt jau_nos raidījumus"
#: podcasts-gtk/resources/gtk/hamburger.ui:12
msgid "_Import Shows"
msgstr "_Importēt raidījumus"
#: podcasts-gtk/resources/gtk/hamburger.ui:22
msgid "_Preferences"
msgstr "_Iestatījumi"
#: podcasts-gtk/resources/gtk/hamburger.ui:29
msgid "_Keyboard Shortcuts"
msgstr "_Tastatūras īsinājumtaustiņi"
#: podcasts-gtk/resources/gtk/hamburger.ui:37
msgid "_About"
msgstr "P_ar"
#: podcasts-gtk/resources/gtk/headerbar.ui:35
#: podcasts-gtk/resources/gtk/headerbar.ui:189
msgid "Add a new feed"
msgstr "Pievienot jaunu plūsmu"
#: podcasts-gtk/resources/gtk/headerbar.ui:53
msgid "Enter feed address to add"
msgstr "Ievadiet pievienojamās plūsmas adresi"
#: podcasts-gtk/resources/gtk/headerbar.ui:89
msgid "Add"
msgstr "Pievienot"
#: podcasts-gtk/resources/gtk/headerbar.ui:133
msgid "You are already subscribed to that feed!"
msgstr "Jūs jau esat abonējis šo plūsmu!"
#: podcasts-gtk/resources/gtk/headerbar.ui:169
msgid "Show Title"
msgstr "Rādīt virsrakstu"
#: podcasts-gtk/resources/gtk/headerbar.ui:210
msgid "Back"
msgstr "Atpakaļ"
#: podcasts-gtk/resources/gtk/help-overlay.ui:12
msgid "General"
msgstr "Vispārīgi"
#: podcasts-gtk/resources/gtk/help-overlay.ui:18
msgctxt "shortcut window"
msgid "Check for new episodes"
msgstr "Pārbaudīt jaunos raidījumus"
#: podcasts-gtk/resources/gtk/help-overlay.ui:25
msgctxt "shortcut window"
msgid "Preferences"
msgstr "Iestatījumi"
#: podcasts-gtk/resources/gtk/help-overlay.ui:32
msgctxt "shortcut window"
msgid "Quit the application"
msgstr "Iziet no lietotnes"
#: podcasts-gtk/resources/gtk/home_view.ui:56
msgid "Today"
msgstr "Šodien"
#: podcasts-gtk/resources/gtk/home_view.ui:112
msgid "Yesterday"
msgstr "Vakar"
#: podcasts-gtk/resources/gtk/home_view.ui:168
msgid "This Week"
msgstr "Šonedēļ"
#: podcasts-gtk/resources/gtk/home_view.ui:224
msgid "This Month"
msgstr "Šomēnes"
#: podcasts-gtk/resources/gtk/home_view.ui:281
msgid "Older"
msgstr "Vecākas"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:101
msgid "An in-app action notification"
msgstr "Paziņojumi lietotnē"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:112
msgid "Undo"
msgstr "Atsaukt"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:72
msgid "Rewind 10 seconds"
msgstr "Attīt 10 sekundes"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:87
msgid "Play"
msgstr "Atskaņot"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:103
msgid "Pause"
msgstr "Pauzēt"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:119
msgid "Fast forward 10 seconds"
msgstr "Patīt uz priekšu 10 sekundes"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:285
msgid "Change the playback speed"
msgstr "Mainīt atskaņošanas ātrumu"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:300
#: podcasts-gtk/resources/gtk/player_toolbar.ui:380
msgid "1.00×"
msgstr "1,00×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:344
msgid "1.50×"
msgstr "1,50×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:348
msgid "1.5 speed rate"
msgstr "1,5 ātrums"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:362
msgid "1.25×"
msgstr "1,25×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:366
msgid "1.25 speed rate"
msgstr "1,25 ātrums"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:384
msgid "Normal speed"
msgstr "Normāls ātrums"
#: podcasts-gtk/resources/gtk/prefs.ui:42
#: podcasts-gtk/resources/gtk/prefs.ui:295
msgid "Preferences"
msgstr "Iestatījumi"
#: podcasts-gtk/resources/gtk/prefs.ui:76
msgid "Appearance"
msgstr "Izskats"
#: podcasts-gtk/resources/gtk/prefs.ui:120
msgid "Dark Theme"
msgstr "Tumšais motīvs"
#: podcasts-gtk/resources/gtk/prefs.ui:166
msgid "Delete played episodes"
msgstr "Dzēst atskaņotās epizodes"
#: podcasts-gtk/resources/gtk/prefs.ui:211
msgid "After"
msgstr "Pēc"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:7
msgid "_Mark All Episodes as Played"
msgstr "Atzī_mēt visas epizodes kā atskaņotas"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:11
msgid "_Website"
msgstr "_Tīmekļa vietne"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:15
msgid "_Unsubscribe"
msgstr "An_ulēt abonementu"
#: podcasts-gtk/resources/gtk/show_menu.ui:51
msgid "Open Website"
msgstr "Atvērt tīmekļa vietni"
#: podcasts-gtk/resources/gtk/show_menu.ui:64
msgid "Mark All as Played"
msgstr "Atzīmēt visu kā atskaņotu"
#: podcasts-gtk/resources/gtk/show_menu.ui:89
msgid "Unsubscribe"
msgstr "Anulēt abonementu"
#: podcasts-gtk/src/app.rs:332
msgid "Fetching new episodes"
msgstr "Saņem jaunas epizodes"
#: podcasts-gtk/src/headerbar.rs:98
msgid "You are already subscribed to this show"
msgstr "Jūs jau esat abonējis šo raidījumu"
#: podcasts-gtk/src/headerbar.rs:106
msgid "Invalid URL"
msgstr "Nederīgs URL"
#: podcasts-gtk/src/prefs.rs:59
msgid "Seconds"
msgstr "Sekundes"
#: podcasts-gtk/src/prefs.rs:60
msgid "Minutes"
msgstr "Minūtes"
#: podcasts-gtk/src/prefs.rs:61
msgid "Hours"
msgstr "Stundas"
#: podcasts-gtk/src/prefs.rs:62
msgid "Days"
msgstr "Dienas"
#: podcasts-gtk/src/prefs.rs:63
msgid "Weeks"
msgstr "Nedēļas"
#: podcasts-gtk/src/stacks/content.rs:35
msgid "New"
msgstr "Jauns"
#: podcasts-gtk/src/stacks/content.rs:36
msgid "Shows"
msgstr "Raidījumi"
#: podcasts-gtk/src/utils.rs:357
msgid "Select the file from which to you want to import shows."
msgstr "Izvēlieties datni, no kuras vēlaties importēt raidījumus."
#: podcasts-gtk/src/utils.rs:360
msgid "_Import"
msgstr "_Importēt"
#: podcasts-gtk/src/utils.rs:369
msgid "OPML file"
msgstr "OPML datne"
#: podcasts-gtk/src/utils.rs:386
msgid "Failed to parse the imported file"
msgstr "Neizdevās parsēt importēto datni"
#: podcasts-gtk/src/utils.rs:391
msgid "Selected file could not be accessed."
msgstr "Nevarēja piekļūt izvēlētajai datnei."
#: podcasts-gtk/src/widgets/aboutdialog.rs:26
msgid "Podcast Client for the GNOME Desktop."
msgstr "Podraižu klients GNOME darbvirsmai."
#: podcasts-gtk/src/widgets/aboutdialog.rs:33
msgid "Learn more about GNOME Podcasts"
msgstr "Uzziniet vairāk par GNOME podraidēm"
#: podcasts-gtk/src/widgets/aboutdialog.rs:38
msgid "translator-credits"
msgstr "Rūdolfs Mazurs <rudolfs.mazurs@gmail.com>"
#: podcasts-gtk/src/widgets/episode.rs:130
msgid "{} min"
msgstr "{} min"
#. sender.send(Action::ErrorNotification(format!("Player Error: {}", error)));
#: podcasts-gtk/src/widgets/player.rs:301
msgid "The media player was unable to execute an action."
msgstr "Mediju atskaņotājs nevarēja izpildīt darbību."
#: podcasts-gtk/src/widgets/show_menu.rs:150
msgid "Marked all episodes as listened"
msgstr "Visas epizodes ir atzīmētas kā noklausītas"
#: podcasts-gtk/src/widgets/show_menu.rs:155
msgid "Unsubscribed from {}"
msgstr "Anulēt abonementu"
+4
View File
@@ -0,0 +1,4 @@
i18n.gettext(meson.project_name(),
args: ['--keyword=i18n', '--keyword=i18n_f', '--keyword=i18n_k',
'--keyword=ni18n:1,2', '--keyword=ni18n_f:1,2', '--keyword=ni18n_k:1,2'],
preset: 'glib')
+408
View File
@@ -0,0 +1,408 @@
# Polish translation for podcasts.
# Copyright © 2018 the podcasts authors.
# This file is distributed under the same license as the podcasts package.
# Piotr Drąg <piotrdrag@gmail.com>, 2018.
# Aviary.pl <community-poland@mozilla.org>, 2018.
#
msgid ""
msgstr ""
"Project-Id-Version: podcasts\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/podcasts/issues\n"
"POT-Creation-Date: 2018-09-28 23:51+0000\n"
"PO-Revision-Date: 2018-09-30 19:15+0200\n"
"Last-Translator: Piotr Drąg <piotrdrag@gmail.com>\n"
"Language-Team: Polish <community-poland@mozilla.org>\n"
"Language: pl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
"|| n%100>=20) ? 1 : 2);\n"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:15
msgid "Top position of the last open main window"
msgstr "Górne położenie ostatnio otwartego głównego okna"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:19
msgid "Left position of the last open main window"
msgstr "Lewe położenie ostatnio otwartego głównego okna"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:23
msgid "Height of the last open main window"
msgstr "Wysokość ostatnio otwartego głównego okna"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:27
msgid "Width of the last open main window"
msgstr "Szerokość ostatnio otwartego głównego okna"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:31
msgid "Maximized state of the last open main window"
msgstr "Zmaksymalizowany stan ostatnio otwartego głównego okna"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:36
msgid "Enable or disable dark theme"
msgstr "Ciemny motyw"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:41
msgid "Whether to periodically refresh content"
msgstr "Czy okresowo odświeżać treści"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:46
msgid "How many periods of time to wait between automatic refreshes"
msgstr "Ile okresów czekać między automatycznym odświeżeniem"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:50
msgid "What period of time to wait between automatic refreshes"
msgstr "Jaki okres czekać między automatycznym odświeżeniem"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:54
msgid "Whether to refresh content after startup"
msgstr "Czy odświeżać treści po uruchomieniu"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:60
msgid "How many periods of time to wait between automatic cleanups"
msgstr "Ile okresów czekać między automatycznym czyszczeniem"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:64
msgid "What period of time to wait between automatic cleanups"
msgstr "Jaki okres czekać między automatycznym czyszczeniem"
#. Weird magic I copy-pasted that sets the Application Name in the Shell.
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:3
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:4
#: podcasts-gtk/src/app.rs:91 podcasts-gtk/src/app.rs:414
#: podcasts-gtk/src/widgets/aboutdialog.rs:31
msgid "Podcasts"
msgstr "Podcasty"
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:4
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:10
msgid "Listen to your favorite podcasts, right from your desktop."
msgstr "Słuchanie ulubionych podcastów prosto z komputera."
#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:6
msgid "@icon@"
msgstr "@icon@"
#. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:13
msgid "Podcast;RSS;"
msgstr "Podcast;Podkasty;RSS;Atom;"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:8
msgid "Podcast app for GNOME"
msgstr "Aplikacja do słuchania podcastów w GNOME"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:51
msgid "Jordan Petridis"
msgstr "Jordan Petridis"
#: podcasts-gtk/resources/gtk/empty_view.ui:46
msgid "This show does not have episodes yet"
msgstr "Ten program nie ma jeszcze odcinków"
#: podcasts-gtk/resources/gtk/empty_view.ui:62
msgid "If you think this is an error, please consider writing a bug report."
msgstr "Jeśli to błąd, to prosimy go zgłosić."
#: podcasts-gtk/resources/gtk/empty_view.ui:106
msgid "Get some shows"
msgstr "Pobierz programy"
#: podcasts-gtk/resources/gtk/empty_view.ui:143
msgid "Add new shows via feed URL"
msgstr "Dodaj nowe programy za pomocą adresu kanału"
#: podcasts-gtk/resources/gtk/empty_view.ui:172
msgid "Import shows from another device"
msgstr "Zaimportuj programy z innego urządzenia"
#: podcasts-gtk/resources/gtk/episode_widget.ui:180
msgid "Calculating episode size…"
msgstr "Obliczanie rozmiaru odcinka…"
#: podcasts-gtk/resources/gtk/episode_widget.ui:220
msgid "Play this episode"
msgstr "Odtwarza ten odcinek"
#: podcasts-gtk/resources/gtk/episode_widget.ui:241
msgid "Cancel the download proccess"
msgstr "Anuluje pobieranie"
#: podcasts-gtk/resources/gtk/episode_widget.ui:264
msgid "Download this episode"
msgstr "Pobiera ten odcinek"
#: podcasts-gtk/resources/gtk/hamburger.ui:7
msgid "_Check for New Episodes"
msgstr "_Wyszukaj nowe odcinki"
#: podcasts-gtk/resources/gtk/hamburger.ui:12
msgid "_Import Shows"
msgstr "Zai_mportuj programy"
#: podcasts-gtk/resources/gtk/hamburger.ui:22
msgid "_Preferences"
msgstr "_Preferencje"
#: podcasts-gtk/resources/gtk/hamburger.ui:27
msgid "_Keyboard Shortcuts"
msgstr "_Skróty klawiszowe"
#: podcasts-gtk/resources/gtk/hamburger.ui:35
msgid "_About Podcasts"
msgstr "_O programie"
#: podcasts-gtk/resources/gtk/headerbar.ui:35
#: podcasts-gtk/resources/gtk/headerbar.ui:189
msgid "Add a new feed"
msgstr "Dodaje nowy kanał"
#: podcasts-gtk/resources/gtk/headerbar.ui:53
msgid "Enter feed address to add"
msgstr "Adres kanału do dodania"
#: podcasts-gtk/resources/gtk/headerbar.ui:89
msgid "Add"
msgstr "Dodaj"
#: podcasts-gtk/resources/gtk/headerbar.ui:133
msgid "You are already subscribed to that feed!"
msgstr "Ten kanał jest już subskrybowany."
#: podcasts-gtk/resources/gtk/headerbar.ui:169
msgid "Show Title"
msgstr "Tytuł programu"
#: podcasts-gtk/resources/gtk/headerbar.ui:210
msgid "Back"
msgstr "Wstecz"
#: podcasts-gtk/resources/gtk/help-overlay.ui:12
msgid "General"
msgstr "Ogólne"
#: podcasts-gtk/resources/gtk/help-overlay.ui:18
msgctxt "shortcut window"
msgid "Check for new episodes"
msgstr "Wyszukanie nowych odcinków"
#: podcasts-gtk/resources/gtk/help-overlay.ui:25
msgctxt "shortcut window"
msgid "Preferences"
msgstr "Preferencje"
#: podcasts-gtk/resources/gtk/help-overlay.ui:32
msgctxt "shortcut window"
msgid "Quit the application"
msgstr "Zakończenie działania programu"
#: podcasts-gtk/resources/gtk/home_view.ui:56
msgid "Today"
msgstr "Dzisiaj"
#: podcasts-gtk/resources/gtk/home_view.ui:112
msgid "Yesterday"
msgstr "Wczoraj"
#: podcasts-gtk/resources/gtk/home_view.ui:168
msgid "This Week"
msgstr "Ten tydzień"
#: podcasts-gtk/resources/gtk/home_view.ui:224
msgid "This Month"
msgstr "Ten miesiąc"
#: podcasts-gtk/resources/gtk/home_view.ui:281
msgid "Older"
msgstr "Starsze"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:101
msgid "An in-app action notification"
msgstr "Powiadomienie z działaniem w programie"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:112
msgid "Undo"
msgstr "Cofnij"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:72
msgid "Rewind 10 seconds"
msgstr "Przewija wstecz o 10 sekund"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:87
msgid "Play"
msgstr "Odtwarza"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:103
msgid "Pause"
msgstr "Wstrzymuje"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:119
msgid "Fast forward 10 seconds"
msgstr "Przewija wprzód o 10 sekund"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:285
msgid "Change the playback speed"
msgstr "Zmienia prędkość odtwarzania"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:300
#: podcasts-gtk/resources/gtk/player_toolbar.ui:380
msgid "1.00×"
msgstr "1,00×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:344
msgid "1.50×"
msgstr "1,50×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:348
msgid "1.5 speed rate"
msgstr "Prędkość 1,5"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:362
msgid "1.25×"
msgstr "1,25×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:366
msgid "1.25 speed rate"
msgstr "Prędkość 1,25"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:384
msgid "Normal speed"
msgstr "Zwykła prędkość"
#: podcasts-gtk/resources/gtk/prefs.ui:42
#: podcasts-gtk/resources/gtk/prefs.ui:295
msgid "Preferences"
msgstr "Preferencje"
#: podcasts-gtk/resources/gtk/prefs.ui:76
msgid "Appearance"
msgstr "Wygląd"
#: podcasts-gtk/resources/gtk/prefs.ui:120
msgid "Dark Theme"
msgstr "Ciemny motyw"
#: podcasts-gtk/resources/gtk/prefs.ui:166
msgid "Delete played episodes"
msgstr "Usuwanie odtworzonych odcinków"
#: podcasts-gtk/resources/gtk/prefs.ui:211
msgid "After"
msgstr "Po"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:7
msgid "_Mark All Episodes as Played"
msgstr "_Oznacz wszystkie odcinki jako odtworzone"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:11
msgid "_Website"
msgstr "Strona _WWW"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:15
msgid "_Unsubscribe"
msgstr "_Zrezygnuj z subskrypcji"
#: podcasts-gtk/resources/gtk/show_menu.ui:51
msgid "Open Website"
msgstr "Otwórz stronę WWW"
#: podcasts-gtk/resources/gtk/show_menu.ui:64
msgid "Mark All as Played"
msgstr "Oznacz wszystkie jako odtworzone"
#: podcasts-gtk/resources/gtk/show_menu.ui:89
msgid "Unsubscribe"
msgstr "Zrezygnuj z subskrypcji"
#: podcasts-gtk/src/app.rs:332
msgid "Fetching new episodes"
msgstr "Pobieranie nowych odcinków"
#: podcasts-gtk/src/headerbar.rs:98
msgid "You are already subscribed to this show"
msgstr "Ten program jest już subskrybowany"
#: podcasts-gtk/src/headerbar.rs:106
msgid "Invalid URL"
msgstr "Nieprawidłowy adres"
#: podcasts-gtk/src/prefs.rs:59
msgid "Seconds"
msgstr "Sekundy"
#: podcasts-gtk/src/prefs.rs:60
msgid "Minutes"
msgstr "Minuty"
#: podcasts-gtk/src/prefs.rs:61
msgid "Hours"
msgstr "Godziny"
#: podcasts-gtk/src/prefs.rs:62
msgid "Days"
msgstr "Dni"
#: podcasts-gtk/src/prefs.rs:63
msgid "Weeks"
msgstr "Tygodnie"
#: podcasts-gtk/src/stacks/content.rs:35
msgid "New"
msgstr "Nowe"
#: podcasts-gtk/src/stacks/content.rs:36
msgid "Shows"
msgstr "Programy"
#: podcasts-gtk/src/utils.rs:357
msgid "Select the file from which to you want to import shows."
msgstr "Proszę wybrać plik, z którego zaimportować programy."
#: podcasts-gtk/src/utils.rs:360
msgid "_Import"
msgstr "Zai_mportuj"
#: podcasts-gtk/src/utils.rs:369
msgid "OPML file"
msgstr "Plik OPML"
#: podcasts-gtk/src/utils.rs:386
msgid "Failed to parse the imported file"
msgstr "Przetworzenie zaimportowanego pliku się nie powiodło"
#: podcasts-gtk/src/utils.rs:391
msgid "Selected file could not be accessed."
msgstr "Nie można uzyskać dostępu do wybranego pliku."
#: podcasts-gtk/src/widgets/aboutdialog.rs:26
msgid "Podcast Client for the GNOME Desktop."
msgstr "Klient podcastów dla środowiska GNOME."
#: podcasts-gtk/src/widgets/aboutdialog.rs:33
msgid "Learn more about GNOME Podcasts"
msgstr "Więcej informacji o Podcastach GNOME"
#: podcasts-gtk/src/widgets/aboutdialog.rs:38
msgid "translator-credits"
msgstr ""
"Piotr Drąg <piotrdrag@gmail.com>, 2018\n"
"Aviary.pl <community-poland@mozilla.org>, 2018"
#: podcasts-gtk/src/widgets/episode.rs:130
msgid "{} min"
msgstr "{}min"
#. sender.send(Action::ErrorNotification(format!("Player Error: {}", error)));
#: podcasts-gtk/src/widgets/player.rs:301
msgid "The media player was unable to execute an action."
msgstr "Odtwarzacz multimediów nie może wykonać działania."
#: podcasts-gtk/src/widgets/show_menu.rs:150
msgid "Marked all episodes as listened"
msgstr "Oznaczono wszystkie odcinki jako odsłuchane"
#: podcasts-gtk/src/widgets/show_menu.rs:155
msgid "Unsubscribed from {}"
msgstr "Zrezygnowano z subskrypcji „{}”"
+418
View File
@@ -0,0 +1,418 @@
# Brazilian Portuguese translation for podcasts.
# Copyright (C) 2018 podcasts's COPYRIGHT HOLDER
# This file is distributed under the same license as the podcasts package.
# Rafael Fontenelle <rafaelff@gnome.org>, 2018.
# Yuri Otávio Lopes Gomes <yuriotavio@hotmail.com>, 2018.
msgid ""
msgstr ""
"Project-Id-Version: podcasts master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/podcasts/issues\n"
"POT-Creation-Date: 2018-10-05 11:47+0000\n"
"PO-Revision-Date: 2018-10-05 11:18-0200\n"
"Last-Translator: Rafael Fontenelle <rafaelff@gnome.org>\n"
"Language-Team: Brazilian Portuguese <gnome-pt_br-list@gnome.org>\n"
"Language: pt_BR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Generator: Virtaal 1.0.0-beta1\n"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:15
msgid "Top position of the last open main window"
msgstr "Posição acima da janela da última principal aberta"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:19
msgid "Left position of the last open main window"
msgstr "Posição à esquerda da última janela principal aberta"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:23
msgid "Height of the last open main window"
msgstr "Altura da última janela principal aberta"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:27
msgid "Width of the last open main window"
msgstr "Largura da última janela principal aberta"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:31
msgid "Maximized state of the last open main window"
msgstr "Estado maximizado da última janela principal aberta"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:36
msgid "Enable or disable dark theme"
msgstr "Habilitar ou desabilitar o tema escuro"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:41
msgid "Whether to periodically refresh content"
msgstr "Se deve atualizar periodicamente o conteúdo"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:46
msgid "How many periods of time to wait between automatic refreshes"
msgstr ""
"Quantos períodos de tempo deve esperar entre as atualizações automáticas"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:50
msgid "What period of time to wait between automatic refreshes"
msgstr "Qual período de tempo deve esperar entre as atualizações automáticas"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:54
msgid "Whether to refresh content after startup"
msgstr "Se deve atualizar o conteúdo após a inicialização"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:60
msgid "How many periods of time to wait between automatic cleanups"
msgstr "Quantos períodos de tempo deve esperar entre limpezas automáticas"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:64
msgid "What period of time to wait between automatic cleanups"
msgstr "Qual período de tempo deve esperar entre limpezas automáticas"
#. Weird magic I copy-pasted that sets the Application Name in the Shell.
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:3
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:4
#: podcasts-gtk/src/app.rs:92 podcasts-gtk/src/app.rs:416
#: podcasts-gtk/src/widgets/aboutdialog.rs:31
msgid "Podcasts"
msgstr "Podcasts"
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:4
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:10
msgid "Listen to your favorite podcasts, right from your desktop."
msgstr "Ouça seus podcasts favoritos diretamente da sua área de trabalho."
#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:6
msgid "@icon@"
msgstr "@icon@"
#. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:13
msgid "Podcast;RSS;"
msgstr "Podcast;RSS;"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:8
msgid "Podcast app for GNOME"
msgstr "Aplicativo de podcast para o GNOME"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:51
msgid "Jordan Petridis"
msgstr "Jordan Petridis"
#: podcasts-gtk/resources/gtk/empty_view.ui:46
msgid "This show does not have episodes yet"
msgstr "Este podcast ainda não tem episódios"
#: podcasts-gtk/resources/gtk/empty_view.ui:62
msgid "If you think this is an error, please consider writing a bug report."
msgstr ""
"Se você acredita que isso é um erro, por favor considere preencher um "
"relatório de erro."
#: podcasts-gtk/resources/gtk/empty_view.ui:106
msgid "Get some shows"
msgstr "Obter alguns podcasts"
#: podcasts-gtk/resources/gtk/empty_view.ui:143
msgid "Add new shows via feed URL"
msgstr "Adicionar novos podcasts via URL do feed"
#: podcasts-gtk/resources/gtk/empty_view.ui:172
msgid "Import shows from another device"
msgstr "Importar podcasts de outro dispositivo"
#: podcasts-gtk/resources/gtk/episode_widget.ui:180
msgid "Calculating episode size…"
msgstr "Calculando o tamanho do episódio…"
#: podcasts-gtk/resources/gtk/episode_widget.ui:220
msgid "Play this episode"
msgstr "Inicia este episódio"
#: podcasts-gtk/resources/gtk/episode_widget.ui:241
#| msgid "Cancel the download proccess"
msgid "Cancel the download process"
msgstr "Cancela o processo de download"
#: podcasts-gtk/resources/gtk/episode_widget.ui:264
msgid "Download this episode"
msgstr "Faz download deste episódio"
#: podcasts-gtk/resources/gtk/hamburger.ui:7
msgid "_Check for New Episodes"
msgstr "_Procurar novos episódios"
#: podcasts-gtk/resources/gtk/hamburger.ui:12
msgid "_Import Shows"
msgstr "_Importar podcasts"
#: podcasts-gtk/resources/gtk/hamburger.ui:22
msgid "_Preferences"
msgstr "_Preferências"
#: podcasts-gtk/resources/gtk/hamburger.ui:27
msgid "_Keyboard Shortcuts"
msgstr "Atal_hos de teclado"
#: podcasts-gtk/resources/gtk/hamburger.ui:35
#| msgid "Podcasts"
msgid "_About Podcasts"
msgstr "_Sobre Podcasts"
#: podcasts-gtk/resources/gtk/headerbar.ui:35
#: podcasts-gtk/resources/gtk/headerbar.ui:189
msgid "Add a new feed"
msgstr "Adiciona um novo feed"
#: podcasts-gtk/resources/gtk/headerbar.ui:53
msgid "Enter feed address to add"
msgstr "Insira o endereço do feed para adicioná-lo"
#: podcasts-gtk/resources/gtk/headerbar.ui:89
msgid "Add"
msgstr "Adicionar"
#: podcasts-gtk/resources/gtk/headerbar.ui:133
msgid "You are already subscribed to that feed!"
msgstr "Você já está inscrito naquele feed!"
#: podcasts-gtk/resources/gtk/headerbar.ui:169
msgid "Show Title"
msgstr "Mostrar título"
#: podcasts-gtk/resources/gtk/headerbar.ui:210
msgid "Back"
msgstr "Voltar"
#: podcasts-gtk/resources/gtk/help-overlay.ui:12
msgid "General"
msgstr "Geral"
#: podcasts-gtk/resources/gtk/help-overlay.ui:18
msgctxt "shortcut window"
msgid "Check for new episodes"
msgstr "Procura por novos episódios"
#: podcasts-gtk/resources/gtk/help-overlay.ui:25
msgctxt "shortcut window"
msgid "Preferences"
msgstr "Preferências"
#: podcasts-gtk/resources/gtk/help-overlay.ui:32
msgctxt "shortcut window"
msgid "Quit the application"
msgstr "Sai do aplicativo"
#: podcasts-gtk/resources/gtk/home_view.ui:56
msgid "Today"
msgstr "Hoje"
#: podcasts-gtk/resources/gtk/home_view.ui:112
msgid "Yesterday"
msgstr "Ontem"
#: podcasts-gtk/resources/gtk/home_view.ui:168
msgid "This Week"
msgstr "Essa semana"
#: podcasts-gtk/resources/gtk/home_view.ui:224
msgid "This Month"
msgstr "Esse mês"
#: podcasts-gtk/resources/gtk/home_view.ui:281
msgid "Older"
msgstr "Antigos"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:101
msgid "An in-app action notification"
msgstr "Uma notificação de ação no aplicativo"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:112
msgid "Undo"
msgstr "Desfazer"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:72
msgid "Rewind 10 seconds"
msgstr "Retroceder 10 segundos"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:87
msgid "Play"
msgstr "Reproduz"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:103
msgid "Pause"
msgstr "Pausa"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:119
msgid "Fast forward 10 seconds"
msgstr "Adianta 10 segundos"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:285
msgid "Change the playback speed"
msgstr "Altera a velocidade de reprodução"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:300
#: podcasts-gtk/resources/gtk/player_toolbar.ui:380
msgid "1.00×"
msgstr "1,00×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:344
msgid "1.50×"
msgstr "1,50×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:348
msgid "1.5 speed rate"
msgstr "Taxa de velocidade de 1,5"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:362
msgid "1.25×"
msgstr "1,25×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:366
msgid "1.25 speed rate"
msgstr "Taxa de velocidade de 1,25"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:384
msgid "Normal speed"
msgstr "Velocidade normal"
#: podcasts-gtk/resources/gtk/prefs.ui:42
#: podcasts-gtk/resources/gtk/prefs.ui:295
msgid "Preferences"
msgstr "Preferências"
#: podcasts-gtk/resources/gtk/prefs.ui:76
msgid "Appearance"
msgstr "Aparência"
#: podcasts-gtk/resources/gtk/prefs.ui:120
msgid "Dark Theme"
msgstr "Tema escuro"
#: podcasts-gtk/resources/gtk/prefs.ui:166
msgid "Delete played episodes"
msgstr "Excluir episódios já reproduzidos"
#: podcasts-gtk/resources/gtk/prefs.ui:211
msgid "After"
msgstr "Após"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:7
msgid "_Mark All Episodes as Played"
msgstr "_Marcar todos os episódios como reproduzidos"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:11
msgid "_Website"
msgstr "_Site"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:15
msgid "_Unsubscribe"
msgstr "Se _desinscrever"
#: podcasts-gtk/resources/gtk/show_menu.ui:51
msgid "Open Website"
msgstr "Abrir o site"
#: podcasts-gtk/resources/gtk/show_menu.ui:64
msgid "Mark All as Played"
msgstr "Marcar todos como reproduzidos"
#: podcasts-gtk/resources/gtk/show_menu.ui:89
msgid "Unsubscribe"
msgstr "Desinscrever"
#: podcasts-gtk/src/app.rs:333
msgid "Fetching new episodes"
msgstr "Buscando novos episódios"
#: podcasts-gtk/src/headerbar.rs:98
msgid "You are already subscribed to this show"
msgstr "Você já está inscrito neste podcast"
#: podcasts-gtk/src/headerbar.rs:106
msgid "Invalid URL"
msgstr "URL inválida"
#: podcasts-gtk/src/prefs.rs:59
msgid "Seconds"
msgstr "Segundos"
#: podcasts-gtk/src/prefs.rs:60
msgid "Minutes"
msgstr "Minutos"
#: podcasts-gtk/src/prefs.rs:61
msgid "Hours"
msgstr "Horas"
#: podcasts-gtk/src/prefs.rs:62
msgid "Days"
msgstr "Dias"
#: podcasts-gtk/src/prefs.rs:63
msgid "Weeks"
msgstr "Semanas"
#: podcasts-gtk/src/stacks/content.rs:35
msgid "New"
msgstr "Novo"
#: podcasts-gtk/src/stacks/content.rs:36
msgid "Shows"
msgstr "Podcasts"
#: podcasts-gtk/src/utils.rs:357
msgid "Select the file from which to you want to import shows."
msgstr "Selecione o arquivo a partir do qual você deseja importar podcasts."
#: podcasts-gtk/src/utils.rs:360
msgid "_Import"
msgstr "_Importar"
#: podcasts-gtk/src/utils.rs:369
msgid "OPML file"
msgstr "Arquivo OPML"
#: podcasts-gtk/src/utils.rs:386
msgid "Failed to parse the imported file"
msgstr "Falha ao analisar o arquivo importado"
#: podcasts-gtk/src/utils.rs:391
msgid "Selected file could not be accessed."
msgstr "O arquivo selecionado não pôde ser acessado."
#: podcasts-gtk/src/widgets/aboutdialog.rs:26
msgid "Podcast Client for the GNOME Desktop."
msgstr "Cliente de podcast para o ambiente GNOME."
#: podcasts-gtk/src/widgets/aboutdialog.rs:33
msgid "Learn more about GNOME Podcasts"
msgstr "Aprenda mais sobre o GNOME Podcasts"
#: podcasts-gtk/src/widgets/aboutdialog.rs:38
msgid "translator-credits"
msgstr ""
"Rafael Fontenelle <rafaelff@gnome.org>\n"
"Yuri Otávio Lopes Gomes <yuriotavio@hotmail.com>"
#: podcasts-gtk/src/widgets/episode.rs:130
msgid "{} min"
msgstr "{} min"
#. sender.send(Action::ErrorNotification(format!("Player Error: {}", error)));
#: podcasts-gtk/src/widgets/player.rs:363
msgid "The media player was unable to execute an action."
msgstr "O reprodutor de mídia não conseguiu executar uma ação."
#: podcasts-gtk/src/widgets/show_menu.rs:150
msgid "Marked all episodes as listened"
msgstr "Marcados todos os episódios como ouvidos"
#: podcasts-gtk/src/widgets/show_menu.rs:155
msgid "Unsubscribed from {}"
msgstr "Desinscrito de {}"
#~ msgid "_About"
#~ msgstr "_Sobre"
#~ msgid "Cancel"
#~ msgstr "Cancelar"
+412
View File
@@ -0,0 +1,412 @@
# Swedish translation for podcasts.
# Copyright © 2018 podcasts's COPYRIGHT HOLDER
# This file is distributed under the same license as the podcasts package.
# Anders Jonsson <anders.jonsson@norsjovallen.se>, 2018.
#
msgid ""
msgstr ""
"Project-Id-Version: podcasts master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/podcasts/issues\n"
"POT-Creation-Date: 2018-09-08 17:18+0000\n"
"PO-Revision-Date: 2018-09-09 19:38+0200\n"
"Last-Translator: Anders Jonsson <anders.jonsson@norsjovallen.se>\n"
"Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n"
"Language: sv\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 2.1.1\n"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:15
msgid "Top position of the last open main window"
msgstr "Topposition för det senast öppna huvudfönstret"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:19
msgid "Left position of the last open main window"
msgstr "Vänsterposition för det senast öppna huvudfönstret"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:23
msgid "Height of the last open main window"
msgstr "Höjden för det senast öppna huvudfönstret"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:27
msgid "Width of the last open main window"
msgstr "Bredden för det senast öppna huvudfönstret"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:31
msgid "Maximized state of the last open main window"
msgstr "Maximeringstillstånd för det senast öppna huvudfönstret"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:36
msgid "Enable or disable dark theme"
msgstr "Aktivera eller inaktivera mörkt tema"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:41
msgid "Whether to periodically refresh content"
msgstr "Huruvida innehåll ska uppdateras periodiskt"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:46
msgid "How many periods of time to wait between automatic refreshes"
msgstr "Hur många tidsperioder att vänta mellan automatiska uppdateringar"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:50
msgid "What period of time to wait between automatic refreshes"
msgstr "Vilken sorts tidsperiod att vänta mellan automatiska uppdateringar"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:54
msgid "Whether to refresh content after startup"
msgstr "Huruvida innehåll ska uppdateras efter uppstart"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:60
msgid "How many periods of time to wait between automatic cleanups"
msgstr "Hur många tidsperioder att vänta mellan automatiska upprensningar"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:64
msgid "What period of time to wait between automatic cleanups"
msgstr "Vilken sorts tidsperiod att vänta mellan automatiska upprensningar"
#. Weird magic I copy-pasted that sets the Application Name in the Shell.
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:3
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:4
#: podcasts-gtk/src/app.rs:91 podcasts-gtk/src/app.rs:414
#: podcasts-gtk/src/widgets/aboutdialog.rs:31
msgid "Podcasts"
msgstr "Poddsändningar"
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:4
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:10
msgid "Listen to your favorite podcasts, right from your desktop."
msgstr "Lyssna på dina favoritpoddsändningar, direkt från ditt skrivbord."
#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:6
msgid "@icon@"
msgstr "@icon@"
#. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:13
msgid "Podcast;RSS;"
msgstr "Poddsändning;Podcast;RSS;"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:8
msgid "Podcast app for GNOME"
msgstr "Poddsändningsprogram för GNOME"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:51
msgid "Jordan Petridis"
msgstr "Jordan Petridis"
#: podcasts-gtk/resources/gtk/empty_view.ui:46
msgid "This show does not have episodes yet"
msgstr "Denna show har inga avsnitt ännu"
#: podcasts-gtk/resources/gtk/empty_view.ui:62
msgid "If you think this is an error, please consider writing a bug report."
msgstr "Överväg att skriva en felrapport om du tror att detta är ett fel."
#: podcasts-gtk/resources/gtk/empty_view.ui:106
msgid "Get some shows"
msgstr "Få tag på några shower"
#: podcasts-gtk/resources/gtk/empty_view.ui:143
msgid "Add new shows via feed URL"
msgstr "Lägg till nya shower via flödes-URL"
#: podcasts-gtk/resources/gtk/empty_view.ui:172
msgid "Import shows from another device"
msgstr "Importera shower från en annan enhet"
#: podcasts-gtk/resources/gtk/episode_widget.ui:180
msgid "Calculating episode size…"
msgstr "Beräknar avsnittsstorlek…"
#: podcasts-gtk/resources/gtk/episode_widget.ui:220
msgid "Play this episode"
msgstr "Spela detta avsnitt"
#: podcasts-gtk/resources/gtk/episode_widget.ui:241
msgid "Cancel the download proccess"
msgstr "Avbryt hämtningsprocessen"
#: podcasts-gtk/resources/gtk/episode_widget.ui:264
msgid "Download this episode"
msgstr "Hämta detta avsnitt"
#: podcasts-gtk/resources/gtk/hamburger.ui:7
msgid "_Check for New Episodes"
msgstr "Sö_k efter nya avsnitt"
#: podcasts-gtk/resources/gtk/hamburger.ui:12
msgid "_Import Shows"
msgstr "_Importera shower"
#: podcasts-gtk/resources/gtk/hamburger.ui:22
msgid "_Preferences"
msgstr "I_nställningar"
#: podcasts-gtk/resources/gtk/hamburger.ui:29
msgid "_Keyboard Shortcuts"
msgstr "_Tangentbordsgenvägar"
#: podcasts-gtk/resources/gtk/hamburger.ui:37
msgid "_About"
msgstr "_Om"
#: podcasts-gtk/resources/gtk/headerbar.ui:35
#: podcasts-gtk/resources/gtk/headerbar.ui:189
msgid "Add a new feed"
msgstr "Lägg till ett nytt flöde"
#: podcasts-gtk/resources/gtk/headerbar.ui:53
msgid "Enter feed address to add"
msgstr "Ange flödesadress att lägga till"
#: podcasts-gtk/resources/gtk/headerbar.ui:89
msgid "Add"
msgstr "Lägg till"
#: podcasts-gtk/resources/gtk/headerbar.ui:133
msgid "You are already subscribed to that feed!"
msgstr "Du prenumererar redan på det flödet!"
#: podcasts-gtk/resources/gtk/headerbar.ui:169
msgid "Show Title"
msgstr "Visa titel"
#: podcasts-gtk/resources/gtk/headerbar.ui:210
msgid "Back"
msgstr "Bakåt"
#: podcasts-gtk/resources/gtk/help-overlay.ui:12
msgid "General"
msgstr "Allmänt"
#: podcasts-gtk/resources/gtk/help-overlay.ui:18
msgctxt "shortcut window"
msgid "Check for new episodes"
msgstr "Sök efter nya avsnitt"
#: podcasts-gtk/resources/gtk/help-overlay.ui:25
msgctxt "shortcut window"
msgid "Preferences"
msgstr "Inställningar"
#: podcasts-gtk/resources/gtk/help-overlay.ui:32
msgctxt "shortcut window"
msgid "Quit the application"
msgstr "Avsluta programmet"
#: podcasts-gtk/resources/gtk/home_view.ui:56
msgid "Today"
msgstr "Idag"
#: podcasts-gtk/resources/gtk/home_view.ui:112
msgid "Yesterday"
msgstr "Igår"
#: podcasts-gtk/resources/gtk/home_view.ui:168
msgid "This Week"
msgstr "Denna vecka"
#: podcasts-gtk/resources/gtk/home_view.ui:224
msgid "This Month"
msgstr "Denna månad"
#: podcasts-gtk/resources/gtk/home_view.ui:281
msgid "Older"
msgstr "Äldre"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:101
msgid "An in-app action notification"
msgstr "En åtgärdsavisering i programmet"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:112
msgid "Undo"
msgstr "Ångra"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:72
msgid "Rewind 10 seconds"
msgstr "Spola tillbaka 10 sekunder"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:87
msgid "Play"
msgstr "Spela"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:103
msgid "Pause"
msgstr "Pausa"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:119
msgid "Fast forward 10 seconds"
msgstr "Spola framåt 10 sekunder"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:285
msgid "Change the playback speed"
msgstr "Ändra uppspelningshastigheten"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:300
#: podcasts-gtk/resources/gtk/player_toolbar.ui:380
msgid "1.00×"
msgstr "1,00×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:344
msgid "1.50×"
msgstr "1,50×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:348
msgid "1.5 speed rate"
msgstr "Hastighetsfaktor 1,5"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:362
msgid "1.25×"
msgstr "1,25×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:366
msgid "1.25 speed rate"
msgstr "Hastighetsfaktor 1,25"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:384
msgid "Normal speed"
msgstr "Normal hastighet"
#: podcasts-gtk/resources/gtk/prefs.ui:42
#: podcasts-gtk/resources/gtk/prefs.ui:295
msgid "Preferences"
msgstr "Inställningar"
#: podcasts-gtk/resources/gtk/prefs.ui:76
msgid "Appearance"
msgstr "Utseende"
#: podcasts-gtk/resources/gtk/prefs.ui:120
msgid "Dark Theme"
msgstr "Mörkt tema"
#: podcasts-gtk/resources/gtk/prefs.ui:166
msgid "Delete played episodes"
msgstr "Ta bort spelade avsnitt"
#: podcasts-gtk/resources/gtk/prefs.ui:211
msgid "After"
msgstr "Efter"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:7
msgid "_Mark All Episodes as Played"
msgstr "_Markera alla avsnitt som spelade"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:11
msgid "_Website"
msgstr "_Webbplats"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:15
msgid "_Unsubscribe"
msgstr "Säg _upp prenumeration"
#: podcasts-gtk/resources/gtk/show_menu.ui:51
msgid "Open Website"
msgstr "Öppna webbplats"
#: podcasts-gtk/resources/gtk/show_menu.ui:64
msgid "Mark All as Played"
msgstr "Markera alla som spelade"
#: podcasts-gtk/resources/gtk/show_menu.ui:89
msgid "Unsubscribe"
msgstr "Säg upp prenumeration"
#: podcasts-gtk/src/app.rs:332
msgid "Fetching new episodes"
msgstr "Hämtar nya avsnitt"
#: podcasts-gtk/src/headerbar.rs:98
msgid "You are already subscribed to this show"
msgstr "Du prenumererar redan på denna show"
#: podcasts-gtk/src/headerbar.rs:106
msgid "Invalid URL"
msgstr "Ogiltig URL"
#: podcasts-gtk/src/prefs.rs:59
msgid "Seconds"
msgstr "Sekunder"
#: podcasts-gtk/src/prefs.rs:60
msgid "Minutes"
msgstr "Minuter"
#: podcasts-gtk/src/prefs.rs:61
msgid "Hours"
msgstr "Timmar"
#: podcasts-gtk/src/prefs.rs:62
msgid "Days"
msgstr "Dagar"
#: podcasts-gtk/src/prefs.rs:63
msgid "Weeks"
msgstr "Veckor"
#: podcasts-gtk/src/stacks/content.rs:35
msgid "New"
msgstr "Nytt"
#: podcasts-gtk/src/stacks/content.rs:36
msgid "Shows"
msgstr "Shower"
#: podcasts-gtk/src/utils.rs:357
msgid "Select the file from which to you want to import shows."
msgstr "Välj filen från vilken du vill importera shower."
#: podcasts-gtk/src/utils.rs:360
msgid "_Import"
msgstr "_Importera"
#: podcasts-gtk/src/utils.rs:369
msgid "OPML file"
msgstr "OPML-fil"
#: podcasts-gtk/src/utils.rs:386
msgid "Failed to parse the imported file"
msgstr "Misslyckades med att tolka den importerade filen"
#: podcasts-gtk/src/utils.rs:391
msgid "Selected file could not be accessed."
msgstr "Den markerade filen kunde inte kommas åt."
#: podcasts-gtk/src/widgets/aboutdialog.rs:26
msgid "Podcast Client for the GNOME Desktop."
msgstr "Poddsändningsklient för GNOME-skrivbordet."
#: podcasts-gtk/src/widgets/aboutdialog.rs:33
msgid "Learn more about GNOME Podcasts"
msgstr "Lär dig mer om GNOME Poddsändningar"
#: podcasts-gtk/src/widgets/aboutdialog.rs:38
msgid "translator-credits"
msgstr ""
"Anders Jonsson <anders.jonsson@norsjovallen.se>\n"
"\n"
"Skicka synpunkter på översättningen till\n"
"<tp-sv@listor.tp-sv.se>."
#: podcasts-gtk/src/widgets/episode.rs:130
msgid "{} min"
msgstr "{} min"
#. sender.send(Action::ErrorNotification(format!("Player Error: {}", error)));
#: podcasts-gtk/src/widgets/player.rs:301
msgid "The media player was unable to execute an action."
msgstr "Mediaspelaren kunde inte utföra en åtgärd."
#: podcasts-gtk/src/widgets/show_menu.rs:150
msgid "Marked all episodes as listened"
msgstr "Markerade alla avsnitt som lyssnade"
#: podcasts-gtk/src/widgets/show_menu.rs:155
msgid "Unsubscribed from {}"
msgstr "Sade upp prenumeration från {}"
#~ msgid "Cancel"
#~ msgstr "Avbryt"
+419
View File
@@ -0,0 +1,419 @@
# Turkish translation for podcasts.
# Copyright (C) 2018 podcasts's COPYRIGHT HOLDER
# This file is distributed under the same license as the podcasts package.
# Emin Tufan Çetin <etcetin@gmail.com>, 2018.
#
msgid ""
msgstr ""
"Project-Id-Version: podcasts master\n"
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/World/podcasts/issues\n"
"POT-Creation-Date: 2018-10-05 11:47+0000\n"
"PO-Revision-Date: 2018-10-06 18:52+0300\n"
"Last-Translator: Emin Tufan Çetin <etcetin@gmail.com>\n"
"Language-Team: Türkçe <gnome-turk@gnome.org>\n"
"Language: tr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Gtranslator 2.91.7\n"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:15
msgid "Top position of the last open main window"
msgstr "Son açılan ana pencerenin üst konumu"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:19
msgid "Left position of the last open main window"
msgstr "Son açılan ana pencerenin sol konumu"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:23
msgid "Height of the last open main window"
msgstr "Son açılan ana pencerenin yüksekliği"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:27
msgid "Width of the last open main window"
msgstr "Son açılan ana pencerenin genişliği"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:31
msgid "Maximized state of the last open main window"
msgstr "Son açılan ana pencerenin büyütülme durumu"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:36
msgid "Enable or disable dark theme"
msgstr "Karanlık gövdeyi etkinleştir veya devre dışı bırak"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:41
msgid "Whether to periodically refresh content"
msgstr "Belirli aralıklarla içeriğin yenilenmesi"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:46
msgid "How many periods of time to wait between automatic refreshes"
msgstr ""
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:50
msgid "What period of time to wait between automatic refreshes"
msgstr ""
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:54
msgid "Whether to refresh content after startup"
msgstr "Başlangıçtan sonra içeriğin yenilenmesi"
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:60
msgid "How many periods of time to wait between automatic cleanups"
msgstr ""
#: podcasts-gtk/resources/org.gnome.Podcasts.gschema.xml:64
msgid "What period of time to wait between automatic cleanups"
msgstr ""
#. Weird magic I copy-pasted that sets the Application Name in the Shell.
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:3
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:4
#: podcasts-gtk/src/app.rs:92 podcasts-gtk/src/app.rs:416
#: podcasts-gtk/src/widgets/aboutdialog.rs:31
msgid "Podcasts"
msgstr "Podcastler"
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:4
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:10
msgid "Listen to your favorite podcasts, right from your desktop."
msgstr "Gözde podcastlerinizi masaüstünüzden dinleyin."
#. Translators: Do NOT translate or transliterate this text (this is an icon file name)!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:6
msgid "@icon@"
msgstr "@icon@"
#. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!
#: podcasts-gtk/resources/org.gnome.Podcasts.desktop.in.in:13
msgid "Podcast;RSS;"
msgstr "Podcast;RSS;Pod Yayını;"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:8
msgid "Podcast app for GNOME"
msgstr "GNOME için Podcast uygulaması"
#: podcasts-gtk/resources/org.gnome.Podcasts.appdata.xml.in.in:51
msgid "Jordan Petridis"
msgstr "Jordan Petridis"
#: podcasts-gtk/resources/gtk/empty_view.ui:46
msgid "This show does not have episodes yet"
msgstr "Bu gösterinin henüz herhangi bir bölümü yok"
#: podcasts-gtk/resources/gtk/empty_view.ui:62
msgid "If you think this is an error, please consider writing a bug report."
msgstr "Bunun bir hata olduğunu düşünüyorsanız lütfen hata bildirimi yazın."
#: podcasts-gtk/resources/gtk/empty_view.ui:106
msgid "Get some shows"
msgstr "Birkaç gösteri edinin"
#: podcasts-gtk/resources/gtk/empty_view.ui:143
msgid "Add new shows via feed URL"
msgstr "Besleme URLʼsi ile yeni gösteriler ekle"
#: podcasts-gtk/resources/gtk/empty_view.ui:172
msgid "Import shows from another device"
msgstr "Gösterileri başka aygıttan içe aktar"
#: podcasts-gtk/resources/gtk/episode_widget.ui:180
msgid "Calculating episode size…"
msgstr "Bölüm boyutu hesaplanıyor…"
#: podcasts-gtk/resources/gtk/episode_widget.ui:220
msgid "Play this episode"
msgstr "Bu bölümü oynat"
#: podcasts-gtk/resources/gtk/episode_widget.ui:241
#| msgid "Cancel the download proccess"
msgid "Cancel the download process"
msgstr "İndirme sürecini iptal et"
#: podcasts-gtk/resources/gtk/episode_widget.ui:264
msgid "Download this episode"
msgstr "Bu bölümü indir"
#: podcasts-gtk/resources/gtk/hamburger.ui:7
msgid "_Check for New Episodes"
msgstr "Yeni Bölümleri _Denetle"
#: podcasts-gtk/resources/gtk/hamburger.ui:12
msgid "_Import Shows"
msgstr "_Gösterileri İçe Aktar"
#: podcasts-gtk/resources/gtk/hamburger.ui:22
msgid "_Preferences"
msgstr "_Tercihler"
#: podcasts-gtk/resources/gtk/hamburger.ui:27
msgid "_Keyboard Shortcuts"
msgstr "_Klavye Kısayolları"
#: podcasts-gtk/resources/gtk/hamburger.ui:35
#| msgid "Podcasts"
msgid "_About Podcasts"
msgstr "Podcastler _Hakkında"
#: podcasts-gtk/resources/gtk/headerbar.ui:35
#: podcasts-gtk/resources/gtk/headerbar.ui:189
msgid "Add a new feed"
msgstr "Yeni besleme ekle"
#: podcasts-gtk/resources/gtk/headerbar.ui:53
msgid "Enter feed address to add"
msgstr "Eklenecek besleme adresini gir"
#: podcasts-gtk/resources/gtk/headerbar.ui:89
msgid "Add"
msgstr "Ekle"
#: podcasts-gtk/resources/gtk/headerbar.ui:133
msgid "You are already subscribed to that feed!"
msgstr "Bu beslemeye zaten abonesiniz!"
#: podcasts-gtk/resources/gtk/headerbar.ui:169
msgid "Show Title"
msgstr "Gösteri Başlığı"
#: podcasts-gtk/resources/gtk/headerbar.ui:210
msgid "Back"
msgstr "Geri"
#: podcasts-gtk/resources/gtk/help-overlay.ui:12
msgid "General"
msgstr "Genel"
#: podcasts-gtk/resources/gtk/help-overlay.ui:18
msgctxt "shortcut window"
msgid "Check for new episodes"
msgstr "Yeni bölümleri denetle"
#: podcasts-gtk/resources/gtk/help-overlay.ui:25
msgctxt "shortcut window"
msgid "Preferences"
msgstr "Tercihler"
#: podcasts-gtk/resources/gtk/help-overlay.ui:32
msgctxt "shortcut window"
msgid "Quit the application"
msgstr "Uygulamadan çık"
#: podcasts-gtk/resources/gtk/home_view.ui:56
msgid "Today"
msgstr "Bugün"
#: podcasts-gtk/resources/gtk/home_view.ui:112
msgid "Yesterday"
msgstr "Dün"
#: podcasts-gtk/resources/gtk/home_view.ui:168
msgid "This Week"
msgstr "Bu Hafta"
#: podcasts-gtk/resources/gtk/home_view.ui:224
msgid "This Month"
msgstr "Bu Ay"
#: podcasts-gtk/resources/gtk/home_view.ui:281
msgid "Older"
msgstr "Daha Eski"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:101
msgid "An in-app action notification"
msgstr "Uygulama içi eylem bildirimi"
#: podcasts-gtk/resources/gtk/inapp_notif.ui:112
msgid "Undo"
msgstr "Geri al"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:72
msgid "Rewind 10 seconds"
msgstr "10 saniye geri sar"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:87
msgid "Play"
msgstr "Oynat"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:103
msgid "Pause"
msgstr "Duraklat"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:119
msgid "Fast forward 10 seconds"
msgstr "10 saniye ileri sar"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:285
msgid "Change the playback speed"
msgstr "Kayıttan yürütme hızını değiştir"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:300
#: podcasts-gtk/resources/gtk/player_toolbar.ui:380
msgid "1.00×"
msgstr "1.00×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:344
msgid "1.50×"
msgstr "1.50×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:348
msgid "1.5 speed rate"
msgstr "1.5 hız oranı"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:362
msgid "1.25×"
msgstr "1.25×"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:366
msgid "1.25 speed rate"
msgstr "1.25 hız oranı"
#: podcasts-gtk/resources/gtk/player_toolbar.ui:384
msgid "Normal speed"
msgstr "Olağan hız"
#: podcasts-gtk/resources/gtk/prefs.ui:42
#: podcasts-gtk/resources/gtk/prefs.ui:295
msgid "Preferences"
msgstr "Tercihler"
#: podcasts-gtk/resources/gtk/prefs.ui:76
msgid "Appearance"
msgstr "Görünüm"
#: podcasts-gtk/resources/gtk/prefs.ui:120
msgid "Dark Theme"
msgstr "Karanlık Gövde"
#: podcasts-gtk/resources/gtk/prefs.ui:166
msgid "Delete played episodes"
msgstr "Oynatılan bölümleri sil"
#: podcasts-gtk/resources/gtk/prefs.ui:211
msgid "After"
msgstr "Şundan Sonra"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:7
msgid "_Mark All Episodes as Played"
msgstr "Tüm Bölümleri Oynatıldı Olarak _İmle"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:11
msgid "_Website"
msgstr "_Web Sitesi"
#: podcasts-gtk/resources/gtk/secondary_menu.ui:15
msgid "_Unsubscribe"
msgstr "_Abonelikten Çık"
#: podcasts-gtk/resources/gtk/show_menu.ui:51
msgid "Open Website"
msgstr "Web Sitesini Aç"
#: podcasts-gtk/resources/gtk/show_menu.ui:64
msgid "Mark All as Played"
msgstr "Tümünü Oynatıldı Olarak İmle"
#: podcasts-gtk/resources/gtk/show_menu.ui:89
msgid "Unsubscribe"
msgstr "Abonelikten Çık"
#: podcasts-gtk/src/app.rs:333
msgid "Fetching new episodes"
msgstr "Yeni bölümler alınıyor"
#: podcasts-gtk/src/headerbar.rs:98
msgid "You are already subscribed to this show"
msgstr "Bu gösteriye zaten abonesiniz"
#: podcasts-gtk/src/headerbar.rs:106
msgid "Invalid URL"
msgstr "Geçersiz URL"
#: podcasts-gtk/src/prefs.rs:59
msgid "Seconds"
msgstr "Saniye"
#: podcasts-gtk/src/prefs.rs:60
msgid "Minutes"
msgstr "Dakika"
#: podcasts-gtk/src/prefs.rs:61
msgid "Hours"
msgstr "Saat"
#: podcasts-gtk/src/prefs.rs:62
msgid "Days"
msgstr "Gün"
#: podcasts-gtk/src/prefs.rs:63
msgid "Weeks"
msgstr "Hafta"
#: podcasts-gtk/src/stacks/content.rs:35
msgid "New"
msgstr "Yeni"
#: podcasts-gtk/src/stacks/content.rs:36
msgid "Shows"
msgstr "Gösteriler"
#: podcasts-gtk/src/utils.rs:357
msgid "Select the file from which to you want to import shows."
msgstr "Gösterileri içe aktarmak istediğiniz dosyayı seçin"
#: podcasts-gtk/src/utils.rs:360
msgid "_Import"
msgstr "_İçe Aktar"
#: podcasts-gtk/src/utils.rs:369
msgid "OPML file"
msgstr "OPML dosyası"
#: podcasts-gtk/src/utils.rs:386
msgid "Failed to parse the imported file"
msgstr "İçe aktarılan dosya ayrıştırılamadı"
#: podcasts-gtk/src/utils.rs:391
msgid "Selected file could not be accessed."
msgstr "Seçilen dosyaya erişilemedi."
#: podcasts-gtk/src/widgets/aboutdialog.rs:26
msgid "Podcast Client for the GNOME Desktop."
msgstr "GNOME Masaüstü için Podcast İstemcisi."
#: podcasts-gtk/src/widgets/aboutdialog.rs:33
msgid "Learn more about GNOME Podcasts"
msgstr "GNOME Podcastler hakkında daha çoğunu öğrenin"
#: podcasts-gtk/src/widgets/aboutdialog.rs:38
msgid "translator-credits"
msgstr "Emin Tufan Çetin <etcetin@gmail.com>"
#: podcasts-gtk/src/widgets/episode.rs:130
msgid "{} min"
msgstr "{} dak"
#. sender.send(Action::ErrorNotification(format!("Player Error: {}", error)));
#: podcasts-gtk/src/widgets/player.rs:363
msgid "The media player was unable to execute an action."
msgstr "Ortam oynatıcı eylemi yürütemiyor."
#: podcasts-gtk/src/widgets/show_menu.rs:150
msgid "Marked all episodes as listened"
msgstr "Tüm bölümler dinlendi olarak imlendi"
#: podcasts-gtk/src/widgets/show_menu.rs:155
msgid "Unsubscribed from {}"
msgstr "{} aboneliğinden çıkıldı"
#~ msgid "_About"
#~ msgstr "_Hakkında"
#~ msgid "org.gnome.Podcasts"
#~ msgstr "org.gnome.Podcasts"
#~ msgid "Cancel"
#~ msgstr "İptal"
#~ msgid "Episode Title"
#~ msgstr "Bölüm Başlığı"
-63
View File
@@ -1,63 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.0 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkBox" id="empty_show">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="orientation">vertical</property>
<property name="spacing">12</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="pixel_size">128</property>
<property name="icon_name">application-rss+xml-symbolic</property>
<property name="use_fallback">True</property>
<style>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">This show does not have any episodes</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="scale" value="1.4399999999999999"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">If you think this is an Error, Plese consider opening a bug report.</property>
<style>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
</interface>
+48 -8
View File
@@ -3,20 +3,20 @@
Copyright (C) 2017 - 2018
This file is part of Hammond.
This file is part of GNOME Podcasts.
Hammond is free software: you can redistribute it and/or modify
GNOME Podcasts 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.
Hammond is distributed in the hope that it will be useful,
GNOME Podcasts 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 Hammond. If not, see <http://www.gnu.org/licenses/>.
along with GNOME Podcasts. If not, see <http://www.gnu.org/licenses/>.
Authors:
Jordan Petridis
@@ -24,12 +24,53 @@ Tobias Bernard
-->
<interface>
<requires lib="gtk+" version="3.20"/>
<requires lib="gtk+" version="3.22"/>
<!-- interface-license-type gplv3 -->
<!-- interface-name Hammond -->
<!-- interface-name GNOME Podcasts -->
<!-- interface-description A podcast client for the GNOME Desktop -->
<!-- interface-copyright 2017 - 2018 -->
<!-- interface-authors Jordan Petridis\nTobias Bernard -->
<object class="GtkBox" id="empty_show">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="orientation">vertical</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">This show does not have episodes yet</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="scale" value="1.4399999999999999"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">If you think this is an error, please consider writing a bug report.</property>
<style>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<object class="GtkBox" id="empty_view">
<property name="visible">True</property>
<property name="can_focus">False</property>
@@ -40,13 +81,12 @@ Tobias Bernard
<property name="orientation">vertical</property>
<property name="spacing">12</property>
<child>
<object class="GtkImage">
<object class="GtkImage" id="image">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="pixel_size">256</property>
<property name="icon_name">org.gnome.Podcasts-symbolic</property>
<style>
<class name="dim-label"/>
</style>
+59 -50
View File
@@ -3,20 +3,20 @@
Copyright (C) 2017 - 2018
This file is part of Hammond.
This file is part of GNOME Podcasts.
Hammond is free software: you can redistribute it and/or modify
GNOME Podcasts 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.
Hammond is distributed in the hope that it will be useful,
GNOME Podcasts 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 Hammond. If not, see <http://www.gnu.org/licenses/>.
along with GNOME Podcasts. If not, see <http://www.gnu.org/licenses/>.
Authors:
Jordan Petridis
@@ -24,9 +24,9 @@ Tobias Bernard
-->
<interface>
<requires lib="gtk+" version="3.20"/>
<requires lib="gtk+" version="3.22"/>
<!-- interface-license-type gplv3 -->
<!-- interface-name Hammond -->
<!-- interface-name GNOME Podcasts -->
<!-- interface-description A podcast client for the GNOME Desktop -->
<!-- interface-copyright 2017 - 2018 -->
<!-- interface-authors Jordan Petridis\nTobias Bernard -->
@@ -35,8 +35,8 @@ Tobias Bernard
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">center</property>
<property name="margin_left">6</property>
<property name="margin_right">6</property>
<property name="margin_start">6</property>
<property name="margin_end">6</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
@@ -44,17 +44,18 @@ Tobias Bernard
<property name="can_focus">False</property>
<child>
<object class="GtkBox" id="info_container">
<property name="width_request">230</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="title_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Episode Title</property>
<property name="label">Episode Title</property>
<property name="ellipsize">end</property>
<property name="width_chars">55</property>
<property name="single_line_mode">True</property>
<property name="track_visited_links">False</property>
<property name="lines">1</property>
@@ -62,7 +63,7 @@ Tobias Bernard
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
@@ -75,7 +76,7 @@ Tobias Bernard
<object class="GtkLabel" id="date_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">3 Jan</property>
<property name="label">3 Jan</property>
<property name="single_line_mode">True</property>
<property name="track_visited_links">False</property>
<style>
@@ -92,7 +93,7 @@ Tobias Bernard
<object class="GtkLabel" id="separator1">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="label" translatable="yes">·</property>
<property name="label">·</property>
<property name="track_visited_links">False</property>
<style>
<class name="dim-label"/>
@@ -108,7 +109,7 @@ Tobias Bernard
<object class="GtkLabel" id="duration_label">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="label" translatable="yes">42 min</property>
<property name="label">42 min</property>
<property name="single_line_mode">True</property>
<property name="track_visited_links">False</property>
<style>
@@ -125,7 +126,7 @@ Tobias Bernard
<object class="GtkLabel" id="separator2">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="label" translatable="yes">·</property>
<property name="label">·</property>
<property name="track_visited_links">False</property>
<style>
<class name="dim-label"/>
@@ -141,7 +142,8 @@ Tobias Bernard
<object class="GtkLabel" id="local_size">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="label" translatable="yes">0 MB</property>
<property name="label">0 MB</property>
<property name="ellipsize">end</property>
<property name="single_line_mode">True</property>
<property name="track_visited_links">False</property>
<style>
@@ -158,7 +160,7 @@ Tobias Bernard
<object class="GtkLabel" id="prog_separator">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="label" translatable="yes">/</property>
<property name="label">/</property>
<property name="single_line_mode">True</property>
<property name="track_visited_links">False</property>
<style>
@@ -175,7 +177,8 @@ Tobias Bernard
<object class="GtkLabel" id="total_size">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="label" translatable="yes">Calculating episode size...</property>
<property name="label" translatable="yes">Calculating episode size</property>
<property name="ellipsize">end</property>
<property name="single_line_mode">True</property>
<property name="track_visited_links">False</property>
<style>
@@ -204,23 +207,53 @@ Tobias Bernard
</packing>
</child>
<child>
<object class="GtkButtonBox" id="button_box">
<object class="GtkBox" id="button_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="layout_style">center</property>
<property name="halign">end</property>
<property name="spacing">6</property>
<child>
<object class="GtkButton" id="cancel_button">
<property name="label" translatable="yes">Cancel</property>
<object class="GtkButton" id="play_button">
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="receives_default">True</property>
<property name="no_show_all">True</property>
<property name="tooltip_text" translatable="yes">Play this episode</property>
<property name="halign">center</property>
<property name="valign">center</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">media-playback-start-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="cancel_button">
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Cancel the download process</property>
<property name="halign">center</property>
<property name="valign">center</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">media-playback-stop-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack_type">end</property>
<property name="position">0</property>
<property name="position">1</property>
</packing>
</child>
<child>
@@ -240,34 +273,10 @@ Tobias Bernard
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
<property name="non_homogeneous">True</property>
</packing>
</child>
<child>
<object class="GtkButton" id="play_button">
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="no_show_all">True</property>
<property name="tooltip_text" translatable="yes">Play this episode</property>
<property name="halign">center</property>
<property name="valign">center</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">media-playback-start-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
<property name="non_homogeneous">True</property>
</packing>
</child>
</object>
@@ -289,8 +298,8 @@ Tobias Bernard
<object class="GtkProgressBar" id="progress_bar">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="margin_left">6</property>
<property name="margin_right">6</property>
<property name="margin_start">6</property>
<property name="margin_end">6</property>
<property name="hexpand">True</property>
<property name="pulse_step">0</property>
</object>
+3 -5
View File
@@ -4,7 +4,7 @@
<menu id="menu">
<section>
<item>
<attribute name="label" translatable="yes">_Check for new episodes</attribute>
<attribute name="label" translatable="yes">_Check for New Episodes</attribute>
<attribute name="action">win.refresh</attribute>
<attribute name="accel">&lt;primary&gt;r</attribute>
</item>
@@ -23,8 +23,6 @@
<attribute name="action">win.preferences</attribute>
<attribute name="accel">&lt;primary&gt;e</attribute>
</item>
</section>
<section>
<item>
<attribute name="label" translatable="yes">_Keyboard Shortcuts</attribute>
<attribute name="action">win.show-help-overlay</attribute>
@@ -34,9 +32,9 @@
<!-- <attribute name="action">win.help</attribute> -->
<!-- </item> -->
<item>
<attribute name="label" translatable="yes">_About</attribute>
<attribute name="label" translatable="yes">_About Podcasts</attribute>
<attribute name="action">win.about</attribute>
</item>
</section>
</menu>
</interface>
</interface>
+44 -76
View File
@@ -3,20 +3,20 @@
Copyright (C) 2017 - 2018
This file is part of Hammond.
This file is part of GNOEM Podcasts.
Hammond is free software: you can redistribute it and/or modify
GNOEM Podcasts 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.
Hammond is distributed in the hope that it will be useful,
GNOEM Podcasts 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 Hammond. If not, see <http://www.gnu.org/licenses/>.
along with GNOEM Podcasts. If not, see <http://www.gnu.org/licenses/>.
Authors:
Jordan Petridis
@@ -24,9 +24,9 @@ Tobias Bernard
-->
<interface>
<requires lib="gtk+" version="3.20"/>
<requires lib="gtk+" version="3.22"/>
<!-- interface-license-type gplv3 -->
<!-- interface-name Hammond -->
<!-- interface-name GNOEM Podcasts -->
<!-- interface-description A podcast client for the GNOME Desktop -->
<!-- interface-copyright 2017 - 2018 -->
<!-- interface-authors Jordan Petridis\nTobias Bernard -->
@@ -41,10 +41,7 @@ Tobias Bernard
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">center</property>
<property name="margin_left">6</property>
<property name="margin_right">6</property>
<property name="margin_top">6</property>
<property name="margin_bottom">6</property>
<property name="border_width">6</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
@@ -147,6 +144,43 @@ Tobias Bernard
<object class="GtkHeaderBar" id="headerbar">
<property name="can_focus">False</property>
<property name="show_close_button">True</property>
<child type="title">
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkStackSwitcher" id="switch">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="show_title">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="label" translatable="yes">Show Title</property>
<property name="ellipsize">end</property>
<property name="track_visited_links">False</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<child>
<object class="GtkMenuButton" id="add_toggle">
<property name="visible">True</property>
@@ -191,72 +225,6 @@ Tobias Bernard
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkBox" id="update_notification">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<child>
<object class="GtkSpinner" id="update_spinner">
<property name="can_focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">6</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="update_label">
<property name="can_focus">False</property>
<property name="label" translatable="yes">Fetching new episodes</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="position">3</property>
</packing>
</child>
<child type="title">
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkStackSwitcher" id="switch">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="show_title">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="label" translatable="yes">Show Title</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<child>
<object class="GtkMenuButton" id="hamburger">
<property name="visible">True</property>
+7 -7
View File
@@ -3,20 +3,20 @@
Copyright (C) 2017 - 2018
This file is part of Hammond.
This file is part of GNOME Podcasts.
Hammond is free software: you can redistribute it and/or modify
GNOME Podcasts 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.
Hammond is distributed in the hope that it will be useful,
GNOME Podcasts 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 Hammond. If not, see <http://www.gnu.org/licenses/>.
along with GNOME Podcasts. If not, see <http://www.gnu.org/licenses/>.
Authors:
Jordan Petridis
@@ -24,9 +24,9 @@ Tobias Bernard
-->
<interface>
<requires lib="gtk+" version="3.20"/>
<requires lib="gtk+" version="3.22"/>
<!-- interface-license-type gplv3 -->
<!-- interface-name Hammond -->
<!-- interface-name GNOME Podcasts -->
<!-- interface-description A podcast client for the GNOME Desktop -->
<!-- interface-copyright 2017 - 2018 -->
<!-- interface-authors Jordan Petridis\nTobias Bernard -->
@@ -40,7 +40,7 @@ Tobias Bernard
<object class="GtkImage" id="cover">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">8</property>
<property name="margin_start">8</property>
<property name="pixel_size">64</property>
<property name="icon_name">image-x-generic-symbolic</property>
</object>
+280 -352
View File
@@ -3,20 +3,20 @@
Copyright (C) 2017 - 2018
This file is part of Hammond.
This file is part of GNOME Podcasts.
Hammond is free software: you can redistribute it and/or modify
GNOME Podcasts 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.
Hammond is distributed in the hope that it will be useful,
GNOME Podcasts 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 Hammond. If not, see <http://www.gnu.org/licenses/>.
along with GNOME Podcasts. If not, see <http://www.gnu.org/licenses/>.
Authors:
Jordan Petridis
@@ -24,376 +24,304 @@ Tobias Bernard
-->
<interface>
<requires lib="gtk+" version="3.20"/>
<requires lib="gtk+" version="3.22"/>
<!-- interface-license-type gplv3 -->
<!-- interface-name Hammond -->
<!-- interface-name GNOME Podcasts -->
<!-- interface-description A podcast client for the GNOME Desktop -->
<!-- interface-copyright 2017 - 2018 -->
<!-- interface-authors Jordan Petridis\nTobias Bernard -->
<object class="GtkBox" id="container">
<property name="name">container</property>
<property name="width_request">400</property>
<property name="height_request">600</property>
<object class="GtkBox" id="frame_parent">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_start">6</property>
<property name="margin_end">6</property>
<property name="margin_top">32</property>
<property name="margin_bottom">32</property>
<property name="hexpand">True</property>
<property name="orientation">vertical</property>
<property name="spacing">24</property>
<child>
<object class="GtkScrolledWindow" id="scrolled_window">
<property name="name">scrolled_window</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">never</property>
<object class="GtkBox" id="today_box">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="hexpand">True</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkViewport">
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes">Today</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="scale" value="1.5"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkBox">
<object class="GtkListBox" id="today_list">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="orientation">vertical</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="frame_parent">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">32</property>
<property name="margin_right">32</property>
<property name="margin_top">32</property>
<property name="margin_bottom">32</property>
<property name="hexpand">True</property>
<property name="orientation">vertical</property>
<property name="spacing">24</property>
<child>
<object class="GtkBox" id="today_box">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="hexpand">True</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes">Today</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="scale" value="1.5"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkListBox" id="today_list">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="selection_mode">none</property>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="yday_box">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="hexpand">True</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Yesterday</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="scale" value="1.5"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkListBox" id="yday_list">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="selection_mode">none</property>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkBox" id="week_box">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="hexpand">True</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">This Week</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="scale" value="1.5"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkListBox" id="week_list">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="selection_mode">none</property>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkBox" id="month_box">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="hexpand">True</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">This Month</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="scale" value="1.5"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkListBox" id="month_list">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="selection_mode">none</property>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkBox" id="rest_box">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="hexpand">True</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes">Older</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="scale" value="1.5"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkListBox" id="rest_list">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="hexpand">True</property>
<property name="selection_mode">none</property>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">5</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="orientation">vertical</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
<property name="hexpand">True</property>
<property name="selection_mode">none</property>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="yday_box">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="hexpand">True</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Yesterday</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="scale" value="1.5"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkListBox" id="yday_list">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="selection_mode">none</property>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkBox" id="week_box">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="hexpand">True</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">This Week</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="scale" value="1.5"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkListBox" id="week_list">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="selection_mode">none</property>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkBox" id="month_box">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="hexpand">True</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">This Month</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="scale" value="1.5"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkListBox" id="month_list">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="selection_mode">none</property>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkBox" id="rest_box">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="hexpand">True</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes">Older</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="scale" value="1.5"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkListBox" id="rest_list">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="hexpand">True</property>
<property name="selection_mode">none</property>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">5</property>
</packing>
</child>
</object>
</interface>
+53 -14
View File
@@ -1,7 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.21.0 -->
<!-- Generated with glade 3.22.0
Copyright (C) 2017 - 2018
This file is part of GNOME Podcast.
GNOME Podcast 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.
GNOME Podcast 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 GNOME Podcast. If not, see <http://www.gnu.org/licenses/>.
Authors:
Jordan Petridis
Tobias Bernard
-->
<interface>
<requires lib="gtk+" version="3.20"/>
<requires lib="gtk+" version="3.22"/>
<!-- interface-license-type gplv3 -->
<!-- interface-name GNOME Podcast -->
<!-- interface-description A podcast client for the GNOME Desktop -->
<!-- interface-copyright 2017 - 2018 -->
<!-- interface-authors Jordan Petridis\nTobias Bernard -->
<object class="GtkRevealer" id="revealer">
<property name="visible">True</property>
<property name="can_focus">False</property>
@@ -21,22 +49,16 @@
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="margin_left">3</property>
<property name="margin_right">3</property>
<property name="margin_start">3</property>
<property name="margin_end">3</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="text">
<property name="visible">True</property>
<object class="GtkSpinner" id="spinner">
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="margin_left">12</property>
<property name="margin_right">12</property>
<property name="label" translatable="yes">An in-app action notification</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
@@ -67,10 +89,27 @@
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="text">
<property name="width_request">150</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="margin_start">12</property>
<property name="margin_end">12</property>
<property name="label" translatable="yes">An in-app action notification</property>
<property name="ellipsize">start</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkButton" id="undo">
<property name="label" translatable="yes">Undo</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="halign">center</property>
@@ -83,7 +122,7 @@
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack_type">end</property>
<property name="position">2</property>
<property name="position">3</property>
</packing>
</child>
</object>
+41 -13
View File
@@ -1,7 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.0 -->
<!-- Generated with glade 3.22.0
Copyright (C) 2017 - 2018
This file is part of GNOME Podcast.
GNOME Podcast 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.
GNOME Podcast 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 GNOME Podcast. If not, see <http://www.gnu.org/licenses/>.
Authors:
Jordan Petridis
Tobias Bernard
-->
<interface>
<requires lib="gtk+" version="3.20"/>
<requires lib="gtk+" version="3.22"/>
<!-- interface-license-type gplv3 -->
<!-- interface-name GNOME Podcast -->
<!-- interface-description A podcast client for the GNOME Desktop -->
<!-- interface-copyright 2017 - 2018 -->
<!-- interface-authors Jordan Petridis\nTobias Bernard -->
<object class="GtkImage" id="fast_forward">
<property name="visible">True</property>
<property name="can_focus">False</property>
@@ -88,7 +116,7 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Fast Forward 10 seconds</property>
<property name="tooltip_text" translatable="yes">Fast forward 10 seconds</property>
<property name="image">fast_forward</property>
<property name="always_show_image">True</property>
</object>
@@ -138,7 +166,7 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Show Title</property>
<property name="label">Show Title</property>
<property name="wrap">True</property>
<property name="ellipsize">end</property>
<property name="max_width_chars">20</property>
@@ -157,7 +185,7 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Episode Title</property>
<property name="label">Episode Title</property>
<property name="wrap">True</property>
<property name="ellipsize">end</property>
<property name="max_width_chars">20</property>
@@ -254,10 +282,10 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Change the Playback speed</property>
<property name="tooltip_text" translatable="yes">Change the playback speed</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="margin_left">6</property>
<property name="margin_start">6</property>
<property name="direction">up</property>
<property name="popover">rate_popover</property>
<child>
@@ -269,7 +297,7 @@
<object class="GtkLabel" id="rate_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">1.00x</property>
<property name="label" translatable="yes">1.00×</property>
</object>
<packing>
<property name="expand">False</property>
@@ -305,15 +333,15 @@
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">6</property>
<property name="margin_right">6</property>
<property name="margin_start">6</property>
<property name="margin_end">6</property>
<property name="margin_top">6</property>
<property name="margin_bottom">6</property>
<property name="orientation">vertical</property>
<property name="spacing">3</property>
<child>
<object class="GtkRadioButton" id="rate_1_50">
<property name="label" translatable="yes">1.50x</property>
<property name="label" translatable="yes">1.50×</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
@@ -331,7 +359,7 @@
</child>
<child>
<object class="GtkRadioButton" id="rate_1_25">
<property name="label" translatable="yes">1.25x</property>
<property name="label" translatable="yes">1.25×</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
@@ -349,7 +377,7 @@
</child>
<child>
<object class="GtkRadioButton" id="normal_rate">
<property name="label" translatable="yes">1.00x</property>
<property name="label" translatable="yes">1.00×</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
+39 -11
View File
@@ -1,7 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.0 -->
<!-- Generated with glade 3.22.0
Copyright (C) 2017 - 2018
This file is part of GNOME Podcasts.
GNOME Podcasts 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.
GNOME Podcasts 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 GNOME Podcasts. If not, see <http://www.gnu.org/licenses/>.
Authors:
Jordan Petridis
Tobias Bernard
-->
<interface>
<requires lib="gtk+" version="3.20"/>
<requires lib="gtk+" version="3.22"/>
<!-- interface-license-type gplv3 -->
<!-- interface-name GNOME Podcasts -->
<!-- interface-description A podcast client for the GNOME Desktop -->
<!-- interface-copyright 2017 - 2018 -->
<!-- interface-authors Jordan Petridis\nTobias Bernard -->
<object class="GtkAdjustment" id="cleanup_value_adj">
<property name="lower">1</property>
<property name="upper">100</property>
@@ -81,10 +109,10 @@
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
<property name="margin_start">6</property>
<property name="margin_end">6</property>
<property name="margin_top">6</property>
<property name="margin_bottom">6</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
@@ -172,10 +200,10 @@
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
<property name="margin_start">6</property>
<property name="margin_end">6</property>
<property name="margin_top">6</property>
<property name="margin_bottom">6</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
@@ -264,7 +292,7 @@
<object class="GtkHeaderBar">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="title">Preferences</property>
<property name="title" translatable="yes">Preferences</property>
<property name="show_close_button">True</property>
<child>
<placeholder/>
+2 -2
View File
@@ -4,7 +4,7 @@
<menu id="menu">
<section>
<item>
<attribute name="label" translatable="yes">_Mark all episodes as played</attribute>
<attribute name="label" translatable="yes">_Mark All Episodes as Played</attribute>
<!-- <attribute name="action">win.refresh</attribute> -->
</item>
<item>
@@ -17,4 +17,4 @@
</item>
</section>
</menu>
</interface>
</interface>
+31 -3
View File
@@ -1,7 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.0 -->
<!-- Generated with glade 3.22.0
Copyright (C) 2017 - 2018
This file is part of GNOME Podcasts.
GNOME Podcasts 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.
GNOME Podcasts 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 GNOME Podcasts. If not, see <http://www.gnu.org/licenses/>.
Authors:
Jordan Petridis
Tobias Bernard
-->
<interface>
<requires lib="gtk+" version="3.20"/>
<requires lib="gtk+" version="3.22"/>
<!-- interface-license-type gplv3 -->
<!-- interface-name GNOME Podcasts -->
<!-- interface-description A podcast client for the GNOME Desktop -->
<!-- interface-copyright 2017 - 2018 -->
<!-- interface-authors Jordan Petridis\nTobias Bernard -->
<object class="GtkPopoverMenu" id="menu">
<property name="can_focus">False</property>
<property name="halign">center</property>
@@ -33,7 +61,7 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="text" translatable="yes">Mark all as played</property>
<property name="text" translatable="yes">Mark All as Played</property>
</object>
<packing>
<property name="expand">False</property>
+92 -186
View File
@@ -3,20 +3,20 @@
Copyright (C) 2017 - 2018
This file is part of Hammond.
This file is part of GNOME Podcasts.
Hammond is free software: you can redistribute it and/or modify
GNOME Podcasts 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.
Hammond is distributed in the hope that it will be useful,
GNOME Podcasts 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 Hammond. If not, see <http://www.gnu.org/licenses/>.
along with GNOME Podcasts. If not, see <http://www.gnu.org/licenses/>.
Authors:
Jordan Petridis
@@ -24,209 +24,115 @@ Tobias Bernard
-->
<interface domain="">
<requires lib="gtk+" version="3.20"/>
<requires lib="gtk+" version="3.22"/>
<!-- interface-license-type gplv3 -->
<!-- interface-name Hammond -->
<!-- interface-name GNOME Podcasts -->
<!-- interface-description A podcast client for the GNOME Desktop -->
<!-- interface-copyright 2017 - 2018 -->
<!-- interface-authors Jordan Petridis\nTobias Bernard -->
<object class="GtkBox" id="container">
<property name="width_request">400</property>
<property name="height_request">600</property>
<object class="GtkBox" id="sub_container">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_top">32</property>
<property name="margin_bottom">32</property>
<property name="hexpand">True</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkScrolledWindow" id="scrolled_window">
<property name="name">scrolled_window</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="vexpand">True</property>
<property name="hscrollbar_policy">never</property>
<child>
<object class="GtkViewport">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can_default">True</property>
<property name="hexpand">True</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="orientation">vertical</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">32</property>
<property name="margin_right">32</property>
<property name="margin_top">32</property>
<property name="margin_bottom">32</property>
<property name="hexpand">True</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkImage" id="cover">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="pixel_size">256</property>
<property name="icon_name">image-x-generic-symbolic</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">never</property>
<property name="min_content_height">80</property>
<child>
<object class="GtkViewport">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkLabel" id="description">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="label">This is embarrasing!
Sorry, we could not find a description for this Show.</property>
<property name="use_markup">True</property>
<property name="justify">center</property>
<property name="wrap">True</property>
<property name="max_width_chars">70</property>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="padding">6</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label_xalign">0</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkListBox" id="episodes">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="selection_mode">none</property>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="orientation">vertical</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack_type">end</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
<object class="GtkPopover" id="show_menu">
<property name="can_focus">False</property>
<property name="position">bottom</property>
<property name="spacing">6</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">6</property>
<property name="margin_right">6</property>
<property name="margin_top">6</property>
<property name="margin_bottom">6</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkModelButton" id="mark_all_watched">
<object class="GtkImage" id="cover">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="text" translatable="yes">Mark all episodes as listened</property>
<property name="centered">True</property>
<property name="can_focus">False</property>
<property name="pixel_size">256</property>
<property name="icon_name">image-x-generic-symbolic</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">never</property>
<property name="min_content_height">80</property>
<child>
<object class="GtkViewport">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkLabel" id="description">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="label">This is embarrasing!
Sorry, we could not find a description for this Show.</property>
<property name="use_markup">True</property>
<property name="justify">center</property>
<property name="wrap">True</property>
<property name="wrap_mode">word-char</property>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="padding">6</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">start</property>
<property name="margin_start">6</property>
<property name="margin_end">6</property>
<property name="label_xalign">0</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkAlignment">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">start</property>
<child>
<object class="GtkListBox" id="episodes">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">start</property>
<property name="selection_mode">none</property>
<property name="activate_on_single_click">False</property>
</object>
</child>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
</interface>
-58
View File
@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.0
Copyright (C) 2017 - 2018
This file is part of Hammond.
Hammond 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.
Hammond 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 Hammond. If not, see <http://www.gnu.org/licenses/>.
Authors:
Jordan Petridis
Tobias Bernard
-->
<interface>
<requires lib="gtk+" version="3.20"/>
<!-- interface-license-type gplv3 -->
<!-- interface-name Hammond -->
<!-- interface-description A podcast client for the GNOME Desktop -->
<!-- interface-copyright 2017 - 2018 -->
<!-- interface-authors Jordan Petridis\nTobias Bernard -->
<object class="GtkBox" id="fb_child">
<property name="width_request">256</property>
<property name="height_request">256</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkImage" id="pd_cover">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="pixel_size">256</property>
<property name="icon_name">image-x-generic-symbolic</property>
<property name="icon_size">0</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
</interface>
-72
View File
@@ -1,72 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.21.0
Copyright (C) 2017 - 2018
This file is part of Hammond.
Hammond 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.
Hammond 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 Hammond. If not, see <http://www.gnu.org/licenses/>.
Authors:
Jordan Petridis
Tobias Bernard
-->
<interface>
<requires lib="gtk+" version="3.20"/>
<!-- interface-license-type gplv3 -->
<!-- interface-name Hammond -->
<!-- interface-description A podcast client for the GNOME Desktop -->
<!-- interface-copyright 2017 - 2018 -->
<!-- interface-authors Jordan Petridis\nTobias Bernard -->
<object class="GtkBox" id="fb_parent">
<property name="name">fb_parent</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkScrolledWindow" id="scrolled_window">
<property name="name">scrolled_window</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<child>
<object class="GtkViewport">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkFlowBox" id="flowbox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">start</property>
<property name="margin_top">24</property>
<property name="margin_bottom">24</property>
<property name="homogeneous">True</property>
<property name="column_spacing">12</property>
<property name="row_spacing">12</property>
<property name="max_children_per_line">20</property>
<property name="selection_mode">none</property>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
</interface>
+12
View File
@@ -17,4 +17,16 @@ list, border {
.player-show-label {
font-size: smaller;
}
.devel headerbar {
background: transparent -gtk-icontheme("system-run-symbolic") 80% 0/128px 128px no-repeat,
linear-gradient(to left,
mix(@theme_selected_bg_color, @theme_bg_color, 0.5) 8%,
@theme_bg_color 25%);
color: alpha(@theme_fg_color, 0.1);
}
.devel headerbar label {
color: @theme_fg_color;
}
@@ -27,34 +27,6 @@
id="title4162">Adwaita Icon Template</title>
<defs
id="defs3">
<linearGradient
inkscape:collect="always"
id="linearGradient1654">
<stop
style="stop-color:#c65f00;stop-opacity:1"
offset="0"
id="stop1642" />
<stop
id="stop1644"
offset="0.03432177"
style="stop-color:#ff7b00;stop-opacity:1" />
<stop
style="stop-color:#c65f00;stop-opacity:1"
offset="0.07062413"
id="stop1646" />
<stop
id="stop1648"
offset="0.92767543"
style="stop-color:#c65f00;stop-opacity:1" />
<stop
style="stop-color:#ff7b00;stop-opacity:1"
offset="0.96428573"
id="stop1650" />
<stop
style="stop-color:#c65f00;stop-opacity:1"
offset="1"
id="stop1652" />
</linearGradient>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath1017">
@@ -173,14 +145,43 @@
transform="rotate(90)" />
</clipPath>
<linearGradient
gradientTransform="translate(114.19091,0.00682385)"
inkscape:collect="always"
xlink:href="#linearGradient1654"
id="linearGradient1640"
xlink:href="#linearGradient1654-6"
id="linearGradient1640-3"
x1="8"
y1="264"
x2="120"
y2="264"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
id="linearGradient1654-6">
<stop
style="stop-color:#a51d2d;stop-opacity:1"
offset="0"
id="stop1642-7" />
<stop
id="stop1644-5"
offset="0.03432177"
style="stop-color:#ed333b;stop-opacity:1" />
<stop
style="stop-color:#c01c28;stop-opacity:1"
offset="0.07062413"
id="stop1646-3" />
<stop
id="stop1648-5"
offset="0.92767543"
style="stop-color:#c01c28;stop-opacity:1" />
<stop
style="stop-color:#ed333b;stop-opacity:1"
offset="0.96428573"
id="stop1650-6" />
<stop
style="stop-color:#c01c28;stop-opacity:1"
offset="1"
id="stop1652-2" />
</linearGradient>
</defs>
<sodipodi:namedview
stroke="#ef2929"
@@ -191,9 +192,9 @@
borderopacity="0.25490196"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.70710678"
inkscape:cx="-263.96829"
inkscape:cy="8.5799908"
inkscape:zoom="4"
inkscape:cx="72.243614"
inkscape:cy="53.509759"
inkscape:current-layer="layer9"
showgrid="false"
inkscape:grid-bbox="true"
@@ -201,7 +202,7 @@
inkscape:showpageshadow="false"
inkscape:window-width="1920"
inkscape:window-height="1016"
inkscape:window-x="1920"
inkscape:window-x="0"
inkscape:window-y="27"
width="400px"
height="300px"
@@ -537,24 +538,28 @@
id="layer9"
inkscape:label="hires"
style="display:inline">
<rect
ry="8"
rx="8"
y="252"
x="8"
height="24"
width="112"
id="rect1025"
style="display:inline;opacity:1;fill:url(#linearGradient1640);fill-opacity:1.0;stroke:none;stroke-width:0.24999999;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:0.99999995, 0.49999997000000002;stroke-dashoffset:0;stroke-opacity:1;enable-background:new" />
<rect
style="display:inline;opacity:1;fill:#ff9533;fill-opacity:1;stroke:none;stroke-width:0.24999999;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:0.99999995, 0.49999997000000002;stroke-dashoffset:0;stroke-opacity:1;enable-background:new"
id="rect927-7"
width="112"
height="75.999992"
x="8"
y="196.00002"
rx="8"
ry="8" />
<g
id="g915"
transform="translate(-114.19091,-0.00683594)">
<rect
style="display:inline;opacity:1;fill:url(#linearGradient1640-3);fill-opacity:1;stroke:none;stroke-width:0.24999999;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:0.99999995, 0.49999997;stroke-dashoffset:0;stroke-opacity:1;enable-background:new"
id="rect1025-9"
width="112"
height="24"
x="122.19091"
y="252.00684"
rx="8"
ry="8" />
<rect
ry="8"
rx="8"
y="196.00685"
x="122.19091"
height="75.999992"
width="112"
id="rect927-7-1"
style="display:inline;opacity:1;fill:#ed333b;fill-opacity:1;stroke:none;stroke-width:0.24999999;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:0.99999995, 0.49999997;stroke-dashoffset:0;stroke-opacity:1;enable-background:new" />
</g>
<path
clip-path="none"
style="display:inline;fill:#949390;fill-opacity:1;stroke:#949390;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;enable-background:new"

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

+15 -1
View File
@@ -1 +1,15 @@
install_subdir('hicolor', install_dir: icondir)
scalable_dir = join_paths('hicolor', 'scalable', 'apps')
install_data(
join_paths(scalable_dir, 'org.gnome.Podcasts.svg'),
install_dir: join_paths(datadir, 'icons', scalable_dir),
rename: '@0@.svg'.format(application_id)
)
symbolic_dir = join_paths('hicolor', 'symbolic', 'apps')
install_data(
join_paths(symbolic_dir, 'org.gnome.Podcasts-symbolic.svg'),
install_dir: join_paths(datadir, 'icons', symbolic_dir),
rename: '@0@-symbolic.svg'.format(application_id)
)
+35 -7
View File
@@ -1,13 +1,41 @@
subdir('icons')
install_data('org.gnome.Podcasts.desktop', install_dir : datadir + '/applications')
install_data('org.gnome.Podcasts.appdata.xml', install_dir : datadir + '/appdata')
desktop_conf = configuration_data()
desktop_conf.set('icon', application_id)
i18n.merge_file ('desktop-file',
type: 'desktop',
input: configure_file(
input: files('org.gnome.Podcasts.desktop.in.in'),
output: 'org.gnome.Podcasts.desktop.in',
configuration: desktop_conf
),
output: '@0@.desktop'.format(application_id),
po_dir: podir,
install: true,
install_dir: join_paths (datadir, 'applications')
)
appdata_conf = configuration_data()
appdata_conf.set('appid', application_id)
i18n.merge_file ('appdata-file',
input: configure_file(
input: files('org.gnome.Podcasts.appdata.xml.in.in'),
output: 'org.gnome.Podcasts.appdata.xml.in',
configuration: appdata_conf
),
output: '@0@.appdata.xml'.format(application_id),
po_dir: podir,
install: true,
install_dir: join_paths (datadir, 'metainfo')
)
install_data('org.gnome.Podcasts.gschema.xml', install_dir: join_paths(datadir, 'glib-2.0', 'schemas'))
configure_file(input: 'org.gnome.Podcasts.service.in',
output: 'org.gnome.Podcasts.service',
configuration: podcasts_conf,
install_dir: join_paths([datadir,'dbus-1/services']))
configure_file(
input: 'org.gnome.Podcasts.service.in',
output: '@0@.service'.format(application_id),
configuration: podcasts_conf,
install_dir: join_paths(datadir,'dbus-1', 'services')
)
meson.add_install_script('../../scripts/compile-gschema.py')
@@ -1,35 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop">
<id>org.gnome.Podcasts</id>
<name>Podcasts</name>
<project_license>GPL-3.0</project_license>
<metadata_license>CC0-1.0</metadata_license>
<developer_name>Jordan Petridis</developer_name>
<summary>Podcast app for GNOME</summary>
<description xml:lang="en">
<p>
Listen to your favorite podcasts, right from your desktop.
</p>
</description>
<url type="homepage">https://gitlab.gnome.org/World/podcasts</url>
<screenshots>
<screenshot>
<image>https://gitlab.gnome.org/World/podcasts/raw/master/screenshots/episodes_view.png</image>
<caption>Page 1</caption>
</screenshot>
<screenshot>
<image>https://gitlab.gnome.org/World/podcasts/raw/master/screenshots/shows_view.png</image>
<caption>Page 2</caption>
</screenshot>
<screenshot>
<image>https://gitlab.gnome.org/World/podcasts/raw/master/screenshots/show_widget.png</image>
<caption>Page 3</caption>
</screenshot>
</screenshots>
<releases>
<release version="0.4.1" date="2018-07-26"/>
</releases>
<url type="homepage">https://gitlab.gnome.org/World/podcasts</url>
<update_contact>jpetridis@gnome.org</update_contact>
<developer_name>Jordan Petridis and others</developer_name>
</component>
@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop">
<id>@appid@</id>
<name>Podcasts</name>
<project_license>GPL-3.0</project_license>
<metadata_license>CC0-1.0</metadata_license>
<content_rating type="oars-1.1" />
<summary>Podcast app for GNOME</summary>
<description>
<p>
Listen to your favorite podcasts, right from your desktop.
</p>
</description>
<url type="homepage">https://gitlab.gnome.org/World/podcasts</url>
<screenshots>
<screenshot>
<image>https://gitlab.gnome.org/World/podcasts/raw/master/screenshots/home_view.png</image>
</screenshot>
<screenshot>
<image>https://gitlab.gnome.org/World/podcasts/raw/master/screenshots/shows_view.png</image>
</screenshot>
<screenshot>
<image>https://gitlab.gnome.org/World/podcasts/raw/master/screenshots/show_widget.png</image>
</screenshot>
</screenshots>
<releases>
<release version="0.4.6" date="2018-10-07">
<description>
<p>
After a month of work, the new version of Podcasts brings Desktop integration with MPRIS,
further UX polish, and a couple bugfixes and improvements.
Additionally existing translations were updated and 9 new were added.
</p>
<ul>
<li>Brazilian Portuguese, Swedish, Italian, Friulian, Hungarian, Croatian, Latvian, Czech and Catalan Translations were added.</li>
<li>The audio player now can integrate with Desktop Environments (MPRIS2)</li>
<li>Further UI and UX polishing and a handful bug fixes</li>
<li>Update to account for the GNOME 3.32 HIG changes</li>
<li>Openssl 1.1 support</li>
<li>Fixed a performance regression when updating RSS Feeds</li>
</ul>
</description>
</release>
<release version="0.4.5" date="2018-08-31">
<description>
<p>
Podcasts 0.4.5 brings a month of bug fixes, performance improvements and initial tranlations support.
</p>
<ul>
<li>Finish, Polish, Turkish, Spanish, German, Galician, Indonesian and Korean Translations were added.</li>
<li>Views now adapt better to different window sizes, thanks to libhandy HdyColumn</li>
<li>The update indacator was moved to an In-App notification</li>
<li>Performance improvments when loading Show Cover images.</li>
<li>Improved handling of HTTP Redirects</li>
</ul>
</description>
</release>
<release version="0.4.4" date="2018-07-31">
<description>
<p>
This is the first release available from Flathub!
</p>
</description>
</release>
</releases>
<url type="homepage">https://wiki.gnome.org/Apps/Podcasts</url>
<update_contact>jpetridis@gnome.org</update_contact>
<developer_name>Jordan Petridis</developer_name>
</component>
@@ -1,11 +0,0 @@
[Desktop Entry]
Name=Podcasts
Comment=Listen to your favorite podcasts, right from your desktop.
Icon=org.gnome.Podcasts
Exec=gnome-podcasts
Terminal=false
Type=Application
StartupNotify=true
Categories=GNOME;GTK;AudioVideo;Audio;
Keywords=Podcast;RSS;
DBusActivatable=true
@@ -0,0 +1,13 @@
[Desktop Entry]
Name=Podcasts
Comment=Listen to your favorite podcasts, right from your desktop.
# Translators: Do NOT translate or transliterate this text (this is an icon file name)!
Icon=@icon@
Exec=gnome-podcasts
Terminal=false
Type=Application
StartupNotify=true
Categories=GNOME;GTK;AudioVideo;Audio;
# Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!
Keywords=Podcast;RSS;
DBusActivatable=true
@@ -9,7 +9,7 @@
<value nick="months" value="6"/>
</enum>
<schema path="/org/gnome/Podcasts/" id="org.gnome.Podcasts">
<schema path="/org/gnome/Podcasts/" id="org.gnome.Podcasts" gettext-domain="gnome-podcasts">
<key name="persist-window-geometry-top" type="i">
<default>-1</default>
<summary>Top position of the last open main window</summary>
@@ -64,4 +64,4 @@
<summary>What period of time to wait between automatic cleanups</summary>
</key>
</schema>
</schemalist>
</schemalist>
@@ -1,3 +1,3 @@
[D-BUS Service]
Name=org.gnome.Podcasts
Exec=@BINDIR@/gnome-podcasts --gapplication-service
Name=@appid@
Exec=@bindir@/gnome-podcasts --gapplication-service
+1 -4
View File
@@ -4,11 +4,8 @@
<file compressed="true" preprocess="xml-stripblanks">gtk/episode_widget.ui</file>
<file compressed="true" preprocess="xml-stripblanks">gtk/show_widget.ui</file>
<file compressed="true" preprocess="xml-stripblanks">gtk/empty_view.ui</file>
<file compressed="true" preprocess="xml-stripblanks">gtk/empty_show.ui</file>
<file compressed="true" preprocess="xml-stripblanks">gtk/home_view.ui</file>
<file compressed="true" preprocess="xml-stripblanks">gtk/home_episode.ui</file>
<file compressed="true" preprocess="xml-stripblanks">gtk/shows_view.ui</file>
<file compressed="true" preprocess="xml-stripblanks">gtk/shows_child.ui</file>
<file compressed="true" preprocess="xml-stripblanks">gtk/headerbar.ui</file>
<file compressed="true" preprocess="xml-stripblanks">gtk/inapp_notif.ui</file>
<file compressed="true" preprocess="xml-stripblanks">gtk/hamburger.ui</file>
@@ -20,4 +17,4 @@
<file compressed="true" preprocess="xml-stripblanks">gtk/prefs.ui</file>
<file compressed="true">gtk/style.css</file>
</gresource>
</gresources>
</gresources>
+155 -61
View File
@@ -1,13 +1,15 @@
#![allow(new_without_default)]
use gio::{self, prelude::*, ApplicationFlags, SettingsBindFlags, SettingsExt, SimpleAction};
use glib;
use gio::{self, prelude::*, ActionMapExt, SettingsExt};
use glib::{self, Variant};
use gtk;
use gtk::prelude::*;
use gettextrs::{bindtextdomain, setlocale, textdomain, LocaleCategory};
use crossbeam_channel::{unbounded, Receiver, Sender};
use fragile::Fragile;
use podcasts_data::Show;
use send_cell::SendCell;
use headerbar::Header;
use prefs::Prefs;
@@ -15,32 +17,36 @@ use settings::{self, WindowGeometry};
use stacks::{Content, PopulatedState};
use utils;
use widgets::about_dialog;
use widgets::appnotif::{InAppNotification, UndoState};
use widgets::appnotif::{InAppNotification, SpinnerState, State};
use widgets::player;
use widgets::show_menu::{mark_all_notif, remove_show_notif, ShowMenu};
use std::cell::RefCell;
use std::env;
use std::rc::Rc;
use std::sync::Arc;
pub const APP_ID: &str = "org.gnome.Podcasts";
use i18n::i18n;
/// Creates an action named $called in the action map $on with the handler $handle
macro_rules! action {
($on:expr, $called:expr, $handle:expr) => {{
// Create a stateless, parameterless action
let act = SimpleAction::new($called, None);
// Connect the handler
act.connect_activate($handle);
// Add it to the map
$on.add_action(&act);
// Return the action
act
}};
pub(crate) const APP_ID: &str = env!("APP_ID");
pub(crate) const VERSION: &str = env!("VERSION");
/// Creates an action named `name` in the action map `T with the handler `F`
fn action<T, F>(thing: &T, name: &str, action: F)
where
T: ActionMapExt,
for<'r, 's> F: Fn(&'r gio::SimpleAction, &'s Option<Variant>) + 'static,
{
// Create a stateless, parameterless action
let act = gio::SimpleAction::new(name, None);
// Connect the handler
act.connect_activate(action);
// Add it to the map
thing.add_action(&act);
}
#[derive(Debug, Clone)]
pub enum Action {
pub(crate) enum Action {
RefreshAllViews,
RefreshEpisodesView,
RefreshEpisodesViewBGR,
@@ -51,17 +57,19 @@ pub enum Action {
ShowShowsAnimated,
HeaderBarShowTile(String),
HeaderBarNormal,
HeaderBarShowUpdateIndicator,
HeaderBarHideUpdateIndicator,
MarkAllPlayerNotification(Arc<Show>),
ShowUpdateNotif(Receiver<bool>),
RemoveShow(Arc<Show>),
ErrorNotification(String),
InitEpisode(i32),
InitShowMenu(SendCell<ShowMenu>),
InitShowMenu(Fragile<ShowMenu>),
EmptyState,
PopulatedState,
RaiseWindow,
}
#[derive(Debug, Clone)]
pub struct App {
pub(crate) struct App {
instance: gtk::Application,
window: gtk::ApplicationWindow,
overlay: gtk::Overlay,
@@ -69,29 +77,46 @@ pub struct App {
content: Rc<Content>,
headerbar: Rc<Header>,
player: Rc<player::PlayerWidget>,
updater: RefCell<Option<InAppNotification>>,
sender: Sender<Action>,
receiver: Receiver<Action>,
}
impl App {
pub fn new(application: &gtk::Application) -> Rc<Self> {
let settings = gio::Settings::new(APP_ID);
pub(crate) fn new(application: &gtk::Application) -> Rc<Self> {
let settings = gio::Settings::new("org.gnome.Podcasts");
let (sender, receiver) = unbounded();
let window = gtk::ApplicationWindow::new(application);
window.set_title("Podcasts");
window.set_title(&i18n("Podcasts"));
if APP_ID.ends_with("Devel") {
window.get_style_context().map(|c| c.add_class("devel"));
}
let weak_s = settings.downgrade();
let weak_app = application.downgrade();
window.connect_delete_event(move |window, _| {
weak_s
.upgrade()
.map(|settings| WindowGeometry::from_window(&window).write(&settings));
let app = match weak_app.upgrade() {
Some(a) => a,
None => return Inhibit(false),
};
let settings = match weak_s.upgrade() {
Some(s) => s,
None => return Inhibit(false),
};
info!("Saving window position");
WindowGeometry::from_window(&window).write(&settings);
info!("Application is exiting");
app.quit();
Inhibit(false)
});
// Create a content instance
let content = Content::new(&sender).expect("Content Initialization failed.");
let content = Content::new(&sender).expect("Content initialization failed.");
// Create the headerbar
let header = Header::new(&content, &sender);
@@ -110,6 +135,8 @@ impl App {
// Add the player to the main Box
wrap.add(&player.action_bar);
let updater = RefCell::new(None);
window.add(&wrap);
let app = App {
@@ -120,6 +147,7 @@ impl App {
headerbar: header,
content,
player,
updater,
sender,
receiver,
};
@@ -154,7 +182,7 @@ impl App {
"dark-theme",
&gtk_settings,
"gtk-application-prefer-dark-theme",
SettingsBindFlags::DEFAULT,
gio::SettingsBindFlags::DEFAULT,
);
}
@@ -187,15 +215,12 @@ impl App {
#[cfg_attr(rustfmt, rustfmt_skip)]
fn setup_gactions(&self) {
let sender = &self.sender;
let win = &self.window;
let instance = &self.instance;
let header = &self.headerbar;
let settings = &self.settings;
let weak_win = self.window.downgrade();
// Create the `refresh` action.
//
// This will trigger a refresh of all the shows in the database.
action!(win, "refresh", clone!(sender => move |_, _| {
action(&self.window, "refresh", clone!(sender => move |_, _| {
gtk::idle_add(clone!(sender => move || {
let s: Option<Vec<_>> = None;
utils::refresh(s, sender.clone());
@@ -205,29 +230,39 @@ impl App {
self.instance.set_accels_for_action("win.refresh", &["<primary>r"]);
// Create the `OPML` import action
action!(win, "import", clone!(sender, win => move |_, _| {
utils::on_import_clicked(&win, &sender)
action(&self.window, "import", clone!(sender, weak_win => move |_, _| {
weak_win.upgrade().map(|win| utils::on_import_clicked(&win, &sender));
}));
// Create the action that shows a `gtk::AboutDialog`
action!(win, "about", clone!(win => move |_, _| about_dialog(&win)));
action(&self.window, "about", clone!(weak_win => move |_, _| {
weak_win.upgrade().map(|win| about_dialog(&win));
}));
// Create the quit action
action!(win, "quit", clone!(instance => move |_, _| instance.quit()));
let weak_instance = self.instance.downgrade();
action(&self.window, "quit", move |_, _| {
weak_instance.upgrade().map(|app| app.quit());
});
self.instance.set_accels_for_action("win.quit", &["<primary>q"]);
action!(
win,
"preferences",
clone!(win, settings => move |_, _| {
let dialog = Prefs::new(&settings);
dialog.show(&win);
})
);
let weak_settings = self.settings.downgrade();
action(&self.window, "preferences", clone!(weak_win => move |_, _| {
let (win, settings) = match (weak_win.upgrade(), weak_settings.upgrade()) {
(Some(win), Some(settings)) => (win, settings),
_ => return,
};
let dialog = Prefs::new(&settings);
dialog.show(&win);
}));
self.instance.set_accels_for_action("win.preferences", &["<primary>e"]);
// Create the menu action
action!(win, "menu",clone!(header => move |_, _| header.open_menu()));
let header = Rc::downgrade(&self.headerbar);
action(&self.window, "menu", move |_, _| {
header.upgrade().map(|h| h.open_menu());
});
// Bind the hamburger menu button to `F10`
self.instance.set_accels_for_action("win.menu", &["F10"]);
}
@@ -247,7 +282,7 @@ impl App {
pop.borrow_mut()
.replace_widget(pd.clone())
.map_err(|err| error!("Failed to update ShowWidget: {}", err))
.map_err(|_| error!("Failed ot update ShowWidget {}", pd.title()))
.map_err(|_| error!("Failed to update ShowWidget {}", pd.title()))
.ok();
}
Action::ShowWidgetAnimated => {
@@ -266,8 +301,6 @@ impl App {
}
Action::HeaderBarShowTile(title) => self.headerbar.switch_to_back(&title),
Action::HeaderBarNormal => self.headerbar.switch_to_normal(),
Action::HeaderBarShowUpdateIndicator => self.headerbar.show_update_notification(),
Action::HeaderBarHideUpdateIndicator => self.headerbar.hide_update_notification(),
Action::MarkAllPlayerNotification(pd) => {
let notif = mark_all_notif(pd, &self.sender);
notif.show(&self.overlay);
@@ -278,27 +311,82 @@ impl App {
}
Action::ErrorNotification(err) => {
error!("An error notification was triggered: {}", err);
let callback = || glib::Continue(false);
let notif = InAppNotification::new(&err, callback, || {}, UndoState::Hidden);
let callback = |revealer: gtk::Revealer| {
revealer.set_reveal_child(false);
glib::Continue(false)
};
let undo_cb: Option<fn()> = None;
let notif = InAppNotification::new(&err, 6000, callback, undo_cb);
notif.show(&self.overlay);
}
Action::ShowUpdateNotif(receiver) => {
let sender = self.sender.clone();
let callback = move |revealer: gtk::Revealer| {
if let Some(_) = receiver.try_recv() {
revealer.set_reveal_child(false);
sender.send(Action::RefreshAllViews);
return glib::Continue(false);
}
glib::Continue(true)
};
let txt = i18n("Fetching new episodes");
let undo_cb: Option<fn()> = None;
let updater = InAppNotification::new(&txt, 250, callback, undo_cb);
updater.set_close_state(State::Hidden);
updater.set_spinner_state(SpinnerState::Active);
let old = self.updater.replace(Some(updater));
old.map(|i| i.destroy());
self.updater
.borrow()
.as_ref()
.map(|i| i.show(&self.overlay));
}
Action::InitEpisode(rowid) => {
let res = self.player.initialize_episode(rowid);
debug_assert!(res.is_ok());
}
Action::InitShowMenu(s) => {
let menu = s.borrow();
self.headerbar.set_secondary_menu(&menu.container);
let menu = &s.get().container;
self.headerbar.set_secondary_menu(menu);
}
Action::EmptyState => {
self.window
.lookup_action("refresh")
.and_then(|action| action.downcast::<gio::SimpleAction>().ok())
// Disable refresh action
.map(|action| action.set_enabled(false));
self.headerbar.switch.set_sensitive(false);
self.content.switch_to_empty_views();
}
Action::PopulatedState => {
self.window
.lookup_action("refresh")
.and_then(|action| action.downcast::<gio::SimpleAction>().ok())
// Enable refresh action
.map(|action| action.set_enabled(true));
self.headerbar.switch.set_sensitive(true);
self.content.switch_to_populated();
}
Action::RaiseWindow => self.window.present(),
}
}
glib::Continue(true)
}
pub fn run() {
let application = gtk::Application::new(APP_ID, ApplicationFlags::empty())
.expect("Application Initialization failed...");
pub(crate) fn run() {
// Set up the textdomain for gettext
setlocale(LocaleCategory::LcAll, "");
bindtextdomain("gnome-podcasts", env!("LOCALEDIR"));
textdomain("gnome-podcasts");
let application = gtk::Application::new(APP_ID, gio::ApplicationFlags::empty())
.expect("Application initialization failed...");
application.set_resource_base_path("/org/gnome/Podcasts");
let weak_app = application.downgrade();
application.connect_startup(move |_| {
@@ -310,16 +398,22 @@ impl App {
let weak = Rc::downgrade(&app);
application.connect_activate(move |_| {
info!("GApplication::activate");
weak.upgrade().map(|app| app.window.activate());
if let Some(app) = weak.upgrade() {
// Ideally Gtk4/GtkBuilder make this irrelvent
app.window.show_all();
app.window.present();
info!("Window presented");
} else {
debug_assert!(false, "I hate computers");
}
});
info!("Init complete");
app.window.show_all();
});
});
// Weird magic I copy-pasted that sets the Application Name in the Shell.
glib::set_application_name("Podcasts");
glib::set_application_name(&i18n("Podcasts"));
glib::set_prgname(Some("gnome-podcasts"));
gtk::Window::set_default_icon_name(APP_ID);
let args: Vec<String> = env::args().collect();
+14 -56
View File
@@ -15,43 +15,20 @@ use utils::{itunes_to_rss, refresh};
use std::rc::Rc;
use i18n::i18n;
#[derive(Debug, Clone)]
// TODO: Factor out the hamburger menu
// TODO: Make a proper state machine for the headerbar states
pub struct Header {
pub container: gtk::HeaderBar,
switch: gtk::StackSwitcher,
pub(crate) struct Header {
pub(crate) container: gtk::HeaderBar,
pub(crate) switch: gtk::StackSwitcher,
back: gtk::Button,
show_title: gtk::Label,
hamburger: gtk::MenuButton,
updater: UpdateIndicator,
add: AddPopover,
dots: gtk::MenuButton,
}
#[derive(Debug, Clone)]
struct UpdateIndicator {
container: gtk::Box,
text: gtk::Label,
spinner: gtk::Spinner,
}
impl UpdateIndicator {
fn show(&self) {
self.spinner.start();
self.spinner.show();
self.container.show();
self.text.show();
}
fn hide(&self) {
self.spinner.stop();
self.spinner.hide();
self.container.hide();
self.text.hide();
}
}
#[derive(Debug, Clone)]
struct AddPopover {
container: gtk::Popover,
@@ -118,7 +95,7 @@ impl AddPopover {
} else {
self.add.set_sensitive(false);
self.result
.set_label("You are already subscribed to this Show");
.set_label(i18n("You are already subscribed to this show").as_str());
self.result.show();
}
Ok(())
@@ -126,7 +103,7 @@ impl AddPopover {
Err(err) => {
self.add.set_sensitive(false);
if !url.is_empty() {
self.result.set_label("Invalid url");
self.result.set_label(i18n("Invalid URL").as_str());
self.result.show();
error!("Error: {}", err);
} else {
@@ -156,16 +133,6 @@ impl Default for Header {
// The 3 dots secondary menu
let dots = builder.get_object("secondary_menu").unwrap();
let update_box = builder.get_object("update_notification").unwrap();
let update_label = builder.get_object("update_label").unwrap();
let update_spinner = builder.get_object("update_spinner").unwrap();
let updater = UpdateIndicator {
container: update_box,
text: update_label,
spinner: update_spinner,
};
let add_toggle = builder.get_object("add_toggle").unwrap();
let add_popover = builder.get_object("add_popover").unwrap();
let new_url = builder.get_object("new_url").unwrap();
@@ -185,7 +152,6 @@ impl Default for Header {
back,
show_title,
hamburger,
updater,
add,
dots,
}
@@ -194,13 +160,13 @@ impl Default for Header {
// TODO: Make a proper state machine for the headerbar states
impl Header {
pub fn new(content: &Content, sender: &Sender<Action>) -> Rc<Self> {
pub(crate) fn new(content: &Content, sender: &Sender<Action>) -> Rc<Self> {
let h = Rc::new(Header::default());
Self::init(&h, content, &sender);
h
}
pub fn init(s: &Rc<Self>, content: &Content, sender: &Sender<Action>) {
pub(crate) fn init(s: &Rc<Self>, content: &Content, sender: &Sender<Action>) {
let weak = Rc::downgrade(s);
s.switch.set_stack(&content.get_stack());
@@ -223,7 +189,7 @@ impl Header {
}));
}
pub fn switch_to_back(&self, title: &str) {
pub(crate) fn switch_to_back(&self, title: &str) {
self.switch.hide();
self.add.toggle.hide();
self.back.show();
@@ -233,7 +199,7 @@ impl Header {
self.dots.show();
}
pub fn switch_to_normal(&self) {
pub(crate) fn switch_to_normal(&self) {
self.switch.show();
self.add.toggle.show();
self.back.hide();
@@ -242,23 +208,15 @@ impl Header {
self.dots.hide();
}
pub fn set_show_title(&self, title: &str) {
pub(crate) fn set_show_title(&self, title: &str) {
self.show_title.set_text(title)
}
pub fn show_update_notification(&self) {
self.updater.show();
}
pub fn hide_update_notification(&self) {
self.updater.hide();
}
pub fn open_menu(&self) {
pub(crate) fn open_menu(&self) {
self.hamburger.clicked();
}
pub fn set_secondary_menu(&self, pop: &gtk::PopoverMenu) {
pub(crate) fn set_secondary_menu(&self, pop: &gtk::PopoverMenu) {
self.dots.set_popover(Some(pop));
}
}
+140
View File
@@ -0,0 +1,140 @@
extern crate gettextrs;
extern crate regex;
use self::gettextrs::gettext;
use self::gettextrs::ngettext;
use self::regex::Captures;
use self::regex::Regex;
#[allow(dead_code)]
fn freplace(input: String, args: &[&str]) -> String {
let mut parts = input.split("{}");
let mut output = parts.next().unwrap_or("").to_string();
for (p, a) in parts.zip(args.iter()) {
output += &(a.to_string() + &p.to_string());
}
output
}
#[allow(dead_code)]
fn kreplace(input: String, kwargs: &[(&str, &str)]) -> String {
let mut s = input.clone();
for (k, v) in kwargs {
if let Ok(re) = Regex::new(&format!("\\{{{}\\}}", k)) {
s = re
.replace_all(&s, |_: &Captures<'_>| v.to_string().clone())
.to_string();
}
}
s
}
#[allow(dead_code)]
pub(crate) fn i18n(format: &str) -> String {
gettext(format)
}
#[allow(dead_code)]
pub(crate) fn i18n_f(format: &str, args: &[&str]) -> String {
let s = gettext(format);
freplace(s, args)
}
#[allow(dead_code)]
pub(crate) fn i18n_k(format: &str, kwargs: &[(&str, &str)]) -> String {
let s = gettext(format);
kreplace(s, kwargs)
}
#[allow(dead_code)]
pub(crate) fn ni18n(single: &str, multiple: &str, number: u32) -> String {
ngettext(single, multiple, number)
}
#[allow(dead_code)]
pub(crate) fn ni18n_f(single: &str, multiple: &str, number: u32, args: &[&str]) -> String {
let s = ngettext(single, multiple, number);
freplace(s, args)
}
#[allow(dead_code)]
pub(crate) fn ni18n_k(
single: &str,
multiple: &str,
number: u32,
kwargs: &[(&str, &str)],
) -> String {
let s = ngettext(single, multiple, number);
kreplace(s, kwargs)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_i18n() {
let out = i18n("translate1");
assert_eq!(out, "translate1");
let out = ni18n("translate1", "translate multi", 1);
assert_eq!(out, "translate1");
let out = ni18n("translate1", "translate multi", 2);
assert_eq!(out, "translate multi");
}
#[test]
fn test_i18n_f() {
let out = i18n_f("{} param", &["one"]);
assert_eq!(out, "one param");
let out = i18n_f("middle {} param", &["one"]);
assert_eq!(out, "middle one param");
let out = i18n_f("end {}", &["one"]);
assert_eq!(out, "end one");
let out = i18n_f("multiple {} and {}", &["one", "two"]);
assert_eq!(out, "multiple one and two");
let out = ni18n_f("singular {} and {}", "plural {} and {}", 2, &["one", "two"]);
assert_eq!(out, "plural one and two");
let out = ni18n_f("singular {} and {}", "plural {} and {}", 1, &["one", "two"]);
assert_eq!(out, "singular one and two");
}
#[test]
fn test_i18n_k() {
let out = i18n_k("{one} param", &[("one", "one")]);
assert_eq!(out, "one param");
let out = i18n_k("middle {one} param", &[("one", "one")]);
assert_eq!(out, "middle one param");
let out = i18n_k("end {one}", &[("one", "one")]);
assert_eq!(out, "end one");
let out = i18n_k("multiple {one} and {two}", &[("one", "1"), ("two", "two")]);
assert_eq!(out, "multiple 1 and two");
let out = i18n_k("multiple {two} and {one}", &[("one", "1"), ("two", "two")]);
assert_eq!(out, "multiple two and 1");
let out = i18n_k("multiple {one} and {one}", &[("one", "1"), ("two", "two")]);
assert_eq!(out, "multiple 1 and 1");
let out = ni18n_k(
"singular {one} and {two}",
"plural {one} and {two}",
1,
&[("one", "1"), ("two", "two")],
);
assert_eq!(out, "singular 1 and two");
let out = ni18n_k(
"singular {one} and {two}",
"plural {one} and {two}",
2,
&[("one", "1"), ("two", "two")],
);
assert_eq!(out, "plural 1 and two");
}
}
+47 -1
View File
@@ -60,8 +60,11 @@ extern crate pretty_assertions;
extern crate chrono;
extern crate crossbeam_channel;
extern crate fragile;
extern crate gettextrs;
extern crate html2text;
extern crate humansize;
extern crate libhandy;
extern crate loggerv;
extern crate open;
extern crate podcasts_data;
@@ -69,9 +72,9 @@ extern crate podcasts_downloader;
extern crate rayon;
extern crate regex;
extern crate reqwest;
extern crate send_cell;
extern crate serde_json;
extern crate url;
extern crate mpris_player;
use log::Level;
@@ -108,8 +111,25 @@ mod settings;
mod static_resource;
mod utils;
mod i18n;
use app::App;
#[cfg(test)]
fn init_gtk_tests() -> Result<(), failure::Error> {
// if gtk::is_initialized() {
// assert!(gtk::is_initialized_main_thread())
// } else {
// gtk::init()?;
// static_resource::init()?;
// }
gtk::init()?;
static_resource::init()?;
gst::init()?;
Ok(())
}
fn main() {
// TODO: make the the logger a cli -vv option
loggerv::init_with_level(Level::Info).expect("Error initializing loggerv.");
@@ -128,3 +148,29 @@ fn main() {
App::run();
}
#[test]
// Even while running the tests with -j 1 and --test-threads=1,
// cargo seems to create new threads and gtk refuses to initialize again.
// So we run every gtk related test here.
fn test_stuff() -> Result<(), failure::Error> {
use headerbar::Header;
use widgets::*;
init_gtk_tests()?;
// If a widget does not exist in the `GtkBuilder`(.ui) file this should panic and fail.
Header::default();
ShowsView::default();
ShowWidget::default();
HomeView::default();
HomeEpisode::default();
EpisodeWidget::default();
EmptyView::default();
EmptyShow::default();
appnotif::InAppNotification::default();
show_menu::ShowMenu::default();
Ok(())
}
+49 -42
View File
@@ -11,10 +11,10 @@ use std::sync::{Arc, Mutex, RwLock};
// use std::path::PathBuf;
// This is messy, undocumented and hacky af.
// I am terrible at writting downloaders and download managers.
// I am terrible at writing downloaders and download managers.
#[derive(Debug)]
pub struct Progress {
pub(crate) struct Progress {
total_bytes: u64,
downloaded_bytes: u64,
cancel: bool,
@@ -31,7 +31,7 @@ impl Default for Progress {
}
impl Progress {
pub fn get_fraction(&self) -> f64 {
pub(crate) fn get_fraction(&self) -> f64 {
let ratio = self.downloaded_bytes as f64 / self.total_bytes as f64;
debug!("{:?}", self);
debug!("Ratio completed: {}", ratio);
@@ -41,21 +41,13 @@ impl Progress {
};
ratio
}
pub fn get_total_size(&self) -> u64 {
self.total_bytes
}
pub fn get_downloaded(&self) -> u64 {
self.downloaded_bytes
}
pub fn cancel(&mut self) {
self.cancel = true;
}
}
impl DownloadProgress for Progress {
fn get_downloaded(&self) -> u64 {
self.downloaded_bytes
}
fn set_downloaded(&mut self, downloaded: u64) {
self.downloaded_bytes = downloaded
}
@@ -64,24 +56,32 @@ impl DownloadProgress for Progress {
self.total_bytes = bytes;
}
fn get_size(&self) -> u64 {
self.total_bytes
}
fn should_cancel(&self) -> bool {
self.cancel
}
fn cancel(&mut self) {
self.cancel = true;
}
}
lazy_static! {
pub static ref ACTIVE_DOWNLOADS: Arc<RwLock<HashMap<i32, Arc<Mutex<Progress>>>>> =
pub(crate) static ref ACTIVE_DOWNLOADS: Arc<RwLock<HashMap<i32, Arc<Mutex<Progress>>>>> =
{ Arc::new(RwLock::new(HashMap::new())) };
static ref DLPOOL: rayon::ThreadPool = rayon::ThreadPoolBuilder::new().build().unwrap();
}
pub fn add(id: i32, directory: String) -> Result<(), Error> {
pub(crate) fn add(id: i32, directory: String) -> Result<(), Error> {
// Create a new `Progress` struct to keep track of dl progress.
let prog = Arc::new(Mutex::new(Progress::default()));
match ACTIVE_DOWNLOADS.write() {
Ok(mut guard) => guard.insert(id, prog.clone()),
Err(err) => return Err(format_err!("ActiveDonwloads: {}.", err)),
Err(err) => return Err(format_err!("ActiveDownloads: {}.", err)),
};
DLPOOL.spawn(move || {
@@ -113,7 +113,7 @@ mod tests {
use podcasts_data::dbqueries;
use podcasts_data::pipeline;
use podcasts_data::utils::get_download_folder;
use podcasts_data::{Episode, Source};
use podcasts_data::{Episode, Save, Source};
use podcasts_downloader::downloader::get_episode;
@@ -127,58 +127,65 @@ mod tests {
#[ignore]
// THIS IS NOT A RELIABLE TEST
// Just quick sanity check
fn test_start_dl() {
fn test_start_dl() -> Result<(), Error> {
let url = "https://web.archive.org/web/20180120110727if_/https://rss.acast.com/thetipoff";
// Create and index a source
let source = Source::from_url(url).unwrap();
// Copy it's id
let mut source = Source::from_url(url)?;
// Copy its id
let sid = source.id();
pipeline::run(vec![source], true).unwrap();
source.set_http_etag(None);
source.set_last_modified(None);
source.save()?;
pipeline::run(vec![source])?;
// Get the Podcast
let pd = dbqueries::get_podcast_from_source_id(sid).unwrap();
// Get the podcast
let pd = dbqueries::get_podcast_from_source_id(sid)?;
let title = "Coming soon... The Tip Off";
// Get an episode
let episode: Episode = dbqueries::get_episode_from_pk(title, pd.id()).unwrap();
let episode: Episode = dbqueries::get_episode_from_pk(title, pd.id())?;
let download_fold = get_download_folder(&pd.title()).unwrap();
let download_fold = get_download_folder(&pd.title())?;
let fold2 = download_fold.clone();
add(episode.rowid(), download_fold).unwrap();
add(episode.rowid(), download_fold)?;
assert_eq!(ACTIVE_DOWNLOADS.read().unwrap().len(), 1);
// Give it soem time to download the file
// Give it some time to download the file
thread::sleep(time::Duration::from_secs(20));
let final_path = format!("{}/{}.mp3", &fold2, episode.rowid());
assert_eq!(ACTIVE_DOWNLOADS.read().unwrap().len(), 0);
assert!(Path::new(&final_path).exists());
fs::remove_file(final_path).unwrap();
fs::remove_file(final_path)?;
Ok(())
}
#[test]
// This test needs access to local system so we ignore it by default.
#[ignore]
fn test_dl_steal_the_stars() {
fn test_dl_steal_the_stars() -> Result<(), Error> {
let url =
"https://web.archive.org/web/20180120104957if_/https://rss.art19.com/steal-the-stars";
// Create and index a source
let source = Source::from_url(url).unwrap();
// Copy it's id
let mut source = Source::from_url(url)?;
// Copy its id
let sid = source.id();
pipeline::run(vec![source], true).unwrap();
source.set_http_etag(None);
source.set_last_modified(None);
source.save()?;
pipeline::run(vec![source])?;
// Get the Podcast
let pd = dbqueries::get_podcast_from_source_id(sid).unwrap();
// Get the podcast
let pd = dbqueries::get_podcast_from_source_id(sid)?;
let title = "Introducing Steal the Stars";
// Get an episode
let mut episode = dbqueries::get_episode_from_pk(title, pd.id())
.unwrap()
.into();
let download_fold = get_download_folder(&pd.title()).unwrap();
let mut episode = dbqueries::get_episode_from_pk(title, pd.id())?.into();
let download_fold = get_download_folder(&pd.title())?;
get_episode(&mut episode, &download_fold, None).unwrap();
get_episode(&mut episode, &download_fold, None)?;
let final_path = format!("{}/{}.mp3", &download_fold, episode.rowid());
assert!(Path::new(&final_path).exists());
fs::remove_file(final_path).unwrap();
fs::remove_file(final_path)?;
Ok(())
}
}
+14 -6
View File
@@ -3,8 +3,10 @@ use gio::{Settings, SettingsExt};
use gtk;
use gtk::prelude::*;
use i18n::i18n;
#[derive(Debug, Clone)]
pub struct Prefs {
pub(crate) struct Prefs {
dialog: gtk::Window,
dark_toggle: gtk::Switch,
cleanup_value: gtk::SpinButton,
@@ -31,13 +33,13 @@ impl Default for Prefs {
// TODO: Refactor components into smaller state machines
impl Prefs {
pub fn new(settings: &Settings) -> Prefs {
pub(crate) fn new(settings: &Settings) -> Prefs {
let h = Prefs::default();
h.init(settings);
h
}
pub fn init(&self, settings: &Settings) {
pub(crate) fn init(&self, settings: &Settings) {
settings.bind(
"dark-theme",
&self.dark_toggle,
@@ -53,11 +55,17 @@ impl Prefs {
let cleanup_p = settings.get_string("cleanup-age-period").unwrap();
let mut cleanup_pos = 0;
let store = gtk::ListStore::new(&[gtk::Type::String]);
for (i, item) in ["Seconds", "Minutes", "Hours", "Days", "Weeks"]
for (i, item) in [
i18n("Seconds"),
i18n("Minutes"),
i18n("Hours"),
i18n("Days"),
i18n("Weeks"),
]
.iter()
.enumerate()
{
let row: &[&ToValue] = &[item];
let row: &[&dyn ToValue] = &[item];
if item.to_lowercase() == cleanup_p {
cleanup_pos = i as i32;
}
@@ -85,7 +93,7 @@ impl Prefs {
});
}
pub fn show(&self, parent: &gtk::ApplicationWindow) {
pub(crate) fn show(&self, parent: &gtk::ApplicationWindow) {
self.dialog.set_transient_for(Some(parent));
self.dialog.set_modal(true);
self.dialog.show_all();
+8 -8
View File
@@ -6,7 +6,7 @@ use gtk::GtkWindowExt;
use chrono::prelude::*;
use chrono::Duration;
pub struct WindowGeometry {
pub(crate) struct WindowGeometry {
left: i32,
top: i32,
width: i32,
@@ -15,7 +15,7 @@ pub struct WindowGeometry {
}
impl WindowGeometry {
pub fn from_window(window: &gtk::ApplicationWindow) -> WindowGeometry {
pub(crate) fn from_window(window: &gtk::ApplicationWindow) -> WindowGeometry {
let position = window.get_position();
let size = window.get_size();
let left = position.0;
@@ -33,7 +33,7 @@ impl WindowGeometry {
}
}
pub fn from_settings(settings: &gio::Settings) -> WindowGeometry {
pub(crate) fn from_settings(settings: &gio::Settings) -> WindowGeometry {
let top = settings.get_int("persist-window-geometry-top");
let left = settings.get_int("persist-window-geometry-left");
let width = settings.get_int("persist-window-geometry-width");
@@ -49,7 +49,7 @@ impl WindowGeometry {
}
}
pub fn apply(&self, window: &gtk::ApplicationWindow) {
pub(crate) fn apply(&self, window: &gtk::ApplicationWindow) {
if self.width > 0 && self.height > 0 {
window.resize(self.width, self.height);
}
@@ -61,7 +61,7 @@ impl WindowGeometry {
}
}
pub fn write(&self, settings: &gio::Settings) {
pub(crate) fn write(&self, settings: &gio::Settings) {
settings.set_int("persist-window-geometry-left", self.left);
settings.set_int("persist-window-geometry-top", self.top);
settings.set_int("persist-window-geometry-width", self.width);
@@ -70,14 +70,14 @@ impl WindowGeometry {
}
}
pub fn get_refresh_interval(settings: &Settings) -> Duration {
pub(crate) fn get_refresh_interval(settings: &Settings) -> Duration {
let time = i64::from(settings.get_int("refresh-interval-time"));
let period = settings.get_string("refresh-interval-period").unwrap();
time_period_to_duration(time, period.as_str())
}
pub fn get_cleanup_date(settings: &Settings) -> DateTime<Utc> {
pub(crate) fn get_cleanup_date(settings: &Settings) -> DateTime<Utc> {
let time = i64::from(settings.get_int("cleanup-age-time"));
let period = settings.get_string("cleanup-age-period").unwrap();
let duration = time_period_to_duration(time, period.as_str());
@@ -85,7 +85,7 @@ pub fn get_cleanup_date(settings: &Settings) -> DateTime<Utc> {
Utc::now() - duration
}
pub fn time_period_to_duration(time: i64, period: &str) -> Duration {
pub(crate) fn time_period_to_duration(time: i64, period: &str) -> Duration {
match period {
"weeks" => Duration::weeks(time),
"days" => Duration::days(time),
+37 -11
View File
@@ -10,8 +10,16 @@ use stacks::{HomeStack, ShowStack};
use std::cell::RefCell;
use std::rc::Rc;
use i18n::i18n;
#[derive(Debug, Clone, Copy)]
pub(crate) enum State {
Populated,
Empty,
}
#[derive(Debug, Clone)]
pub struct Content {
pub(crate) struct Content {
stack: gtk::Stack,
shows: Rc<RefCell<ShowStack>>,
home: Rc<RefCell<HomeStack>>,
@@ -19,13 +27,13 @@ pub struct Content {
}
impl Content {
pub fn new(sender: &Sender<Action>) -> Result<Rc<Content>, Error> {
pub(crate) fn new(sender: &Sender<Action>) -> Result<Rc<Content>, Error> {
let stack = gtk::Stack::new();
let home = Rc::new(RefCell::new(HomeStack::new(sender.clone())?));
let shows = Rc::new(RefCell::new(ShowStack::new(sender.clone())));
stack.add_titled(&home.borrow().get_stack(), "home", "New");
stack.add_titled(&shows.borrow().get_stack(), "shows", "Shows");
stack.add_titled(&home.borrow().get_stack(), "home", &i18n("New"));
stack.add_titled(&shows.borrow().get_stack(), "shows", &i18n("Shows"));
let con = Content {
stack,
@@ -36,12 +44,12 @@ impl Content {
Ok(Rc::new(con))
}
pub fn update(&self) {
pub(crate) fn update(&self) {
self.update_home();
self.update_shows();
}
pub fn update_home(&self) {
pub(crate) fn update_home(&self) {
self.home
.borrow_mut()
.update()
@@ -49,7 +57,7 @@ impl Content {
.ok();
}
pub fn update_home_if_background(&self) {
pub(crate) fn update_home_if_background(&self) {
if self.stack.get_visible_child_name() != Some("home".into()) {
self.update_home();
}
@@ -63,7 +71,7 @@ impl Content {
.ok();
}
pub fn update_shows_view(&self) {
pub(crate) fn update_shows_view(&self) {
self.shows
.borrow_mut()
.update()
@@ -71,7 +79,7 @@ impl Content {
.ok();
}
pub fn update_widget_if_same(&self, pid: i32) {
pub(crate) fn update_widget_if_same(&self, pid: i32) {
let pop = self.shows.borrow().populated();
pop.borrow_mut()
.update_widget_if_same(pid)
@@ -79,11 +87,29 @@ impl Content {
.ok();
}
pub fn get_stack(&self) -> gtk::Stack {
pub(crate) fn get_stack(&self) -> gtk::Stack {
self.stack.clone()
}
pub fn get_shows(&self) -> Rc<RefCell<ShowStack>> {
pub(crate) fn get_shows(&self) -> Rc<RefCell<ShowStack>> {
self.shows.clone()
}
pub(crate) fn switch_to_empty_views(&self) {
use gtk::StackTransitionType::*;
self.home
.borrow_mut()
.switch_visible(State::Empty, Crossfade);
self.shows.borrow_mut().switch_visible(State::Empty);
}
pub(crate) fn switch_to_populated(&self) {
use gtk::StackTransitionType::*;
self.home
.borrow_mut()
.switch_visible(State::Populated, Crossfade);
self.shows.borrow_mut().switch_visible(State::Populated);
}
}
+21 -47
View File
@@ -4,22 +4,16 @@ use gtk::StackTransitionType;
use crossbeam_channel::Sender;
use failure::Error;
use podcasts_data::dbqueries::is_episodes_populated;
use podcasts_data::errors::DataError;
use app::Action;
use stacks::State;
use widgets::{EmptyView, HomeView};
use std::ops::Deref;
use std::rc::Rc;
#[derive(Debug, Clone, Copy)]
enum State {
Home,
Empty,
}
#[derive(Debug, Clone)]
pub struct HomeStack {
pub(crate) struct HomeStack {
empty: EmptyView,
episodes: Rc<HomeView>,
stack: gtk::Stack,
@@ -28,16 +22,16 @@ pub struct HomeStack {
}
impl HomeStack {
pub fn new(sender: Sender<Action>) -> Result<HomeStack, Error> {
let episodes = HomeView::new(sender.clone())?;
let empty = EmptyView::new();
pub(crate) fn new(sender: Sender<Action>) -> Result<HomeStack, Error> {
let episodes = HomeView::new(sender.clone(), None)?;
let empty = EmptyView::default();
let stack = gtk::Stack::new();
let state = State::Empty;
stack.add_named(&episodes.container, "home");
stack.add_named(&empty.container, "empty");
stack.add_named(episodes.view.container(), "home");
stack.add_named(empty.deref(), "empty");
let mut home = HomeStack {
let home = HomeStack {
empty,
episodes,
stack,
@@ -45,36 +39,26 @@ impl HomeStack {
sender,
};
home.determine_state()?;
Ok(home)
}
pub fn get_stack(&self) -> gtk::Stack {
pub(crate) fn get_stack(&self) -> gtk::Stack {
self.stack.clone()
}
pub fn update(&mut self) -> Result<(), Error> {
// Copy the vertical scrollbar adjustment from the old view.
self.episodes
.save_alignment()
.map_err(|err| error!("Failed to set episodes_view allignment: {}", err))
.ok();
self.replace_view()?;
// Determine the actuall state.
self.determine_state().map_err(From::from)
}
fn replace_view(&mut self) -> Result<(), Error> {
pub(crate) fn update(&mut self) -> Result<(), Error> {
// Get the container of the view
let old = &self.episodes.container.clone();
let eps = HomeView::new(self.sender.clone())?;
let old = &self.episodes.view.container().clone();
// Copy the vertical scrollbar adjustment from the old view.
let vadj = self.episodes.view.get_vadjustment();
let eps = HomeView::new(self.sender.clone(), vadj)?;
// Remove the old widget and add the new one
// during this the previous view is removed,
// and the visibile child fallsback to empty view.
// and the visible child falls back to empty view.
self.stack.remove(old);
self.stack.add_named(&eps.container, "home");
self.stack.add_named(eps.view.container(), "home");
// Keep the previous state.
let s = self.state;
// Set the visible child back to the previous one to avoid
@@ -90,13 +74,13 @@ impl HomeStack {
Ok(())
}
fn switch_visible(&mut self, s: State, animation: StackTransitionType) {
pub(crate) fn switch_visible(&mut self, s: State, animation: StackTransitionType) {
use self::State::*;
match s {
Home => {
Populated => {
self.stack.set_visible_child_full("home", animation);
self.state = Home;
self.state = Populated;
}
Empty => {
self.stack.set_visible_child_full("empty", animation);
@@ -104,14 +88,4 @@ impl HomeStack {
}
}
}
fn determine_state(&mut self) -> Result<(), DataError> {
if is_episodes_populated()? {
self.switch_visible(State::Home, StackTransitionType::Crossfade);
} else {
self.switch_visible(State::Empty, StackTransitionType::Crossfade);
};
Ok(())
}
}
+4 -4
View File
@@ -3,7 +3,7 @@ mod home;
mod populated;
mod show;
pub use self::content::Content;
pub use self::home::HomeStack;
pub use self::populated::{PopulatedStack, PopulatedState};
pub use self::show::{ShowStack, ShowState};
pub(crate) use self::content::{Content, State};
pub(crate) use self::home::HomeStack;
pub(crate) use self::populated::{PopulatedStack, PopulatedState};
pub(crate) use self::show::ShowStack;
+30 -28
View File
@@ -15,13 +15,13 @@ use std::rc::Rc;
use std::sync::Arc;
#[derive(Debug, Clone, Copy)]
pub enum PopulatedState {
pub(crate) enum PopulatedState {
View,
Widget,
}
#[derive(Debug, Clone)]
pub struct PopulatedStack {
pub(crate) struct PopulatedStack {
container: gtk::Box,
populated: Rc<ShowsView>,
show: Rc<ShowWidget>,
@@ -31,15 +31,15 @@ pub struct PopulatedStack {
}
impl PopulatedStack {
pub fn new(sender: Sender<Action>) -> PopulatedStack {
pub(crate) fn new(sender: Sender<Action>) -> PopulatedStack {
let stack = gtk::Stack::new();
let state = PopulatedState::View;
let populated = ShowsView::new(sender.clone());
let populated = ShowsView::new(sender.clone(), None);
let show = Rc::new(ShowWidget::default());
let container = gtk::Box::new(gtk::Orientation::Horizontal, 0);
stack.add_named(&populated.container, "shows");
stack.add_named(&show.container, "widget");
stack.add_named(populated.view.container(), "shows");
stack.add_named(show.view.container(), "widget");
container.add(&stack);
container.show_all();
@@ -53,12 +53,12 @@ impl PopulatedStack {
}
}
pub fn update(&mut self) {
pub(crate) fn update(&mut self) {
self.update_widget().map_err(|err| format!("{}", err)).ok();
self.update_shows().map_err(|err| format!("{}", err)).ok();
}
pub fn update_shows(&mut self) -> Result<(), Error> {
pub(crate) fn update_shows(&mut self) -> Result<(), Error> {
// The current visible child might change depending on
// removal and insertion in the gtk::Stack, so we have
// to make sure it will stay the same.
@@ -69,34 +69,36 @@ impl PopulatedStack {
Ok(())
}
pub fn replace_shows(&mut self) -> Result<(), Error> {
let old = &self.populated.container.clone();
pub(crate) fn replace_shows(&mut self) -> Result<(), Error> {
let old = &self.populated.view.container().clone();
debug!("Name: {:?}", WidgetExt::get_name(old));
self.populated
.save_alignment()
.map_err(|err| error!("Failed to set episodes_view allignment: {}", err))
.ok();
let pop = ShowsView::new(self.sender.clone());
let vadj = self.populated.view.get_vadjustment();
let pop = ShowsView::new(self.sender.clone(), vadj);
self.populated = pop;
self.stack.remove(old);
self.stack.add_named(&self.populated.container, "shows");
self.stack
.add_named(self.populated.view.container(), "shows");
old.destroy();
Ok(())
}
pub fn replace_widget(&mut self, pd: Arc<Show>) -> Result<(), Error> {
let old = self.show.container.clone();
pub(crate) fn replace_widget(&mut self, pd: Arc<Show>) -> Result<(), Error> {
let old = self.show.view.container().clone();
// save the ShowWidget vertical scrollabar alignment
self.show.show_id().map(|id| self.show.save_vadjustment(id));
// Get the ShowWidget vertical alignment
let vadj = self.show.view.get_vadjustment();
let new = match self.show.show_id() {
// If the previous show was the same, restore the alignment
Some(id) if id == pd.id() => ShowWidget::new(pd, self.sender.clone(), vadj),
// else leave the valignemnt to default
_ => ShowWidget::new(pd.clone(), self.sender.clone(), None),
};
let new = ShowWidget::new(pd, self.sender.clone());
self.show = new;
self.stack.remove(&old);
self.stack.add_named(&self.show.container, "widget");
self.stack.add_named(self.show.view.container(), "widget");
// The current visible child might change depending on
// removal and insertion in the gtk::Stack, so we have
@@ -107,8 +109,8 @@ impl PopulatedStack {
Ok(())
}
pub fn update_widget(&mut self) -> Result<(), Error> {
let old = self.show.container.clone();
pub(crate) fn update_widget(&mut self) -> Result<(), Error> {
let old = self.show.view.container().clone();
let id = self.show.show_id();
if id.is_none() {
return Ok(());
@@ -128,7 +130,7 @@ impl PopulatedStack {
}
// Only update widget if its show_id is equal to pid.
pub fn update_widget_if_same(&mut self, pid: i32) -> Result<(), Error> {
pub(crate) fn update_widget_if_same(&mut self, pid: i32) -> Result<(), Error> {
if self.show.show_id() != Some(pid) {
debug!("Different widget. Early return");
return Ok(());
@@ -137,11 +139,11 @@ impl PopulatedStack {
self.update_widget()
}
pub fn container(&self) -> gtk::Box {
pub(crate) fn container(&self) -> gtk::Box {
self.container.clone()
}
pub fn switch_visible(&mut self, state: PopulatedState, animation: StackTransitionType) {
pub(crate) fn switch_visible(&mut self, state: PopulatedState, animation: StackTransitionType) {
use self::PopulatedState::*;
match state {
+17 -23
View File
@@ -3,40 +3,36 @@ use gtk::prelude::*;
use crossbeam_channel::Sender;
use failure::Error;
use podcasts_data::dbqueries::is_podcasts_populated;
use podcasts_data::dbqueries::is_episodes_populated;
use app::Action;
use stacks::content::State;
use stacks::PopulatedStack;
use utils::get_ignored_shows;
use widgets::EmptyView;
use std::cell::RefCell;
use std::ops::Deref;
use std::rc::Rc;
#[derive(Debug, Clone, Copy)]
pub enum ShowState {
Populated,
Empty,
}
#[derive(Debug, Clone)]
pub struct ShowStack {
pub(crate) struct ShowStack {
empty: EmptyView,
populated: Rc<RefCell<PopulatedStack>>,
stack: gtk::Stack,
state: ShowState,
state: State,
sender: Sender<Action>,
}
impl ShowStack {
pub fn new(sender: Sender<Action>) -> Self {
pub(crate) fn new(sender: Sender<Action>) -> Self {
let populated = Rc::new(RefCell::new(PopulatedStack::new(sender.clone())));
let empty = EmptyView::new();
let empty = EmptyView::default();
let stack = gtk::Stack::new();
let state = ShowState::Empty;
let state = State::Empty;
stack.add_named(&populated.borrow().container(), "populated");
stack.add_named(&empty.container, "empty");
stack.add_named(empty.deref(), "empty");
let mut show = ShowStack {
empty,
@@ -51,21 +47,21 @@ impl ShowStack {
show
}
pub fn get_stack(&self) -> gtk::Stack {
pub(crate) fn get_stack(&self) -> gtk::Stack {
self.stack.clone()
}
pub fn populated(&self) -> Rc<RefCell<PopulatedStack>> {
pub(crate) fn populated(&self) -> Rc<RefCell<PopulatedStack>> {
self.populated.clone()
}
pub fn update(&mut self) -> Result<(), Error> {
pub(crate) fn update(&mut self) -> Result<(), Error> {
self.populated.borrow_mut().update();
self.determine_state()
}
fn switch_visible(&mut self, s: ShowState) {
use self::ShowState::*;
pub(crate) fn switch_visible(&mut self, s: State) {
use self::State::*;
match s {
Populated => {
@@ -80,14 +76,12 @@ impl ShowStack {
}
fn determine_state(&mut self) -> Result<(), Error> {
use self::ShowState::*;
let ign = get_ignored_shows()?;
debug!("IGNORED SHOWS {:?}", ign);
if is_podcasts_populated(&ign)? {
self.switch_visible(Populated);
if is_episodes_populated(&ign)? {
self.sender.send(Action::PopulatedState);
} else {
self.switch_visible(Empty);
self.sender.send(Action::EmptyState);
};
Ok(())
+2 -2
View File
@@ -1,7 +1,7 @@
use gio::{resources_register, Error, Resource};
use glib::Bytes;
pub fn init() -> Result<(), Error> {
pub(crate) fn init() -> Result<(), Error> {
// load the gresource binary at build time and include/link it into the final
// binary.
let res_bytes = include_bytes!("../resources/resources.gresource");
@@ -10,7 +10,7 @@ pub fn init() -> Result<(), Error> {
let gbytes = Bytes::from_static(res_bytes.as_ref());
let resource = Resource::new_from_data(&gbytes)?;
// Register the resource so It wont be dropped and will continue to live in
// Register the resource so it won't be dropped and will continue to live in
// memory.
resources_register(&resource);
+120 -93
View File
@@ -1,19 +1,19 @@
#![cfg_attr(feature = "cargo-clippy", allow(type_complexity))]
use gdk::FrameClockExt;
use gdk_pixbuf::Pixbuf;
use glib;
use gdk_pixbuf::{Object, Pixbuf};
use glib::{self, object::WeakRef};
use gtk;
use gtk::prelude::*;
use gtk::{IsA, Widget};
use chrono::prelude::*;
use crossbeam_channel::{unbounded, Sender};
use crossbeam_channel::{bounded, unbounded, Sender};
use failure::Error;
use fragile::Fragile;
use rayon;
use regex::Regex;
use reqwest;
use send_cell::SendCell;
use serde_json::Value;
// use podcasts_data::feed;
@@ -29,6 +29,8 @@ use std::sync::{Arc, Mutex, RwLock};
use app::Action;
use i18n::i18n;
/// Lazy evaluates and loads widgets to the parent `container` widget.
///
/// Accepts an `IntoIterator`, `data`, as the source from which each widget
@@ -62,16 +64,25 @@ use app::Action;
/// let list = gtk::ListBox::new();
/// lazy_load(widgets, list, |w| w, || {});
/// ```
pub fn lazy_load<T, C, F, W, U>(data: T, container: C, mut contructor: F, callback: U)
where
pub(crate) fn lazy_load<T, C, F, W, U>(
data: T,
container: WeakRef<C>,
mut contructor: F,
callback: U,
) where
T: IntoIterator + 'static,
T::Item: 'static,
C: ContainerExt + 'static,
C: IsA<Object> + ContainerExt + 'static,
F: FnMut(T::Item) -> W + 'static,
W: IsA<Widget> + WidgetExt,
U: Fn() + 'static,
{
let func = move |x| {
let container = match container.upgrade() {
Some(c) => c,
None => return,
};
let widget = contructor(x);
container.add(&widget);
widget.show();
@@ -86,7 +97,7 @@ where
/// If you just want to lazy add `widgets` to a `container` check if
/// `lazy_load` fits your needs first.
#[cfg_attr(feature = "cargo-clippy", allow(redundant_closure))]
pub fn lazy_load_full<T, F, U>(data: T, mut func: F, finish_callback: U)
pub(crate) fn lazy_load_full<T, F, U>(data: T, mut func: F, finish_callback: U)
where
T: IntoIterator + 'static,
T::Item: 'static,
@@ -108,7 +119,7 @@ where
// Kudos to Julian Sparber
// https://blogs.gnome.org/jsparber/2018/04/29/animate-a-scrolledwindow/
#[cfg_attr(feature = "cargo-clippy", allow(float_cmp))]
pub fn smooth_scroll_to(view: &gtk::ScrolledWindow, target: &gtk::Adjustment) {
pub(crate) fn smooth_scroll_to(view: &gtk::ScrolledWindow, target: &gtk::Adjustment) {
if let Some(adj) = view.get_vadjustment() {
if let Some(clock) = view.get_frame_clock() {
let duration = 200;
@@ -145,91 +156,122 @@ lazy_static! {
static ref IGNORESHOWS: Arc<Mutex<HashSet<i32>>> = Arc::new(Mutex::new(HashSet::new()));
}
pub fn ignore_show(id: i32) -> Result<bool, Error> {
pub(crate) fn ignore_show(id: i32) -> Result<bool, Error> {
IGNORESHOWS
.lock()
.map(|mut guard| guard.insert(id))
.map_err(|err| format_err!("{}", err))
}
pub fn uningore_show(id: i32) -> Result<bool, Error> {
pub(crate) fn uningore_show(id: i32) -> Result<bool, Error> {
IGNORESHOWS
.lock()
.map(|mut guard| guard.remove(&id))
.map_err(|err| format_err!("{}", err))
}
pub fn get_ignored_shows() -> Result<Vec<i32>, Error> {
pub(crate) fn get_ignored_shows() -> Result<Vec<i32>, Error> {
IGNORESHOWS
.lock()
.map(|guard| guard.iter().cloned().collect::<Vec<_>>())
.map_err(|err| format_err!("{}", err))
}
pub fn cleanup(cleanup_date: DateTime<Utc>) {
pub(crate) fn cleanup(cleanup_date: DateTime<Utc>) {
checkup(cleanup_date)
.map_err(|err| error!("Check up failed: {}", err))
.ok();
}
pub fn refresh<S>(source: Option<S>, sender: Sender<Action>)
pub(crate) fn refresh<S>(source: Option<S>, sender: Sender<Action>)
where
S: IntoIterator<Item = Source> + Send + 'static,
{
// If we try to update the whole db,
// Exit early if `source` table is empty
if source.is_none() {
match dbqueries::is_source_populated(&[]) {
Ok(false) => {
info!("No source of feeds where found, returning");
return;
}
Err(err) => debug_assert!(false, err),
_ => (),
};
}
refresh_feed(source, sender)
.map_err(|err| error!("Failed to update feeds: {}", err))
.ok();
}
/// Update the rss feed(s) originating from `source`.
/// If `source` is None, Fetches all the `Source` entries in the database and updates them.
/// When It's done,it queues up a `RefreshViews` action.
fn refresh_feed<S>(source: Option<S>, sender: Sender<Action>) -> Result<(), Error>
fn refresh_feed<S>(source: Option<S>, sender: Sender<Action>)
where
S: IntoIterator<Item = Source> + Send + 'static,
{
sender.send(Action::HeaderBarShowUpdateIndicator);
rayon::spawn(move || {
let (up_sender, up_receiver) = bounded(1);
sender.send(Action::ShowUpdateNotif(up_receiver));
if let Some(s) = source {
// Refresh only specified feeds
pipeline::run(s, false)
pipeline::run(s)
.map_err(|err| error!("Error: {}", err))
.map_err(|_| error!("Error While trying to update the database."))
.map_err(|_| error!("Error while trying to update the database."))
.ok();
} else {
// Refresh all the feeds
dbqueries::get_sources()
.map(|s| s.into_iter())
.and_then(|s| pipeline::run(s, false))
.and_then(pipeline::run)
.map_err(|err| error!("Error: {}", err))
.ok();
};
sender.send(Action::HeaderBarHideUpdateIndicator);
sender.send(Action::RefreshAllViews);
up_sender.send(true);
});
Ok(())
}
lazy_static! {
static ref CACHED_PIXBUFS: RwLock<HashMap<(i32, u32), Mutex<SendCell<Pixbuf>>>> =
static ref CACHED_PIXBUFS: RwLock<HashMap<(i32, u32), Mutex<Fragile<Pixbuf>>>> =
{ RwLock::new(HashMap::new()) };
static ref COVER_DL_REGISTRY: RwLock<HashSet<i32>> = RwLock::new(HashSet::new());
static ref THREADPOOL: rayon::ThreadPool = rayon::ThreadPoolBuilder::new().build().unwrap();
}
// Since gdk_pixbuf::Pixbuf is refference counted and every episode,
// Since gdk_pixbuf::Pixbuf is reference counted and every episode,
// use the cover of the Podcast Feed/Show, We can only create a Pixbuf
// cover per show and pass around the Rc pointer.
//
// GObjects do not implement Send trait, so SendCell is a way around that.
// Also lazy_static requires Sync trait, so that's what the mutexes are.
// TODO: maybe use something that would just scale to requested size?
pub fn set_image_from_path(image: &gtk::Image, show_id: i32, size: u32) -> Result<(), Error> {
pub(crate) fn set_image_from_path(
image: &gtk::Image,
show_id: i32,
size: u32,
) -> Result<(), Error> {
if let Ok(hashmap) = CACHED_PIXBUFS.read() {
// Check if the requested (cover + size) is already in the cache
// and if so do an early return after that.
if let Some(guard) = hashmap.get(&(show_id, size)) {
guard
.lock()
.map_err(|err| format_err!("Fragile Mutex: {}", err))
.and_then(|fragile| {
fragile
.try_get()
.map(|px| image.set_from_pixbuf(px))
.map_err(From::from)
})?;
return Ok(());
}
}
// Check if there's an active download about this show cover.
// If there is, a callback will be set so this function will be called again.
// If the download succedes, there should be a quick return from the pixbuf cache_image
// If the download succeeds, there should be a quick return from the pixbuf cache_image
// If it fails another download will be scheduled.
if let Ok(guard) = COVER_DL_REGISTRY.read() {
if guard.contains(&show_id) {
@@ -242,34 +284,23 @@ pub fn set_image_from_path(image: &gtk::Image, show_id: i32, size: u32) -> Resul
}
}
if let Ok(hashmap) = CACHED_PIXBUFS.read() {
// Check if the requested (cover + size) is already in the chache
// and if so do an early return after that.
if let Some(guard) = hashmap.get(&(show_id, size)) {
guard
.lock()
.map_err(|err| format_err!("SendCell Mutex: {}", err))
.and_then(|sendcell| {
sendcell
.try_get()
.map(|px| image.set_from_pixbuf(px))
.ok_or_else(|| format_err!("Pixbuf was accessed from a different thread"))
})?;
return Ok(());
}
}
let (sender, receiver) = unbounded();
THREADPOOL.spawn(move || {
if let Ok(mut guard) = COVER_DL_REGISTRY.write() {
guard.insert(show_id);
if let Ok(mut guard) = COVER_DL_REGISTRY.write() {
// Add the id to the hashmap from the main thread to avoid queuing more than one downloads.
guard.insert(show_id);
drop(guard);
THREADPOOL.spawn(move || {
// This operation is polling and will block the thread till the download is finished
if let Ok(pd) = dbqueries::get_podcast_cover_from_id(show_id) {
sender.send(downloader::cache_image(&pd));
}
guard.remove(&show_id);
}
});
if let Ok(mut guard) = COVER_DL_REGISTRY.write() {
guard.remove(&show_id);
}
});
}
let image = image.clone();
let s = size as i32;
@@ -278,7 +309,7 @@ pub fn set_image_from_path(image: &gtk::Image, show_id: i32, size: u32) -> Resul
if let Ok(path) = path {
if let Ok(px) = Pixbuf::new_from_file_at_scale(&path, s, s, true) {
if let Ok(mut hashmap) = CACHED_PIXBUFS.write() {
hashmap.insert((show_id, size), Mutex::new(SendCell::new(px.clone())));
hashmap.insert((show_id, size), Mutex::new(Fragile::new(px.clone())));
image.set_from_pixbuf(&px);
}
}
@@ -292,8 +323,8 @@ pub fn set_image_from_path(image: &gtk::Image, show_id: i32, size: u32) -> Resul
}
// FIXME: the signature should be `fn foo(s: Url) -> Result<Url, Error>`
pub fn itunes_to_rss(url: &str) -> Result<String, Error> {
let id = itunes_id_from_url(url).ok_or_else(|| format_err!("Failed to find an Itunes ID."))?;
pub(crate) fn itunes_to_rss(url: &str) -> Result<String, Error> {
let id = itunes_id_from_url(url).ok_or_else(|| format_err!("Failed to find an iTunes ID."))?;
lookup_id(id)
}
@@ -317,18 +348,16 @@ fn lookup_id(id: u32) -> Result<String, Error> {
.ok_or_else(|| format_err!("Failed to get url from itunes response"))
}
pub fn on_import_clicked(window: &gtk::ApplicationWindow, sender: &Sender<Action>) {
pub(crate) fn on_import_clicked(window: &gtk::ApplicationWindow, sender: &Sender<Action>) {
use glib::translate::ToGlib;
use gtk::{FileChooserAction, FileChooserNative, FileFilter, ResponseType};
// let dialog = FileChooserDialog::new(title, Some(&window), FileChooserAction::Open);
// TODO: It might be better to use a FileChooserNative widget.
// Create the FileChooser Dialog
let dialog = FileChooserNative::new(
Some("Select the file from which to you want to Import Shows."),
Some(i18n("Select the file from which to you want to import shows.").as_str()),
Some(window),
FileChooserAction::Open,
Some("_Import"),
Some(i18n("_Import").as_str()),
None,
);
@@ -337,43 +366,38 @@ pub fn on_import_clicked(window: &gtk::ApplicationWindow, sender: &Sender<Action
// Set a filter to show only xml files
let filter = FileFilter::new();
FileFilterExt::set_name(&filter, Some("OPML file"));
FileFilterExt::set_name(&filter, Some(i18n("OPML file").as_str()));
filter.add_mime_type("application/xml");
filter.add_mime_type("text/xml");
dialog.add_filter(&filter);
dialog.connect_response(clone!(sender => move |dialog, resp| {
debug!("Dialong Response {}", resp);
if resp == ResponseType::Accept.to_glib() {
// TODO: Show an in-app notifictaion if the file can not be accessed
if let Some(filename) = dialog.get_filename() {
debug!("File selected: {:?}", filename);
let resp = dialog.run();
debug!("Dialog Response {}", resp);
if resp == ResponseType::Accept.to_glib() {
if let Some(filename) = dialog.get_filename() {
debug!("File selected: {:?}", filename);
rayon::spawn(clone!(sender => move || {
// Parse the file and import the feeds
if let Ok(sources) = opml::import_from_file(filename) {
// Refresh the succesfully parsed feeds to index them
refresh(Some(sources), sender)
} else {
let text = String::from("Failed to parse the Imported file");
sender.send(Action::ErrorNotification(text));
}
}))
} else {
let text = String::from("Selected File could not be accessed.");
sender.send(Action::ErrorNotification(text));
}
rayon::spawn(clone!(sender => move || {
// Parse the file and import the feeds
if let Ok(sources) = opml::import_from_file(filename) {
// Refresh the successfully parsed feeds to index them
refresh(Some(sources), sender)
} else {
let text = i18n("Failed to parse the imported file");
sender.send(Action::ErrorNotification(text));
}
}))
} else {
let text = i18n("Selected file could not be accessed.");
sender.send(Action::ErrorNotification(text));
}
dialog.destroy();
}));
dialog.run();
}
}
#[cfg(test)]
mod tests {
use super::*;
use failure::Error;
// use podcasts_data::Source;
// use podcasts_data::dbqueries;
@@ -398,29 +422,32 @@ mod tests {
// }
#[test]
fn test_itunes_to_rss() {
fn test_itunes_to_rss() -> Result<(), Error> {
let itunes_url = "https://itunes.apple.com/podcast/id1195206601";
let rss_url = String::from("http://feeds.feedburner.com/InterceptedWithJeremyScahill");
assert_eq!(rss_url, itunes_to_rss(itunes_url).unwrap());
assert_eq!(rss_url, itunes_to_rss(itunes_url)?);
let itunes_url = "https://itunes.apple.com/podcast/id000000000000000";
assert!(itunes_to_rss(itunes_url).is_err());
Ok(())
}
#[test]
fn test_itunes_id() {
fn test_itunes_id() -> Result<(), Error> {
let id = 1195206601;
let itunes_url = "https://itunes.apple.com/podcast/id1195206601";
assert_eq!(id, itunes_id_from_url(itunes_url).unwrap());
Ok(())
}
#[test]
fn test_itunes_lookup_id() {
fn test_itunes_lookup_id() -> Result<(), Error> {
let id = 1195206601;
let rss_url = "http://feeds.feedburner.com/InterceptedWithJeremyScahill";
assert_eq!(rss_url, lookup_id(id).unwrap());
assert_eq!(rss_url, lookup_id(id)?);
let id = 000000000;
assert!(lookup_id(id).is_err());
Ok(())
}
}
+17 -11
View File
@@ -1,41 +1,47 @@
use app::APP_ID;
use app::{APP_ID, VERSION};
use gtk;
use gtk::prelude::*;
use i18n::i18n;
// Totally copied it from fractal.
// https://gitlab.gnome.org/danigm/fractal/blob/503e311e22b9d7540089d735b92af8e8f93560c5/fractal-gtk/src/app.rs#L1883-1912
/// Given a `window` create and attach an `gtk::AboutDialog` to it.
pub fn about_dialog(window: &gtk::ApplicationWindow) {
// Feel free to add yourself if you contribured.
pub(crate) fn about_dialog(window: &gtk::ApplicationWindow) {
// Feel free to add yourself if you contributed.
// Please keep it sorted alphabetically
let authors = &[
"Alexandre Franke",
"Carlos Soriano",
"Constantin Nickel",
"Daniel García Moreno",
"Felix Häcker",
"Gabriele Musco",
"Ivan Augusto",
"James Wykeham-Martin",
"Jordan Petridis",
"Julian Sparber",
"Matthew Martin",
"Piotr Drąg",
"Rowan Lewis",
"Zander Brown",
];
let dialog = gtk::AboutDialog::new();
dialog.set_logo_icon_name(APP_ID);
dialog.set_comments("Podcast Client for the GNOME Desktop.");
dialog.set_comments(i18n("Podcast Client for the GNOME Desktop.").as_str());
dialog.set_copyright("© 2017, 2018 Jordan Petridis");
dialog.set_license_type(gtk::License::Gpl30);
dialog.set_modal(true);
// TODO: make it show it fetches the commit hash from which it was built
// and the version number is kept in sync automaticly
dialog.set_version("0.4.1");
dialog.set_program_name("Podcasts");
// TODO: Need a wiki page first.
// dialog.set_website("https://wiki.gnome.org/Design/Apps/Potential/Podcasts");
// dialog.set_website_label("Learn more about GNOME Podcasts");
dialog.set_version(VERSION);
dialog.set_program_name(&i18n("Podcasts"));
dialog.set_website("https://wiki.gnome.org/Apps/Podcasts");
dialog.set_website_label(i18n("Learn more about GNOME Podcasts").as_str());
dialog.set_transient_for(window);
dialog.set_artists(&["Tobias Bernard", "Sam Hewitt"]);
dialog.set_authors(authors);
dialog.set_translator_credits(i18n("translator-credits").as_str());
dialog.connect_response(|dlg, _| dlg.destroy());
+81 -17
View File
@@ -6,17 +6,26 @@ use std::cell::RefCell;
use std::rc::Rc;
#[derive(Debug, Clone, Copy)]
pub enum UndoState {
#[allow(dead_code)]
pub(crate) enum State {
Shown,
Hidden,
}
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)]
pub(crate) enum SpinnerState {
Active,
Stopped,
}
#[derive(Debug, Clone)]
pub struct InAppNotification {
pub(crate) struct InAppNotification {
revealer: gtk::Revealer,
text: gtk::Label,
undo: gtk::Button,
close: gtk::Button,
spinner: gtk::Spinner,
}
impl Default for InAppNotification {
@@ -27,32 +36,54 @@ impl Default for InAppNotification {
let text: gtk::Label = builder.get_object("text").unwrap();
let undo: gtk::Button = builder.get_object("undo").unwrap();
let close: gtk::Button = builder.get_object("close").unwrap();
let spinner = builder.get_object("spinner").unwrap();
InAppNotification {
revealer,
text,
undo,
close,
spinner,
}
}
}
/// Timer should be in milliseconds
impl InAppNotification {
pub fn new<F, U>(text: &str, mut callback: F, undo_callback: U, show_undo: UndoState) -> Self
pub(crate) fn new<F, U>(
text: &str,
timer: u32,
mut callback: F,
undo_callback: Option<U>,
) -> Self
where
F: FnMut() -> glib::Continue + 'static,
F: FnMut(gtk::Revealer) -> glib::Continue + 'static,
U: Fn() + 'static,
{
let notif = InAppNotification::default();
notif.text.set_text(&text);
let revealer = notif.revealer.clone();
let id = timeout_add_seconds(6, move || {
revealer.set_reveal_child(false);
callback()
let revealer_weak = notif.revealer.downgrade();
let mut time = 0;
let id = timeout_add(250, move || {
if time < timer {
time += 250;
return glib::Continue(true);
};
let revealer = match revealer_weak.upgrade() {
Some(r) => r,
None => return glib::Continue(false),
};
callback(revealer)
});
let id = Rc::new(RefCell::new(Some(id)));
if undo_callback.is_some() {
notif.set_undo_state(State::Shown)
};
// Cancel the callback
let revealer = notif.revealer.clone();
notif.undo.connect_clicked(move |_| {
@@ -61,34 +92,67 @@ impl InAppNotification {
glib::source::source_remove(id);
}
undo_callback();
if let Some(ref f) = undo_callback {
f();
}
// Hide the notification
revealer.set_reveal_child(false);
});
// Hide the revealer when the close button is clicked
let revealer = notif.revealer.clone();
let revealer_weak = notif.revealer.downgrade();
notif.close.connect_clicked(move |_| {
let revealer = match revealer_weak.upgrade() {
Some(r) => r,
None => return,
};
revealer.set_reveal_child(false);
});
match show_undo {
UndoState::Shown => (),
UndoState::Hidden => notif.undo.hide(),
}
notif
}
// This is a seperate method cause in order to get a nice animation
// This is a separate method cause in order to get a nice animation
// the revealer should be attached to something that displays it.
// Previously we where doing it in the constructor, which had the result
// of the animation being skipped cause there was no parent widget to display it.
pub fn show(&self, overlay: &gtk::Overlay) {
pub(crate) fn show(&self, overlay: &gtk::Overlay) {
overlay.add_overlay(&self.revealer);
// We need to display the notification after the widget is added to the overlay
// so there will be a nice animation.
self.revealer.set_reveal_child(true);
}
pub(crate) fn set_undo_state(&self, state: State) {
match state {
State::Shown => self.undo.show(),
State::Hidden => self.undo.hide(),
}
}
pub(crate) fn set_close_state(&self, state: State) {
match state {
State::Shown => self.close.show(),
State::Hidden => self.close.hide(),
}
}
pub(crate) fn set_spinner_state(&self, state: SpinnerState) {
match state {
SpinnerState::Active => {
self.spinner.start();
self.spinner.show();
}
SpinnerState::Stopped => {
self.spinner.stop();
self.spinner.hide();
}
}
}
pub(crate) fn destroy(self) {
self.revealer.destroy();
}
}
+57
View File
@@ -0,0 +1,57 @@
use gtk::{self, prelude::*, Adjustment, Orientation, PolicyType};
use utils::smooth_scroll_to;
#[derive(Debug, Clone)]
pub(crate) struct BaseView {
container: gtk::Box,
scrolled_window: gtk::ScrolledWindow,
}
impl Default for BaseView {
fn default() -> Self {
let container = gtk::Box::new(Orientation::Horizontal, 0);
let scrolled_window = gtk::ScrolledWindow::new(None, None);
scrolled_window.set_policy(PolicyType::Never, PolicyType::Automatic);
container.set_size_request(360, -1);
container.add(&scrolled_window);
container.show_all();
BaseView {
container,
scrolled_window,
}
}
}
impl BaseView {
pub(crate) fn container(&self) -> &gtk::Box {
&self.container
}
pub(crate) fn scrolled_window(&self) -> &gtk::ScrolledWindow {
&self.scrolled_window
}
pub(crate) fn add<T: IsA<gtk::Widget>>(&self, widget: &T) {
self.scrolled_window.add(widget);
}
pub(crate) fn set_adjutments<'a, 'b>(
&self,
hadjustment: Option<&'a Adjustment>,
vadjustment: Option<&'b Adjustment>,
) {
if let Some(h) = hadjustment {
smooth_scroll_to(&self.scrolled_window, h);
}
if let Some(v) = vadjustment {
smooth_scroll_to(&self.scrolled_window, v);
}
}
pub(crate) fn get_vadjustment(&self) -> Option<Adjustment> {
self.scrolled_window().get_vadjustment()
}
}
+29 -9
View File
@@ -1,21 +1,41 @@
use gtk;
use app::APP_ID;
use gtk::{self, prelude::*};
use std::ops::Deref;
#[derive(Debug, Clone)]
pub struct EmptyView {
pub container: gtk::Box,
#[derive(Clone, Debug)]
pub(crate) struct EmptyView(gtk::Box);
impl Deref for EmptyView {
type Target = gtk::Box;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Default for EmptyView {
fn default() -> Self {
let builder = gtk::Builder::new_from_resource("/org/gnome/Podcasts/gtk/empty_view.ui");
let view: gtk::Box = builder.get_object("empty_view").unwrap();
EmptyView { container: view }
let image: gtk::Image = builder.get_object("image").unwrap();
image.set_from_icon_name(format!("{}-symbolic", APP_ID).as_str(), 256);
EmptyView(view)
}
}
impl EmptyView {
pub fn new() -> EmptyView {
EmptyView::default()
#[derive(Clone, Debug)]
pub(crate) struct EmptyShow(gtk::Box);
impl Deref for EmptyShow {
type Target = gtk::Box;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Default for EmptyShow {
fn default() -> Self {
let builder = gtk::Builder::new_from_resource("/org/gnome/Podcasts/gtk/empty_view.ui");
let box_: gtk::Box = builder.get_object("empty_show").unwrap();
EmptyShow(box_)
}
}
+38 -18
View File
@@ -13,6 +13,7 @@ use open;
use podcasts_data::dbqueries;
use podcasts_data::utils::get_download_folder;
use podcasts_data::EpisodeWidgetModel;
use podcasts_downloader::downloader::DownloadProgress;
use app::Action;
use manager;
@@ -21,6 +22,8 @@ use std::cell::RefCell;
use std::rc::{Rc, Weak};
use std::sync::{Arc, Mutex, TryLockError};
use i18n::i18n_f;
lazy_static! {
static ref SIZE_OPTS: Arc<size_opts::FileSizeOpts> = {
// Declare a custom humansize option struct
@@ -40,8 +43,8 @@ lazy_static! {
}
#[derive(Clone, Debug)]
pub struct EpisodeWidget {
pub container: gtk::Box,
pub(crate) struct EpisodeWidget {
pub(crate) container: gtk::Box,
info: InfoLabels,
buttons: Buttons,
progressbar: gtk::ProgressBar,
@@ -62,7 +65,7 @@ struct InfoLabels {
#[derive(Clone, Debug)]
struct Buttons {
container: gtk::ButtonBox,
container: gtk::Box,
play: gtk::Button,
download: gtk::Button,
cancel: gtk::Button,
@@ -76,7 +79,7 @@ impl InfoLabels {
// Set the date label.
self.set_date(episode.epoch());
// Set the duaration label.
// Set the duration label.
self.set_duration(episode.duration());
// Set the total_size label.
@@ -116,14 +119,15 @@ impl InfoLabels {
// Set the duration label of the episode widget.
fn set_duration(&self, seconds: Option<i32>) {
// If lenght is provided
// If length is provided
if let Some(s) = seconds {
// Convert seconds to minutes
let minutes = chrono::Duration::seconds(s.into()).num_minutes();
// If the lenght is 1 or more minutes
// If the length is 1 or more minutes
if minutes != 0 {
// Set the label and show them.
self.duration.set_text(&format!("{} min", minutes));
self.duration
.set_text(&i18n_f("{} min", &[&minutes.to_string()]));
self.duration.show();
self.separator1.show();
return;
@@ -207,7 +211,7 @@ impl Default for EpisodeWidget {
}
impl EpisodeWidget {
pub fn new(episode: EpisodeWidgetModel, sender: &Sender<Action>) -> Rc<Self> {
pub(crate) fn new(episode: EpisodeWidgetModel, sender: &Sender<Action>) -> Rc<Self> {
let widget = Rc::new(Self::default());
let episode = RefCell::new(Some(episode));
let weak = Rc::downgrade(&widget);
@@ -224,15 +228,15 @@ impl EpisodeWidget {
// When the widget is attached to a parent,
// since it's a rust struct and not a widget the
// compiler drops the refference to it at the end of
// compiler drops the reference to it at the end of
// scope. That's cause we only attach the `self.container`
// to the parent.
//
// So this callback keeps a refference to the Rust Struct
// So this callback keeps a reference to the Rust Struct
// so the compiler won't drop it.
//
// When the widget is detached from it's parent view this
// callback runs freeing the last refference we were holding.
// When the widget is detached from its parent view this
// callback runs freeing the last reference we were holding.
let foo = RefCell::new(Some(widget.clone()));
widget.container.connect_remove(move |_, _| {
foo.borrow_mut().take();
@@ -345,7 +349,7 @@ impl EpisodeWidget {
Ok(m.get(&id).cloned())
};
// State: InProggress
// State: InProgress
if let Some(prog) = active_dl()? {
// set a callback that will update the state when the download finishes
let callback = clone!(weak, sender => move || {
@@ -439,7 +443,7 @@ impl EpisodeWidget {
if let Ok(ep) = dbqueries::get_episode_widget_from_rowid(id) {
on_download_clicked(&ep, &sender)
.and_then(|_| {
info!("Donwload started succesfully.");
info!("Download started successfully.");
Self::determine_buttons_state(&weak, &ep, &sender)
})
.map_err(|err| error!("Error: {}", err))
@@ -516,8 +520,12 @@ fn progress_bar_helper(
None => return Ok(glib::Continue(false)),
};
let (fraction, downloaded) = match prog.try_lock() {
Ok(guard) => (guard.get_fraction(), guard.get_downloaded()),
let (fraction, downloaded, cancel) = match prog.try_lock() {
Ok(guard) => (
guard.get_fraction(),
guard.get_downloaded(),
guard.should_cancel(),
),
Err(TryLockError::WouldBlock) => return Ok(glib::Continue(true)),
Err(TryLockError::Poisoned(_)) => return Err(format_err!("Progress Mutex is poisoned")),
};
@@ -544,7 +552,19 @@ fn progress_bar_helper(
if (fraction >= 1.0) && (!fraction.is_nan()) {
Ok(glib::Continue(false))
} else if !active {
} else if !active || cancel {
// if the total size is not a number, hide it
if widget
.info
.total_size
.get_text()
.as_ref()
.map(|s| s.trim_right_matches(" MB"))
.and_then(|s| s.parse::<i32>().ok())
.is_none()
{
widget.info.total_size.hide();
}
Ok(glib::Continue(false))
} else {
Ok(glib::Continue(true))
@@ -573,7 +593,7 @@ fn total_size_helper(
// Get the total_bytes.
let total_bytes = match prog.try_lock() {
Ok(guard) => guard.get_total_size(),
Ok(guard) => guard.get_size(),
Err(TryLockError::WouldBlock) => return Ok(glib::Continue(true)),
Err(TryLockError::Poisoned(_)) => return Err(format_err!("Progress Mutex is poisoned")),
};
+46 -64
View File
@@ -1,26 +1,19 @@
use chrono::prelude::*;
use failure::Error;
use gtk;
use gtk::prelude::*;
use gtk::{self, prelude::*, Adjustment};
use crossbeam_channel::Sender;
use libhandy::{Column, ColumnExt};
use podcasts_data::dbqueries;
use podcasts_data::EpisodeWidgetModel;
use send_cell::SendCell;
use app::Action;
use utils::{self, lazy_load_full};
use widgets::EpisodeWidget;
use widgets::{BaseView, EpisodeWidget};
use std::cell::Cell;
use std::rc::Rc;
use std::sync::Mutex;
lazy_static! {
pub static ref EPISODES_VIEW_VALIGNMENT: Mutex<Option<SendCell<gtk::Adjustment>>> =
Mutex::new(None);
}
#[derive(Debug, Clone)]
enum ListSplit {
@@ -32,9 +25,8 @@ enum ListSplit {
}
#[derive(Debug, Clone)]
pub struct HomeView {
pub container: gtk::Box,
scrolled_window: gtk::ScrolledWindow,
pub(crate) struct HomeView {
pub(crate) view: BaseView,
frame_parent: gtk::Box,
today_box: gtk::Box,
yday_box: gtk::Box,
@@ -50,9 +42,8 @@ pub struct HomeView {
impl Default for HomeView {
fn default() -> Self {
let view = BaseView::default();
let builder = gtk::Builder::new_from_resource("/org/gnome/Podcasts/gtk/home_view.ui");
let container: gtk::Box = builder.get_object("container").unwrap();
let scrolled_window: gtk::ScrolledWindow = builder.get_object("scrolled_window").unwrap();
let frame_parent: gtk::Box = builder.get_object("frame_parent").unwrap();
let today_box: gtk::Box = builder.get_object("today_box").unwrap();
let yday_box: gtk::Box = builder.get_object("yday_box").unwrap();
@@ -65,9 +56,19 @@ impl Default for HomeView {
let month_list: gtk::ListBox = builder.get_object("month_list").unwrap();
let rest_list: gtk::ListBox = builder.get_object("rest_list").unwrap();
let column = Column::new();
column.show();
column.set_maximum_width(700);
// For some reason the Column is not seen as a gtk::container
// and therefore we can't call add() without the cast
let column = column.upcast::<gtk::Widget>();
let column = column.downcast::<gtk::Container>().unwrap();
column.add(&frame_parent);
view.add(&column);
HomeView {
container,
scrolled_window,
view,
frame_parent,
today_box,
yday_box,
@@ -85,69 +86,51 @@ impl Default for HomeView {
// TODO: REFACTOR ME
impl HomeView {
pub fn new(sender: Sender<Action>) -> Result<Rc<HomeView>, Error> {
pub(crate) fn new(
sender: Sender<Action>,
vadj: Option<Adjustment>,
) -> Result<Rc<HomeView>, Error> {
use self::ListSplit::*;
let view = Rc::new(HomeView::default());
let home = Rc::new(HomeView::default());
let ignore = utils::get_ignored_shows()?;
let episodes = dbqueries::get_episodes_widgets_filter_limit(&ignore, 100)?;
let now_utc = Utc::now();
let view_ = view.clone();
let home_weak = Rc::downgrade(&home);
let func = move |ep: EpisodeWidgetModel| {
let home = match home_weak.upgrade() {
Some(h) => h,
None => return,
};
let epoch = ep.epoch();
let widget = HomeEpisode::new(ep, &sender);
match split(&now_utc, i64::from(epoch)) {
Today => add_to_box(&widget, &view_.today_list, &view_.today_box),
Yday => add_to_box(&widget, &view_.yday_list, &view_.yday_box),
Week => add_to_box(&widget, &view_.week_list, &view_.week_box),
Month => add_to_box(&widget, &view_.month_list, &view_.month_box),
Rest => add_to_box(&widget, &view_.rest_list, &view_.rest_box),
Today => add_to_box(&widget, &home.today_list, &home.today_box),
Yday => add_to_box(&widget, &home.yday_list, &home.yday_box),
Week => add_to_box(&widget, &home.week_list, &home.week_box),
Month => add_to_box(&widget, &home.month_list, &home.month_box),
Rest => add_to_box(&widget, &home.rest_list, &home.rest_box),
}
};
let view_ = view.clone();
let home_weak = Rc::downgrade(&home);
let callback = move || {
view_
.set_vadjustment()
.map_err(|err| format!("{}", err))
.ok();
let home = match home_weak.upgrade() {
Some(h) => h,
None => return,
};
if let Some(ref v) = vadj {
home.view.set_adjutments(None, Some(v))
};
};
lazy_load_full(episodes, func, callback);
view.container.show_all();
Ok(view)
}
/// Set scrolled window vertical adjustment.
fn set_vadjustment(&self) -> Result<(), Error> {
let guard = EPISODES_VIEW_VALIGNMENT
.lock()
.map_err(|err| format_err!("Failed to lock widget align mutex: {}", err))?;
if let Some(ref sendcell) = *guard {
// Copy the vertical scrollbar adjustment from the old view into the new one.
sendcell
.try_get()
.map(|x| utils::smooth_scroll_to(&self.scrolled_window, &x));
}
Ok(())
}
/// Save the vertical scrollbar position.
pub fn save_alignment(&self) -> Result<(), Error> {
if let Ok(mut guard) = EPISODES_VIEW_VALIGNMENT.lock() {
let adj = self
.scrolled_window
.get_vadjustment()
.ok_or_else(|| format_err!("Could not get the adjustment"))?;
*guard = Some(SendCell::new(adj));
info!("Saved episodes_view alignment.");
}
Ok(())
home.view.container().show_all();
Ok(home)
}
}
@@ -173,10 +156,9 @@ fn split(now: &DateTime<Utc>, epoch: i64) -> ListSplit {
}
#[derive(Debug, Clone)]
struct HomeEpisode {
pub(crate) struct HomeEpisode {
container: gtk::Box,
image: gtk::Image,
// FIXME: Change it to `EpisodeWidget` instead of a `Box`?
episode: gtk::Box,
}
+15 -10
View File
@@ -1,17 +1,22 @@
mod aboutdialog;
pub mod appnotif;
pub(crate) mod appnotif;
mod base_view;
mod empty;
mod episode;
mod home_view;
pub mod player;
pub(crate) mod player;
mod show;
pub mod show_menu;
pub(crate) mod show_menu;
mod shows_view;
pub use self::aboutdialog::about_dialog;
pub use self::empty::EmptyView;
pub use self::episode::EpisodeWidget;
pub use self::home_view::HomeView;
pub use self::show::ShowWidget;
pub use self::show_menu::ShowMenu;
pub use self::shows_view::ShowsView;
pub(crate) use self::aboutdialog::about_dialog;
pub(crate) use self::base_view::BaseView;
pub(crate) use self::empty::{EmptyShow, EmptyView};
pub(crate) use self::episode::EpisodeWidget;
pub(crate) use self::home_view::HomeView;
pub(crate) use self::show::ShowWidget;
pub(crate) use self::show_menu::ShowMenu;
pub(crate) use self::shows_view::ShowsView;
#[cfg(test)]
pub(crate) use self::home_view::HomeEpisode;
+130 -28
View File
@@ -6,12 +6,12 @@ use gtk;
use gtk::prelude::*;
use gio::{File, FileExt};
use glib::SignalHandlerId;
use glib::{SignalHandlerId, WeakRef};
use chrono::NaiveTime;
use chrono::{prelude::*, NaiveTime};
use crossbeam_channel::Sender;
use failure::Error;
use send_cell::SendCell;
use fragile::Fragile;
use podcasts_data::{dbqueries, USER_AGENT};
use podcasts_data::{EpisodeWidgetModel, ShowCoverModel};
@@ -22,6 +22,13 @@ use utils::set_image_from_path;
use std::ops::Deref;
use std::path::Path;
use std::rc::Rc;
use std::cell::RefCell;
use std::sync::Mutex;
use i18n::i18n;
use std::sync::Arc;
use mpris_player::{PlaybackStatus, MprisPlayer, Metadata, OrgMprisMediaPlayer2Player};
#[derive(Debug, Clone, Copy)]
enum SeekDirection {
@@ -45,14 +52,27 @@ struct PlayerInfo {
show: gtk::Label,
episode: gtk::Label,
cover: gtk::Image,
mpris: Arc<MprisPlayer>,
episode_id: RefCell<Option<i32>>,
}
impl PlayerInfo {
// FIXME: create a Diesel Model of the joined episode and podcast query instead
fn init(&self, episode: &EpisodeWidgetModel, podcast: &ShowCoverModel) {
self.episode_id.replace(Some(episode.rowid()));
self.set_cover_image(podcast);
self.set_show_title(podcast);
self.set_episode_title(episode);
let mut metadata = Metadata::new();
metadata.artist = Some(vec![podcast.title().to_string()]);
metadata.title = Some(episode.title().to_string());
// FIXME: .image_uri() returns an http url, we should instead
// pass it the local path to the downloaded cover image.
metadata.art_url = podcast.image_uri().clone().map(From::from);
self.mpris.set_metadata(metadata);
self.mpris.set_can_play(true);
}
fn set_episode_title(&self, episode: &EpisodeWidgetModel) {
@@ -104,7 +124,7 @@ impl Deref for Position {
impl PlayerTimes {
/// Update the duration `gtk::Label` and the max range of the `gtk::SclaeBar`.
pub fn on_duration_changed(&self, duration: Duration) {
pub(crate) fn on_duration_changed(&self, duration: Duration) {
let seconds = duration.seconds().map(|v| v as f64).unwrap_or(0.0);
self.slider.block_signal(&self.slider_update);
@@ -114,8 +134,8 @@ impl PlayerTimes {
self.duration.set_text(&format_duration(seconds as u32));
}
/// Update the `gtk::SclaeBar` when the pipeline position is changed.
pub fn on_position_updated(&self, position: Position) {
/// Update the `gtk::Scale` bar when the pipeline position is changed.
pub(crate) fn on_position_updated(&self, position: Position) {
let seconds = position.seconds().map(|v| v as f64).unwrap_or(0.0);
self.slider.block_signal(&self.slider_update);
@@ -132,7 +152,7 @@ fn format_duration(seconds: u32) -> String {
if seconds >= 3600 {
time.format("%T").to_string()
} else {
time.format("%M:%S").to_string()
time.format("%M%S").to_string()
}
}
@@ -153,11 +173,12 @@ struct PlayerControls {
pause: gtk::Button,
forward: gtk::Button,
rewind: gtk::Button,
last_pause: RefCell<Option<DateTime<Local>>>,
}
#[derive(Debug, Clone)]
pub struct PlayerWidget {
pub action_bar: gtk::ActionBar,
pub(crate) struct PlayerWidget {
pub(crate) action_bar: gtk::ActionBar,
player: gst_player::Player,
controls: PlayerControls,
timer: PlayerTimes,
@@ -174,6 +195,15 @@ impl Default for PlayerWidget {
Some(&dispatcher.upcast::<gst_player::PlayerSignalDispatcher>()),
);
let mpris = MprisPlayer::new(
"Podcasts".to_string(),
"GNOME Podcasts".to_string(),
"org.gnome.Podcasts.desktop".to_string(),
);
mpris.set_can_play(false);
mpris.set_can_seek(false);
mpris.set_can_set_fullscreen(false);
let mut config = player.get_config();
config.set_user_agent(USER_AGENT);
config.set_position_update_interval(250);
@@ -194,6 +224,7 @@ impl Default for PlayerWidget {
pause,
forward,
rewind,
last_pause: RefCell::new(None),
};
let timer_container = builder.get_object("timer").unwrap();
@@ -202,7 +233,8 @@ impl Default for PlayerWidget {
let separator = builder.get_object("separator").unwrap();
let slider: gtk::Scale = builder.get_object("seek").unwrap();
slider.set_range(0.0, 1.0);
let slider_update = Rc::new(Self::connect_update_slider(&slider, &player));
let player_weak = player.downgrade();
let slider_update = Rc::new(Self::connect_update_slider(&slider, player_weak));
let timer = PlayerTimes {
container: timer_container,
progressed,
@@ -217,10 +249,12 @@ impl Default for PlayerWidget {
let episode = builder.get_object("episode_label").unwrap();
let cover = builder.get_object("show_cover").unwrap();
let info = PlayerInfo {
mpris,
container: labels,
show,
episode,
cover,
episode_id: RefCell::new(None),
};
let radio150 = builder.get_object("rate_1_50").unwrap();
@@ -250,7 +284,7 @@ impl Default for PlayerWidget {
}
impl PlayerWidget {
pub fn new(sender: &Sender<Action>) -> Rc<Self> {
pub(crate) fn new(sender: &Sender<Action>) -> Rc<Self> {
let w = Rc::new(Self::default());
Self::init(&w, sender);
w
@@ -259,6 +293,7 @@ impl PlayerWidget {
fn init(s: &Rc<Self>, sender: &Sender<Action>) {
Self::connect_control_buttons(s);
Self::connect_rate_buttons(s);
Self::connect_mpris_buttons(s, sender);
Self::connect_gst_signals(s, sender);
}
@@ -287,6 +322,36 @@ impl PlayerWidget {
}));
}
fn connect_mpris_buttons(s: &Rc<Self>, sender: &Sender<Action>) {
let weak = Rc::downgrade(s);
let mpris = s.info.mpris.clone();
s.info.mpris.connect_play_pause(clone!(weak => move || {
let player = match weak.upgrade() {
Some(s) => s,
None => return
};
if let Ok(status) = mpris.get_playback_status() {
match status.as_ref() {
"Paused" => player.play(),
"Stopped" => player.play(),
_ => player.pause(),
};
}
}));
s.info.mpris.connect_next(clone!(weak => move || {
weak.upgrade().map(|p| p.fast_forward());
}));
s.info.mpris.connect_previous(clone!(weak => move || {
weak.upgrade().map(|p| p.rewind());
}));
s.info.mpris.connect_raise(clone!(sender => move || sender.send(Action::RaiseWindow)));
}
#[cfg_attr(rustfmt, rustfmt_skip)]
fn connect_gst_signals(s: &Rc<Self>, sender: &Sender<Action>) {
// Log gst warnings.
@@ -295,30 +360,30 @@ impl PlayerWidget {
// Log gst errors.
s.player.connect_error(clone!(sender => move |_, _error| {
// sender.send(Action::ErrorNotification(format!("Player Error: {}", error)));
let s = "The media player was unable to execute an action.".into();
let s = i18n("The media player was unable to execute an action.");
sender.send(Action::ErrorNotification(s));
}));
// The followign callbacks require `Send` but are handled by the gtk main loop
let weak = SendCell::new(Rc::downgrade(s));
// The following callbacks require `Send` but are handled by the gtk main loop
let weak = Fragile::new(Rc::downgrade(s));
// Update the duration label and the slider
s.player.connect_duration_changed(clone!(weak => move |_, clock| {
weak.borrow()
weak.get()
.upgrade()
.map(|p| p.timer.on_duration_changed(Duration(clock)));
}));
// Update the position label and the slider
s.player.connect_position_updated(clone!(weak => move |_, clock| {
weak.borrow()
weak.get()
.upgrade()
.map(|p| p.timer.on_position_updated(Position(clock)));
}));
// Reset the slider to 0 and show a play button
s.player.connect_end_of_stream(clone!(weak => move |_| {
weak.borrow()
weak.get()
.upgrade()
.map(|p| p.stop());
}));
@@ -349,14 +414,14 @@ impl PlayerWidget {
fn on_rate_changed(&self, rate: f64) {
self.set_playback_rate(rate);
self.rate.label.set_text(&format!("{:.2}x", rate));
self.rate.label.set_text(&format!("{:.2}×", rate));
}
fn reveal(&self) {
self.action_bar.show();
}
pub fn initialize_episode(&self, rowid: i32) -> Result<(), Error> {
pub(crate) fn initialize_episode(&self, rowid: i32) -> Result<(), Error> {
let ep = dbqueries::get_episode_widget_from_rowid(rowid)?;
let pd = dbqueries::get_podcast_cover_from_id(ep.show_id())?;
@@ -383,11 +448,49 @@ impl PlayerWidget {
Ok(())
}
fn connect_update_slider(slider: &gtk::Scale, player: &gst_player::Player) -> SignalHandlerId {
slider.connect_value_changed(clone!(player => move |slider| {
fn connect_update_slider(
slider: &gtk::Scale,
player: WeakRef<gst_player::Player>,
) -> SignalHandlerId {
slider.connect_value_changed(move |slider| {
let player = match player.upgrade() {
Some(p) => p,
None => return,
};
let value = slider.get_value() as u64;
player.seek(ClockTime::from_seconds(value));
}))
})
}
fn smart_rewind(&self) -> Option<()> {
lazy_static! {
static ref LAST_KNOWN_EPISODE: Mutex<Option<i32>> = Mutex::new(None);
};
// Figure out the time delta, in seconds, between the last pause and now
let now = Local::now();
let last: &Option<DateTime<_>> = &*self.controls.last_pause.borrow();
let last = last.clone()?;
let delta = (now - last).num_seconds();
// Get interval passed in the gst stream
let seconds_passed = self.player.get_position().seconds()?;
// get the last known episode id
let mut last = LAST_KNOWN_EPISODE.lock().unwrap();
// get the current playing episode id
let current_id = *self.info.episode_id.borrow();
// Only rewind on pause if the stream position is passed a certain point,
// and the player has been paused for more than a minute,
// and the episode id is the same
if seconds_passed >= 90 && delta >= 60 && current_id == *last {
self.seek(ClockTime::from_seconds(5), SeekDirection::Backwards);
}
// Set the last knows episode to the current one
*last = current_id;
Some(())
}
}
@@ -398,7 +501,9 @@ impl PlayerExt for PlayerWidget {
self.controls.pause.show();
self.controls.play.hide();
self.smart_rewind();
self.player.play();
self.info.mpris.set_playback_status(PlaybackStatus::Playing);
}
fn pause(&self) {
@@ -406,13 +511,9 @@ impl PlayerExt for PlayerWidget {
self.controls.play.show();
self.player.pause();
self.info.mpris.set_playback_status(PlaybackStatus::Paused);
// Only rewind on pause if the stream position is passed a certain point.
if let Some(sec) = self.player.get_position().seconds() {
if sec >= 90 {
self.seek(ClockTime::from_seconds(5), SeekDirection::Backwards);
}
}
self.controls.last_pause.replace(Some(Local::now()));
}
#[cfg_attr(rustfmt, rustfmt_skip)]
@@ -421,6 +522,7 @@ impl PlayerExt for PlayerWidget {
self.controls.play.show();
self.player.stop();
self.info.mpris.set_playback_status(PlaybackStatus::Paused);
// Reset the slider bar to the start
self.timer.on_position_updated(Position(ClockTime::from_seconds(0)));
+46 -78
View File
@@ -1,32 +1,27 @@
use glib;
use gtk;
use gtk::prelude::*;
use gtk::{self, prelude::*, Adjustment};
use crossbeam_channel::Sender;
use crossbeam_channel::{bounded, Sender};
use failure::Error;
use fragile::Fragile;
use html2text;
use libhandy::{Column, ColumnExt};
use rayon;
use send_cell::SendCell;
use podcasts_data::dbqueries;
use podcasts_data::Show;
use app::Action;
use utils::{self, lazy_load};
use widgets::{EpisodeWidget, ShowMenu};
use widgets::{BaseView, EmptyShow, EpisodeWidget, ShowMenu};
use std::ops::Deref;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
lazy_static! {
static ref SHOW_WIDGET_VALIGNMENT: Mutex<Option<(i32, SendCell<gtk::Adjustment>)>> =
Mutex::new(None);
}
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct ShowWidget {
pub container: gtk::Box,
scrolled_window: gtk::ScrolledWindow,
pub(crate) struct ShowWidget {
pub(crate) view: BaseView,
cover: gtk::Image,
description: gtk::Label,
episodes: gtk::ListBox,
@@ -36,16 +31,25 @@ pub struct ShowWidget {
impl Default for ShowWidget {
fn default() -> Self {
let builder = gtk::Builder::new_from_resource("/org/gnome/Podcasts/gtk/show_widget.ui");
let container: gtk::Box = builder.get_object("container").unwrap();
let scrolled_window: gtk::ScrolledWindow = builder.get_object("scrolled_window").unwrap();
let episodes = builder.get_object("episodes").unwrap();
let sub_cont: gtk::Box = builder.get_object("sub_container").unwrap();
let cover: gtk::Image = builder.get_object("cover").unwrap();
let description: gtk::Label = builder.get_object("description").unwrap();
let episodes = builder.get_object("episodes").unwrap();
let view = BaseView::default();
let column = Column::new();
column.set_maximum_width(700);
// For some reason the Column is not seen as a gtk::container
// and therefore we can't call add() without the cast
let column = column.upcast::<gtk::Widget>();
let column = column.downcast::<gtk::Container>().unwrap();
column.add(&sub_cont);
view.add(&column);
column.show_all();
ShowWidget {
container,
scrolled_window,
view,
cover,
description,
episodes,
@@ -55,21 +59,25 @@ impl Default for ShowWidget {
}
impl ShowWidget {
pub fn new(pd: Arc<Show>, sender: Sender<Action>) -> Rc<ShowWidget> {
pub(crate) fn new(
pd: Arc<Show>,
sender: Sender<Action>,
vadj: Option<Adjustment>,
) -> Rc<ShowWidget> {
let mut pdw = ShowWidget::default();
pdw.init(&pd);
let menu = ShowMenu::new(&pd, &pdw.episodes, &sender);
sender.send(Action::InitShowMenu(SendCell::new(menu)));
sender.send(Action::InitShowMenu(Fragile::new(menu)));
let pdw = Rc::new(pdw);
let res = populate_listbox(&pdw, pd.clone(), sender);
let res = populate_listbox(&pdw, pd.clone(), sender, vadj);
debug_assert!(res.is_ok());
pdw
}
pub fn init(&mut self, pd: &Arc<Show>) {
pub(crate) fn init(&mut self, pd: &Arc<Show>) {
self.set_description(pd.description());
self.show_id = Some(pd.id());
@@ -82,51 +90,13 @@ impl ShowWidget {
utils::set_image_from_path(&self.cover, pd.id(), 256)
}
/// Set the descripton text.
/// Set the description text.
fn set_description(&self, text: &str) {
self.description
.set_markup(html2text::from_read(text.as_bytes(), 70).trim());
.set_markup(html2text::from_read(text.as_bytes(), 80).trim());
}
/// Save the scrollabar vajustment to the cache.
pub fn save_vadjustment(&self, oldid: i32) -> Result<(), Error> {
if let Ok(mut guard) = SHOW_WIDGET_VALIGNMENT.lock() {
let adj = self
.scrolled_window
.get_vadjustment()
.ok_or_else(|| format_err!("Could not get the adjustment"))?;
*guard = Some((oldid, SendCell::new(adj)));
debug!("Widget Alignment was saved with ID: {}.", oldid);
}
Ok(())
}
/// Set scrolled window vertical adjustment.
fn set_vadjustment(&self, pd: &Arc<Show>) -> Result<(), Error> {
let guard = SHOW_WIDGET_VALIGNMENT
.lock()
.map_err(|err| format_err!("Failed to lock widget align mutex: {}", err))?;
if let Some((oldid, ref sendcell)) = *guard {
// Only copy the old scrollbar if both widget's represent the same podcast.
debug!("PID: {}", pd.id());
debug!("OLDID: {}", oldid);
if pd.id() != oldid {
debug!("Early return");
return Ok(());
};
// Copy the vertical scrollbar adjustment from the old view into the new one.
sendcell
.try_get()
.map(|x| utils::smooth_scroll_to(&self.scrolled_window, &x));
}
Ok(())
}
pub fn show_id(&self) -> Option<i32> {
pub(crate) fn show_id(&self) -> Option<i32> {
self.show_id
}
}
@@ -136,9 +106,8 @@ fn populate_listbox(
show: &Rc<ShowWidget>,
pd: Arc<Show>,
sender: Sender<Action>,
vadj: Option<Adjustment>,
) -> Result<(), Error> {
use crossbeam_channel::bounded;
let count = dbqueries::get_pd_episodes_count(&pd)?;
let (sender_, receiver) = bounded(1);
@@ -151,15 +120,13 @@ fn populate_listbox(
}));
if count == 0 {
let builder = gtk::Builder::new_from_resource("/org/gnome/Podcasts/gtk/empty_show.ui");
let container: gtk::Box = builder
.get_object("empty_show")
.ok_or_else(|| format_err!("FOO"))?;
show.episodes.add(&container);
let empty = EmptyShow::default();
show.episodes.add(empty.deref());
return Ok(());
}
let show_ = show.clone();
let show_weak = Rc::downgrade(&show);
let list_weak = show.episodes.downgrade();
gtk::idle_add(move || {
let episodes = match receiver.try_recv() {
Some(e) => e,
@@ -167,17 +134,18 @@ fn populate_listbox(
};
debug_assert!(episodes.len() as i64 == count);
let list = show_.episodes.clone();
let constructor = clone!(sender => move |ep| {
EpisodeWidget::new(ep, &sender).container.clone()
});
let callback = clone!(pd, show_ => move || {
let res = show_.set_vadjustment(&pd);
debug_assert!(res.is_ok());
let callback = clone!(show_weak, vadj => move || {
match (show_weak.upgrade(), &vadj) {
(Some(ref shows), Some(ref v)) => shows.view.set_adjutments(None, Some(v)),
_ => (),
};
});
lazy_load(episodes, list.clone(), constructor, callback);
lazy_load(episodes, list_weak.clone(), constructor, callback);
glib::Continue(false)
});

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