Merge branch 'embedded-player' into 'master'
Embedded player Closes #38 See merge request World/hammond!40
This commit is contained in:
commit
c7cfc81c6f
@ -25,7 +25,7 @@ variables:
|
|||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- apt-get update -yqq
|
- apt-get update -yqq
|
||||||
- apt-get install -yqq --no-install-recommends build-essential libgtk-3-dev meson
|
- apt-get install -yqq --no-install-recommends build-essential libgtk-3-dev meson libgstreamer1.0-dev
|
||||||
|
|
||||||
- mkdir -p .cargo_cache
|
- mkdir -p .cargo_cache
|
||||||
# Only stuff inside the repo directory can be cached
|
# Only stuff inside the repo directory can be cached
|
||||||
@ -41,12 +41,12 @@ variables:
|
|||||||
- cargo test -- --test-threads=1 --ignored
|
- cargo test -- --test-threads=1 --ignored
|
||||||
<<: *cargo_cache
|
<<: *cargo_cache
|
||||||
|
|
||||||
rust:stable:
|
.rust:stable:
|
||||||
# https://hub.docker.com/_/rust/
|
# https://hub.docker.com/_/rust/
|
||||||
image: "rust"
|
image: "rust"
|
||||||
<<: *cargo_test
|
<<: *cargo_test
|
||||||
|
|
||||||
rust:nightly:
|
.rust:nightly:
|
||||||
# https://hub.docker.com/r/rustlang/rust/
|
# https://hub.docker.com/r/rustlang/rust/
|
||||||
image: "rustlang/rust:nightly"
|
image: "rustlang/rust:nightly"
|
||||||
<<: *cargo_test
|
<<: *cargo_test
|
||||||
@ -129,7 +129,7 @@ rustfmt:
|
|||||||
|
|
||||||
# Configure and run clippy on nightly
|
# Configure and run clippy on nightly
|
||||||
# Only fails on errors atm.
|
# Only fails on errors atm.
|
||||||
clippy:
|
.clippy:
|
||||||
image: "registry.gitlab.gnome.org/alatiera/hammond-container-images/clippy:nightly"
|
image: "registry.gitlab.gnome.org/alatiera/hammond-container-images/clippy:nightly"
|
||||||
stage: lint
|
stage: lint
|
||||||
variables:
|
variables:
|
||||||
|
|||||||
11
CHANGELOG.md
11
CHANGELOG.md
@ -6,12 +6,17 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
### Added:
|
### Added:
|
||||||
- Keyboard Shortcuts and a Shortcuts dialog were implemented. (ZanderBrown) [!33](https://gitlab.gnome.org/World/hammond/merge_requests/33)
|
- Keyboard Shortcuts and a Shortcuts dialog were implemented. (ZanderBrown)
|
||||||
|
[!33](https://gitlab.gnome.org/World/hammond/merge_requests/33)
|
||||||
|
|
||||||
### Changed:
|
### Changed:
|
||||||
- The `FileChooser` of the OPML import was changed to use the `FileChooserNative` widget/API. (ZanderBrown) [!33](https://gitlab.gnome.org/World/hammond/merge_requests/33)
|
- The `FileChooser` of the OPML import was changed to use the `FileChooserNative` widget/API. (ZanderBrown)
|
||||||
- The `EpisdeWidget` was refactored. [!38](https://gitlab.gnome.org/World/hammond/merge_requests/38)
|
[!33](https://gitlab.gnome.org/World/hammond/merge_requests/33)
|
||||||
|
- The `EpisdeWidget` was refactored.
|
||||||
|
[!38](https://gitlab.gnome.org/World/hammond/merge_requests/38)
|
||||||
- `EpisdeWidget`'s progressbar was changed to be non-blocking and should feel way more responsive now. 9b0ac5b83dadecdff51cd398293afdf0d5276012
|
- `EpisdeWidget`'s progressbar was changed to be non-blocking and should feel way more responsive now. 9b0ac5b83dadecdff51cd398293afdf0d5276012
|
||||||
|
- An embeded audio player was implemented!
|
||||||
|
[!40](https://gitlab.gnome.org/World/hammond/merge_requests/40)
|
||||||
|
|
||||||
### Fixed:
|
### Fixed:
|
||||||
- Fixed a bug whre the about dialog would be unclosable. (ZanderBrown) [!37](https://gitlab.gnome.org/World/hammond/merge_requests/37)
|
- Fixed a bug whre the about dialog would be unclosable. (ZanderBrown) [!37](https://gitlab.gnome.org/World/hammond/merge_requests/37)
|
||||||
|
|||||||
142
Cargo.lock
generated
142
Cargo.lock
generated
@ -701,6 +701,122 @@ dependencies = [
|
|||||||
"pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gstreamer"
|
||||||
|
version = "0.11.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"cfg-if 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"gstreamer-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"muldiv 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"num-rational 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gstreamer-base"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"gstreamer 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"gstreamer-base-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"gstreamer-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gstreamer-base-sys"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"gstreamer-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gstreamer-player"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"gstreamer 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"gstreamer-player-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"gstreamer-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"gstreamer-video 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gstreamer-player-sys"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"gstreamer-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"gstreamer-video-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gstreamer-sys"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gstreamer-video"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"gstreamer 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"gstreamer-base 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"gstreamer-base-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"gstreamer-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"gstreamer-video-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gstreamer-video-sys"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"gstreamer-base-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"gstreamer-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gtk"
|
name = "gtk"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@ -805,6 +921,8 @@ dependencies = [
|
|||||||
"gdk-pixbuf 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gdk-pixbuf 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gio 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gio 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"gstreamer 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"gstreamer-player 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gtk 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gtk 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"hammond-data 0.1.0",
|
"hammond-data 0.1.0",
|
||||||
"hammond-downloader 0.1.0",
|
"hammond-downloader 0.1.0",
|
||||||
@ -1185,6 +1303,11 @@ dependencies = [
|
|||||||
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "muldiv"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "native-tls"
|
name = "native-tls"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
@ -1230,6 +1353,15 @@ dependencies = [
|
|||||||
"num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-rational"
|
||||||
|
version = "0.1.42"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"num-integer 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.1.43"
|
version = "0.1.43"
|
||||||
@ -2462,6 +2594,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
"checksum glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "615bef979b5838526aee99241afc80cfb2e34a8735d4bcb8ec6072598c18a408"
|
"checksum glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "615bef979b5838526aee99241afc80cfb2e34a8735d4bcb8ec6072598c18a408"
|
||||||
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
|
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
|
||||||
"checksum gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "70409d6405db8b1591602fcd0cbe8af52cd9976dd39194442b4c149ba343f86d"
|
"checksum gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "70409d6405db8b1591602fcd0cbe8af52cd9976dd39194442b4c149ba343f86d"
|
||||||
|
"checksum gstreamer 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "90d7cde9eae8f6bf4d41c254915976b236143935ec2c0b027636274e998e54b4"
|
||||||
|
"checksum gstreamer-base 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05ec7a84b4160b61c72ea27ccf3f46eb9c8f996c5991746623e69e3e532e3cb5"
|
||||||
|
"checksum gstreamer-base-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "501a7add44f256aab6cb5b65ef121c449197cf55087d6a7586846c8d1e42e88b"
|
||||||
|
"checksum gstreamer-player 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d831e2c4aa1296a8d0f3b4caa0b5ae8d6f5c0eed67ed20236f8647010005c63e"
|
||||||
|
"checksum gstreamer-player-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3b9476078cc76164446e88b2c4331e81e24a07f7b7c3a8b4bf8975a47998ebd4"
|
||||||
|
"checksum gstreamer-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b2f51e25a6f97dd4bfd640cba96f192f8759b8766afd66d6d9ea0f82ca14a37"
|
||||||
|
"checksum gstreamer-video 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "76c6c8971688f530ae93e96ea29fe6051658bb4d00b4b40d30575ca1d8a25a18"
|
||||||
|
"checksum gstreamer-video-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ed798787e78a0f1c8be06bd3adcab03f962f049a820743aae9f690f56a0d538"
|
||||||
"checksum gtk 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d695d6be4110618a97c19cd068e8a00e53e33b87e3c65cdc5397667498b1bc24"
|
"checksum gtk 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d695d6be4110618a97c19cd068e8a00e53e33b87e3c65cdc5397667498b1bc24"
|
||||||
"checksum gtk-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d9554cf5b3a85a13fb39258c65b04b262989c1d7a758f8f555b77a478621a91"
|
"checksum gtk-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d9554cf5b3a85a13fb39258c65b04b262989c1d7a758f8f555b77a478621a91"
|
||||||
"checksum handlebars 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e7bdb08e879b8c78ee90f5022d121897c31ea022cb0cc6d13f2158c7a9fbabb1"
|
"checksum handlebars 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e7bdb08e879b8c78ee90f5022d121897c31ea022cb0cc6d13f2158c7a9fbabb1"
|
||||||
@ -2503,11 +2643,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
"checksum mime_guess 2.0.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "130ea3c9c1b65dba905ab5a4d9ac59234a9585c24d135f264e187fe7336febbd"
|
"checksum mime_guess 2.0.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "130ea3c9c1b65dba905ab5a4d9ac59234a9585c24d135f264e187fe7336febbd"
|
||||||
"checksum mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "6d771e3ef92d58a8da8df7d6976bfca9371ed1de6619d9d5a5ce5b1f29b85bfe"
|
"checksum mio 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "6d771e3ef92d58a8da8df7d6976bfca9371ed1de6619d9d5a5ce5b1f29b85bfe"
|
||||||
"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
|
"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
|
||||||
|
"checksum muldiv 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1cbef5aa2e8cd82a18cc20e26434cc9843e1ef46e55bfabe5bddb022236c5b3e"
|
||||||
"checksum native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f74dbadc8b43df7864539cedb7bc91345e532fdd913cfdc23ad94f4d2d40fbc0"
|
"checksum native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f74dbadc8b43df7864539cedb7bc91345e532fdd913cfdc23ad94f4d2d40fbc0"
|
||||||
"checksum net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)" = "9044faf1413a1057267be51b5afba8eb1090bd2231c693664aa1db716fe1eae0"
|
"checksum net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)" = "9044faf1413a1057267be51b5afba8eb1090bd2231c693664aa1db716fe1eae0"
|
||||||
"checksum new_debug_unreachable 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0cdc457076c78ab54d5e0d6fa7c47981757f1e34dc39ff92787f217dede586c4"
|
"checksum new_debug_unreachable 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0cdc457076c78ab54d5e0d6fa7c47981757f1e34dc39ff92787f217dede586c4"
|
||||||
"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2"
|
"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2"
|
||||||
"checksum num-integer 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)" = "6ac0ea58d64a89d9d6b7688031b3be9358d6c919badcf7fbb0527ccfd891ee45"
|
"checksum num-integer 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)" = "6ac0ea58d64a89d9d6b7688031b3be9358d6c919badcf7fbb0527ccfd891ee45"
|
||||||
|
"checksum num-rational 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "ee314c74bd753fc86b4780aa9475da469155f3848473a261d2d18e35245a784e"
|
||||||
"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
|
"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
|
||||||
"checksum num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "775393e285254d2f5004596d69bb8bc1149754570dcc08cf30cabeba67955e28"
|
"checksum num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "775393e285254d2f5004596d69bb8bc1149754570dcc08cf30cabeba67955e28"
|
||||||
"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30"
|
"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30"
|
||||||
|
|||||||
@ -85,7 +85,9 @@ pub use models::{Episode, EpisodeWidgetQuery, Podcast, PodcastCoverQuery, Source
|
|||||||
|
|
||||||
// Set the user agent, See #53 for more
|
// Set the user agent, See #53 for more
|
||||||
// Keep this in sync with Tor-browser releases
|
// Keep this in sync with Tor-browser releases
|
||||||
const USER_AGENT: &str = "Mozilla/5.0 (Windows NT 6.1; rv:52.0) Gecko/20100101 Firefox/52.0";
|
/// The user-agent to be used for all the requests.
|
||||||
|
/// It originates from the Tor-browser UA.
|
||||||
|
pub const USER_AGENT: &str = "Mozilla/5.0 (Windows NT 6.1; rv:52.0) Gecko/20100101 Firefox/52.0";
|
||||||
|
|
||||||
/// [XDG Base Direcotory](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html) Paths.
|
/// [XDG Base Direcotory](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html) Paths.
|
||||||
#[allow(missing_debug_implementations)]
|
#[allow(missing_debug_implementations)]
|
||||||
|
|||||||
@ -11,6 +11,8 @@ crossbeam-channel = "0.1.2"
|
|||||||
gdk = "0.8.0"
|
gdk = "0.8.0"
|
||||||
gdk-pixbuf = "0.4.0"
|
gdk-pixbuf = "0.4.0"
|
||||||
glib = "0.5.0"
|
glib = "0.5.0"
|
||||||
|
gstreamer = "0.11.2"
|
||||||
|
gstreamer-player = "0.11.0"
|
||||||
humansize = "1.1.0"
|
humansize = "1.1.0"
|
||||||
lazy_static = "1.0.0"
|
lazy_static = "1.0.0"
|
||||||
log = "0.4.1"
|
log = "0.4.1"
|
||||||
|
|||||||
372
hammond-gtk/resources/gtk/player_toolbar.ui
Normal file
372
hammond-gtk/resources/gtk/player_toolbar.ui
Normal file
@ -0,0 +1,372 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Generated with glade 3.22.0 -->
|
||||||
|
<interface>
|
||||||
|
<requires lib="gtk+" version="3.20"/>
|
||||||
|
<object class="GtkImage" id="ff_image">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="sensitive">False</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="margin_top">1</property>
|
||||||
|
<property name="icon_name">media-seek-forward-symbolic</property>
|
||||||
|
<property name="icon_size">1</property>
|
||||||
|
</object>
|
||||||
|
<object class="GtkImage" id="pause_image">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="margin_top">1</property>
|
||||||
|
<property name="icon_name">media-playback-pause-symbolic</property>
|
||||||
|
<property name="icon_size">1</property>
|
||||||
|
</object>
|
||||||
|
<object class="GtkImage" id="play_image">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="margin_top">1</property>
|
||||||
|
<property name="icon_name">media-playback-start-symbolic</property>
|
||||||
|
<property name="icon_size">1</property>
|
||||||
|
</object>
|
||||||
|
<object class="GtkImage" id="previous_image">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="margin_top">1</property>
|
||||||
|
<property name="icon_name">media-seek-backward-symbolic</property>
|
||||||
|
<property name="icon_size">1</property>
|
||||||
|
</object>
|
||||||
|
<object class="GtkActionBar" id="action_bar">
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="no_show_all">True</property>
|
||||||
|
<property name="valign">center</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="buttons">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="valign">center</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="rewind_button">
|
||||||
|
<property name="width_request">42</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Previous</property>
|
||||||
|
<property name="image">previous_image</property>
|
||||||
|
<property name="always_show_image">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="play_button">
|
||||||
|
<property name="width_request">60</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Play</property>
|
||||||
|
<property name="image">play_image</property>
|
||||||
|
<property name="always_show_image">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="pause_button">
|
||||||
|
<property name="width_request">60</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Play</property>
|
||||||
|
<property name="image">pause_image</property>
|
||||||
|
<property name="always_show_image">True</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="ff_button">
|
||||||
|
<property name="width_request">42</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="tooltip_text" translatable="yes">Next</property>
|
||||||
|
<property name="image">ff_image</property>
|
||||||
|
<property name="always_show_image">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
<property name="position">3</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<style>
|
||||||
|
<class name="linked"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="info">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="spacing">6</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkImage" id="show_cover">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="valign">center</property>
|
||||||
|
<property name="pixel_size">34</property>
|
||||||
|
<property name="icon_name">image-x-generic-symbolic</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</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="receives_default">True</property>
|
||||||
|
<property name="halign">center</property>
|
||||||
|
<property name="valign">center</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="show_label">
|
||||||
|
<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="wrap">True</property>
|
||||||
|
<property name="ellipsize">end</property>
|
||||||
|
<property name="max_width_chars">20</property>
|
||||||
|
<style>
|
||||||
|
<class name="player-show-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" id="episode_label">
|
||||||
|
<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="wrap">True</property>
|
||||||
|
<property name="ellipsize">end</property>
|
||||||
|
<property name="max_width_chars">20</property>
|
||||||
|
<style>
|
||||||
|
<class name="player-episode-label"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkScale" id="seek">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="hexpand">True</property>
|
||||||
|
<property name="round_digits">1</property>
|
||||||
|
<property name="draw_value">False</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="timer">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="valign">center</property>
|
||||||
|
<property name="spacing">6</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="progress_time_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="valign">center</property>
|
||||||
|
<property name="label">0:00</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="separator">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="valign">center</property>
|
||||||
|
<property name="label">/</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="total_duration_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="valign">center</property>
|
||||||
|
<property name="label">0:00</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="position">3</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkMenuButton" id="rate_button">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="halign">center</property>
|
||||||
|
<property name="valign">center</property>
|
||||||
|
<property name="margin_left">6</property>
|
||||||
|
<property name="direction">up</property>
|
||||||
|
<property name="popover">rate_popover</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="spacing">6</property>
|
||||||
|
<child>
|
||||||
|
<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>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkImage">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="icon_name">go-down-symbolic</property>
|
||||||
|
<property name="icon_size">1</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="position">4</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<object class="GtkPopover" id="rate_popover">
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="relative_to">rate_button</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">3</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkRadioButton" id="rate_1_50">
|
||||||
|
<property name="label" translatable="yes">1.50x</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">False</property>
|
||||||
|
<property name="halign">center</property>
|
||||||
|
<property name="valign">center</property>
|
||||||
|
<property name="draw_indicator">True</property>
|
||||||
|
<property name="group">normal_rate</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkRadioButton" id="rate_1_25">
|
||||||
|
<property name="label" translatable="yes">1.25x</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">False</property>
|
||||||
|
<property name="halign">center</property>
|
||||||
|
<property name="valign">center</property>
|
||||||
|
<property name="draw_indicator">True</property>
|
||||||
|
<property name="group">normal_rate</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkRadioButton" id="normal_rate">
|
||||||
|
<property name="label" translatable="yes">1.00x</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">False</property>
|
||||||
|
<property name="halign">center</property>
|
||||||
|
<property name="valign">center</property>
|
||||||
|
<property name="active">True</property>
|
||||||
|
<property name="draw_indicator">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</interface>
|
||||||
@ -9,3 +9,12 @@ row:last-child {
|
|||||||
list, border {
|
list, border {
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.player-episode-label {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: smaller;
|
||||||
|
}
|
||||||
|
|
||||||
|
.player-show-label {
|
||||||
|
font-size: smaller;
|
||||||
|
}
|
||||||
@ -13,6 +13,7 @@
|
|||||||
<file compressed="true" preprocess="xml-stripblanks">gtk/inapp_notif.ui</file>
|
<file compressed="true" preprocess="xml-stripblanks">gtk/inapp_notif.ui</file>
|
||||||
<file compressed="true" preprocess="xml-stripblanks">gtk/menus.ui</file>
|
<file compressed="true" preprocess="xml-stripblanks">gtk/menus.ui</file>
|
||||||
<file compressed="true" preprocess="xml-stripblanks">gtk/help-overlay.ui</file>
|
<file compressed="true" preprocess="xml-stripblanks">gtk/help-overlay.ui</file>
|
||||||
|
<file compressed="true" preprocess="xml-stripblanks">gtk/player_toolbar.ui</file>
|
||||||
<file compressed="true">gtk/style.css</file>
|
<file compressed="true">gtk/style.css</file>
|
||||||
</gresource>
|
</gresource>
|
||||||
</gresources>
|
</gresources>
|
||||||
|
|||||||
@ -17,6 +17,7 @@ use settings::{self, WindowGeometry};
|
|||||||
use stacks::{Content, PopulatedState};
|
use stacks::{Content, PopulatedState};
|
||||||
use utils;
|
use utils;
|
||||||
use widgets::appnotif::{InAppNotification, UndoState};
|
use widgets::appnotif::{InAppNotification, UndoState};
|
||||||
|
use widgets::player;
|
||||||
use widgets::{about_dialog, mark_all_notif, remove_show_notif};
|
use widgets::{about_dialog, mark_all_notif, remove_show_notif};
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
@ -53,6 +54,7 @@ pub enum Action {
|
|||||||
MarkAllPlayerNotification(Arc<Podcast>),
|
MarkAllPlayerNotification(Arc<Podcast>),
|
||||||
RemoveShow(Arc<Podcast>),
|
RemoveShow(Arc<Podcast>),
|
||||||
ErrorNotification(String),
|
ErrorNotification(String),
|
||||||
|
InitEpisode(i32),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -67,10 +69,6 @@ impl App {
|
|||||||
let application = gtk::Application::new("org.gnome.Hammond", ApplicationFlags::empty())
|
let application = gtk::Application::new("org.gnome.Hammond", ApplicationFlags::empty())
|
||||||
.expect("Application Initialization failed...");
|
.expect("Application Initialization failed...");
|
||||||
|
|
||||||
// Weird magic I copy-pasted that sets the Application Name in the Shell.
|
|
||||||
glib::set_application_name("Hammond");
|
|
||||||
glib::set_prgname(Some("Hammond"));
|
|
||||||
|
|
||||||
let cleanup_date = settings::get_cleanup_date(&settings);
|
let cleanup_date = settings::get_cleanup_date(&settings);
|
||||||
utils::cleanup(cleanup_date);
|
utils::cleanup(cleanup_date);
|
||||||
|
|
||||||
@ -101,6 +99,7 @@ impl App {
|
|||||||
Inhibit(false)
|
Inhibit(false)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
// Create a content instance
|
// Create a content instance
|
||||||
let content =
|
let content =
|
||||||
Rc::new(Content::new(sender.clone()).expect(
|
Rc::new(Content::new(sender.clone()).expect(
|
||||||
@ -117,8 +116,15 @@ impl App {
|
|||||||
let overlay = gtk::Overlay::new();
|
let overlay = gtk::Overlay::new();
|
||||||
overlay.add(&content.get_stack());
|
overlay.add(&content.get_stack());
|
||||||
|
|
||||||
// Add the overlay to the main window
|
let wrap = gtk::Box::new(gtk::Orientation::Vertical, 0);
|
||||||
window.add(&overlay);
|
// Add the overlay to the main Box
|
||||||
|
wrap.add(&overlay);
|
||||||
|
|
||||||
|
let player = player::PlayerWidget::new(&sender);
|
||||||
|
// Add the player to the main Box
|
||||||
|
wrap.add(&player.action_bar);
|
||||||
|
|
||||||
|
window.add(&wrap);
|
||||||
|
|
||||||
WindowGeometry::from_settings(&settings).apply(&window);
|
WindowGeometry::from_settings(&settings).apply(&window);
|
||||||
|
|
||||||
@ -127,8 +133,8 @@ impl App {
|
|||||||
window.show_all();
|
window.show_all();
|
||||||
window.activate();
|
window.activate();
|
||||||
|
|
||||||
gtk::timeout_add(50, clone!(sender, receiver => move || {
|
gtk::timeout_add(25, clone!(sender, receiver => move || {
|
||||||
// Uses receiver, content, header, sender, overlay
|
// Uses receiver, content, header, sender, overlay, playback
|
||||||
match receiver.try_recv() {
|
match receiver.try_recv() {
|
||||||
Ok(Action::RefreshAllViews) => content.update(),
|
Ok(Action::RefreshAllViews) => content.update(),
|
||||||
Ok(Action::RefreshShowsView) => content.update_shows_view(),
|
Ok(Action::RefreshShowsView) => content.update_shows_view(),
|
||||||
@ -183,7 +189,8 @@ impl App {
|
|||||||
let notif = InAppNotification::new(&err, callback,
|
let notif = InAppNotification::new(&err, callback,
|
||||||
|| {}, UndoState::Hidden);
|
|| {}, UndoState::Hidden);
|
||||||
notif.show(&overlay);
|
notif.show(&overlay);
|
||||||
}
|
},
|
||||||
|
Ok(Action::InitEpisode(rowid)) => player.initialize_episode(rowid).unwrap(),
|
||||||
Err(_) => (),
|
Err(_) => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,6 +295,11 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(self) {
|
pub fn run(self) {
|
||||||
|
// Weird magic I copy-pasted that sets the Application Name in the Shell.
|
||||||
|
glib::set_application_name("Hammond");
|
||||||
|
glib::set_prgname(Some("Hammond"));
|
||||||
|
// We need out own org.gnome.Hammon icon
|
||||||
|
gtk::Window::set_default_icon_name("multimedia-player");
|
||||||
ApplicationExtManual::run(&self.app_instance, &[]);
|
ApplicationExtManual::run(&self.app_instance, &[]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,8 @@ extern crate gdk;
|
|||||||
extern crate gdk_pixbuf;
|
extern crate gdk_pixbuf;
|
||||||
extern crate gio;
|
extern crate gio;
|
||||||
extern crate glib;
|
extern crate glib;
|
||||||
|
extern crate gstreamer as gst;
|
||||||
|
extern crate gstreamer_player as gst_player;
|
||||||
extern crate gtk;
|
extern crate gtk;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
@ -80,6 +82,7 @@ fn main() {
|
|||||||
// TODO: make the the logger a cli -vv option
|
// TODO: make the the logger a cli -vv option
|
||||||
loggerv::init_with_level(Level::Info).expect("Error initializing loggerv.");
|
loggerv::init_with_level(Level::Info).expect("Error initializing loggerv.");
|
||||||
gtk::init().expect("Error initializing gtk.");
|
gtk::init().expect("Error initializing gtk.");
|
||||||
|
gst::init().expect("Error initializing gstreamer");
|
||||||
static_resource::init().expect("Something went wrong with the resource file initialization.");
|
static_resource::init().expect("Something went wrong with the resource file initialization.");
|
||||||
|
|
||||||
// Add custom style
|
// Add custom style
|
||||||
|
|||||||
@ -7,6 +7,7 @@ use chrono::prelude::*;
|
|||||||
use crossbeam_channel::Sender;
|
use crossbeam_channel::Sender;
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
use humansize::{file_size_opts as size_opts, FileSize};
|
use humansize::{file_size_opts as size_opts, FileSize};
|
||||||
|
#[allow(unused_imports)]
|
||||||
use open;
|
use open;
|
||||||
|
|
||||||
use hammond_data::dbqueries;
|
use hammond_data::dbqueries;
|
||||||
@ -16,7 +17,6 @@ use hammond_data::EpisodeWidgetQuery;
|
|||||||
use app::Action;
|
use app::Action;
|
||||||
use manager;
|
use manager;
|
||||||
|
|
||||||
use std::path::Path;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::{Arc, Mutex, TryLockError};
|
use std::sync::{Arc, Mutex, TryLockError};
|
||||||
|
|
||||||
@ -446,29 +446,19 @@ fn on_play_bttn_clicked(
|
|||||||
episode: &mut EpisodeWidgetQuery,
|
episode: &mut EpisodeWidgetQuery,
|
||||||
sender: &Sender<Action>,
|
sender: &Sender<Action>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
open_uri(episode.rowid())?;
|
// Mark played
|
||||||
episode.set_played_now()?;
|
episode.set_played_now()?;
|
||||||
|
// Grey out the title
|
||||||
widget.info.set_title(&episode);
|
widget.info.set_title(&episode);
|
||||||
|
|
||||||
|
// Play the episode
|
||||||
|
sender.send(Action::InitEpisode(episode.rowid()))?;
|
||||||
|
// Refresh background views to match the normal/greyout title state
|
||||||
sender
|
sender
|
||||||
.send(Action::RefreshEpisodesViewBGR)
|
.send(Action::RefreshEpisodesViewBGR)
|
||||||
.map_err(From::from)
|
.map_err(From::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_uri(rowid: i32) -> Result<(), Error> {
|
|
||||||
let uri = dbqueries::get_episode_local_uri_from_id(rowid)?
|
|
||||||
.ok_or_else(|| format_err!("Expected Some found None."))?;
|
|
||||||
|
|
||||||
if Path::new(&uri).exists() {
|
|
||||||
info!("Opening {}", uri);
|
|
||||||
open::that(&uri)?;
|
|
||||||
} else {
|
|
||||||
bail!("File \"{}\" does not exist.", uri);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup a callback that will update the progress bar.
|
// Setup a callback that will update the progress bar.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[cfg_attr(feature = "cargo-clippy", allow(if_same_then_else))]
|
#[cfg_attr(feature = "cargo-clippy", allow(if_same_then_else))]
|
||||||
|
|||||||
@ -3,6 +3,7 @@ pub mod appnotif;
|
|||||||
mod empty;
|
mod empty;
|
||||||
mod episode;
|
mod episode;
|
||||||
mod home_view;
|
mod home_view;
|
||||||
|
pub mod player;
|
||||||
mod show;
|
mod show;
|
||||||
mod shows_view;
|
mod shows_view;
|
||||||
|
|
||||||
|
|||||||
430
hammond-gtk/src/widgets/player.rs
Normal file
430
hammond-gtk/src/widgets/player.rs
Normal file
@ -0,0 +1,430 @@
|
|||||||
|
use gst::prelude::*;
|
||||||
|
use gst::ClockTime;
|
||||||
|
use gst_player;
|
||||||
|
|
||||||
|
use gtk;
|
||||||
|
use gtk::prelude::*;
|
||||||
|
|
||||||
|
use gio::{File, FileExt};
|
||||||
|
use glib::SignalHandlerId;
|
||||||
|
|
||||||
|
use chrono::NaiveTime;
|
||||||
|
use crossbeam_channel::Sender;
|
||||||
|
use failure::Error;
|
||||||
|
use send_cell::SendCell;
|
||||||
|
|
||||||
|
use hammond_data::{dbqueries, USER_AGENT};
|
||||||
|
use hammond_data::{EpisodeWidgetQuery, PodcastCoverQuery};
|
||||||
|
|
||||||
|
use app::Action;
|
||||||
|
use utils::set_image_from_path;
|
||||||
|
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
enum SeekDirection {
|
||||||
|
Backwards,
|
||||||
|
Forward,
|
||||||
|
}
|
||||||
|
|
||||||
|
trait PlayerExt {
|
||||||
|
fn play(&self);
|
||||||
|
fn pause(&self);
|
||||||
|
fn stop(&self);
|
||||||
|
fn seek(&self, offset: ClockTime, direction: SeekDirection);
|
||||||
|
fn fast_forward(&self);
|
||||||
|
fn rewind(&self);
|
||||||
|
fn set_playback_rate(&self, f64);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct PlayerInfo {
|
||||||
|
container: gtk::Box,
|
||||||
|
show: gtk::Label,
|
||||||
|
episode: gtk::Label,
|
||||||
|
cover: gtk::Image,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlayerInfo {
|
||||||
|
// FIXME: create a Diesel Model of the joined episode and podcast query instead
|
||||||
|
fn init(&self, episode: &EpisodeWidgetQuery, podcast: &PodcastCoverQuery) {
|
||||||
|
self.set_cover_image(podcast);
|
||||||
|
self.set_show_title(podcast);
|
||||||
|
self.set_episode_title(episode);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_episode_title(&self, episode: &EpisodeWidgetQuery) {
|
||||||
|
self.episode.set_text(episode.title());
|
||||||
|
self.episode.set_tooltip_text(episode.title());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_show_title(&self, show: &PodcastCoverQuery) {
|
||||||
|
self.show.set_text(show.title());
|
||||||
|
self.show.set_tooltip_text(show.title());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_cover_image(&self, show: &PodcastCoverQuery) {
|
||||||
|
set_image_from_path(&self.cover, show.id(), 34)
|
||||||
|
.map_err(|err| error!("Player Cover: {}", err))
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct PlayerTimes {
|
||||||
|
container: gtk::Box,
|
||||||
|
progressed: gtk::Label,
|
||||||
|
duration: gtk::Label,
|
||||||
|
separator: gtk::Label,
|
||||||
|
slider: gtk::Scale,
|
||||||
|
slider_update: Rc<SignalHandlerId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
struct Duration(ClockTime);
|
||||||
|
|
||||||
|
impl Deref for Duration {
|
||||||
|
type Target = ClockTime;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
struct Position(ClockTime);
|
||||||
|
|
||||||
|
impl Deref for Position {
|
||||||
|
type Target = ClockTime;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlayerTimes {
|
||||||
|
/// Update the duration `gtk::Label` and the max range of the `gtk::SclaeBar`.
|
||||||
|
pub 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);
|
||||||
|
self.slider.set_range(0.0, seconds);
|
||||||
|
self.slider.unblock_signal(&self.slider_update);
|
||||||
|
|
||||||
|
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) {
|
||||||
|
let seconds = position.seconds().map(|v| v as f64).unwrap_or(0.0);
|
||||||
|
|
||||||
|
self.slider.block_signal(&self.slider_update);
|
||||||
|
self.slider.set_value(seconds);
|
||||||
|
self.slider.unblock_signal(&self.slider_update);
|
||||||
|
|
||||||
|
self.progressed.set_text(&format_duration(seconds as u32));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_duration(seconds: u32) -> String {
|
||||||
|
let time = NaiveTime::from_num_seconds_from_midnight(seconds, 0);
|
||||||
|
|
||||||
|
if seconds >= 3600 {
|
||||||
|
time.format("%T").to_string()
|
||||||
|
} else {
|
||||||
|
time.format("%M:%S").to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct PlayerRate {
|
||||||
|
radio150: gtk::RadioButton,
|
||||||
|
radio125: gtk::RadioButton,
|
||||||
|
radio_normal: gtk::RadioButton,
|
||||||
|
popover: gtk::Popover,
|
||||||
|
btn: gtk::MenuButton,
|
||||||
|
label: gtk::Label,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct PlayerControls {
|
||||||
|
container: gtk::Box,
|
||||||
|
play: gtk::Button,
|
||||||
|
pause: gtk::Button,
|
||||||
|
forward: gtk::Button,
|
||||||
|
rewind: gtk::Button,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct PlayerWidget {
|
||||||
|
pub action_bar: gtk::ActionBar,
|
||||||
|
player: gst_player::Player,
|
||||||
|
controls: PlayerControls,
|
||||||
|
timer: PlayerTimes,
|
||||||
|
info: PlayerInfo,
|
||||||
|
rate: PlayerRate,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PlayerWidget {
|
||||||
|
fn default() -> Self {
|
||||||
|
let dispatcher = gst_player::PlayerGMainContextSignalDispatcher::new(None);
|
||||||
|
let player = gst_player::Player::new(
|
||||||
|
None,
|
||||||
|
// Use the gtk main thread
|
||||||
|
Some(&dispatcher.upcast::<gst_player::PlayerSignalDispatcher>()),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut config = player.get_config();
|
||||||
|
config.set_user_agent(USER_AGENT);
|
||||||
|
config.set_position_update_interval(250);
|
||||||
|
player.set_config(config).unwrap();
|
||||||
|
|
||||||
|
let builder = gtk::Builder::new_from_resource("/org/gnome/Hammond/gtk/player_toolbar.ui");
|
||||||
|
let action_bar = builder.get_object("action_bar").unwrap();
|
||||||
|
|
||||||
|
let buttons = builder.get_object("buttons").unwrap();
|
||||||
|
let play = builder.get_object("play_button").unwrap();
|
||||||
|
let pause = builder.get_object("pause_button").unwrap();
|
||||||
|
let forward = builder.get_object("ff_button").unwrap();
|
||||||
|
let rewind = builder.get_object("rewind_button").unwrap();
|
||||||
|
|
||||||
|
let controls = PlayerControls {
|
||||||
|
container: buttons,
|
||||||
|
play,
|
||||||
|
pause,
|
||||||
|
forward,
|
||||||
|
rewind,
|
||||||
|
};
|
||||||
|
|
||||||
|
let timer_container = builder.get_object("timer").unwrap();
|
||||||
|
let progressed = builder.get_object("progress_time_label").unwrap();
|
||||||
|
let duration = builder.get_object("total_duration_label").unwrap();
|
||||||
|
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 timer = PlayerTimes {
|
||||||
|
container: timer_container,
|
||||||
|
progressed,
|
||||||
|
duration,
|
||||||
|
separator,
|
||||||
|
slider,
|
||||||
|
slider_update,
|
||||||
|
};
|
||||||
|
|
||||||
|
let labels = builder.get_object("info").unwrap();
|
||||||
|
let show = builder.get_object("show_label").unwrap();
|
||||||
|
let episode = builder.get_object("episode_label").unwrap();
|
||||||
|
let cover = builder.get_object("show_cover").unwrap();
|
||||||
|
let info = PlayerInfo {
|
||||||
|
container: labels,
|
||||||
|
show,
|
||||||
|
episode,
|
||||||
|
cover,
|
||||||
|
};
|
||||||
|
|
||||||
|
let radio150 = builder.get_object("rate_1_50").unwrap();
|
||||||
|
let radio125 = builder.get_object("rate_1_25").unwrap();
|
||||||
|
let radio_normal = builder.get_object("normal_rate").unwrap();
|
||||||
|
let popover = builder.get_object("rate_popover").unwrap();
|
||||||
|
let btn = builder.get_object("rate_button").unwrap();
|
||||||
|
let label = builder.get_object("rate_label").unwrap();
|
||||||
|
let rate = PlayerRate {
|
||||||
|
radio150,
|
||||||
|
radio125,
|
||||||
|
radio_normal,
|
||||||
|
popover,
|
||||||
|
label,
|
||||||
|
btn,
|
||||||
|
};
|
||||||
|
|
||||||
|
PlayerWidget {
|
||||||
|
player,
|
||||||
|
action_bar,
|
||||||
|
controls,
|
||||||
|
timer,
|
||||||
|
info,
|
||||||
|
rate,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlayerWidget {
|
||||||
|
pub fn new(sender: &Sender<Action>) -> Rc<Self> {
|
||||||
|
let w = Rc::new(Self::default());
|
||||||
|
Self::init(&w, sender);
|
||||||
|
w
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(s: &Rc<Self>, sender: &Sender<Action>) {
|
||||||
|
Self::connect_control_buttons(s);
|
||||||
|
Self::connect_rate_buttons(s);
|
||||||
|
Self::connect_gst_signals(s, sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
|
/// Connect the `PlayerControls` buttons to the `PlayerExt` methods.
|
||||||
|
fn connect_control_buttons(s: &Rc<Self>) {
|
||||||
|
// Connect the play button to the gst Player.
|
||||||
|
s.controls.play.connect_clicked(clone!(s => move |_| s.play()));
|
||||||
|
|
||||||
|
// Connect the pause button to the gst Player.
|
||||||
|
s.controls.pause.connect_clicked(clone!(s => move |_| s.pause()));
|
||||||
|
|
||||||
|
// Connect the rewind button to the gst Player.
|
||||||
|
s.controls.rewind.connect_clicked(clone!(s => move |_| s.rewind()));
|
||||||
|
|
||||||
|
// Connect the fast-forward button to the gst Player.
|
||||||
|
s.controls.forward.connect_clicked(clone!(s => move |_| s.fast_forward()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
|
fn connect_gst_signals(s: &Rc<Self>, sender: &Sender<Action>) {
|
||||||
|
// Log gst warnings.
|
||||||
|
s.player.connect_warning(move |_, warn| warn!("gst warning: {}", warn));
|
||||||
|
|
||||||
|
// Log gst errors.
|
||||||
|
s.player.connect_error(clone!(sender => move |_, error| {
|
||||||
|
// FIXME: should never occur and should not be user facing.
|
||||||
|
sender.send(Action::ErrorNotification(format!("Player Error: {}", error)))
|
||||||
|
.map_err(|err| error!("Error: {}", err))
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
// The followign callbacks require `Send` but are handled by the gtk main loop
|
||||||
|
let s2 = SendCell::new(s.clone());
|
||||||
|
|
||||||
|
// Update the duration label and the slider
|
||||||
|
s.player.connect_duration_changed(clone!(s2 => move |_, clock| {
|
||||||
|
s2.borrow().timer.on_duration_changed(Duration(clock));
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Update the position label and the slider
|
||||||
|
s.player.connect_position_updated(clone!(s2 => move |_, clock| {
|
||||||
|
s2.borrow().timer.on_position_updated(Position(clock));
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Reset the slider to 0 and show a play button
|
||||||
|
s.player.connect_end_of_stream(clone!(s2 => move |_| s2.borrow().stop()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
|
fn connect_rate_buttons(s: &Rc<Self>) {
|
||||||
|
s.rate.radio_normal.connect_toggled(clone!(s => move |_| s.on_rate_changed(1.00)));
|
||||||
|
s.rate.radio125.connect_toggled(clone!(s => move |_| s.on_rate_changed(1.25)));
|
||||||
|
s.rate.radio150.connect_toggled(clone!(s => move |_| s.on_rate_changed(1.50)));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_rate_changed(&self, rate: f64) {
|
||||||
|
self.set_playback_rate(rate);
|
||||||
|
self.rate.label.set_text(&format!("{:.2}x", rate));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reveal(&self) {
|
||||||
|
self.action_bar.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub 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.podcast_id())?;
|
||||||
|
|
||||||
|
self.info.init(&ep, &pd);
|
||||||
|
// Currently that will always be the case since the play button is
|
||||||
|
// only shown if the file is downloaded
|
||||||
|
if let Some(ref path) = ep.local_uri() {
|
||||||
|
if Path::new(path).exists() {
|
||||||
|
// path is an absolute fs path ex. "foo/bar/baz".
|
||||||
|
// Convert it so it will have a "file:///"
|
||||||
|
// FIXME: convert it properly
|
||||||
|
if let Some(uri) = File::new_for_path(path).get_uri() {
|
||||||
|
// play the file
|
||||||
|
self.player.set_uri(&uri);
|
||||||
|
self.play();
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: log an error
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Stream stuff
|
||||||
|
// unimplemented!()
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn connect_update_slider(slider: >k::Scale, player: &gst_player::Player) -> SignalHandlerId {
|
||||||
|
slider.connect_value_changed(clone!(player => move |slider| {
|
||||||
|
let value = slider.get_value() as u64;
|
||||||
|
player.seek(ClockTime::from_seconds(value as u64));
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlayerExt for PlayerWidget {
|
||||||
|
fn play(&self) {
|
||||||
|
self.reveal();
|
||||||
|
|
||||||
|
self.controls.pause.show();
|
||||||
|
self.controls.play.hide();
|
||||||
|
|
||||||
|
self.player.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pause(&self) {
|
||||||
|
self.controls.pause.hide();
|
||||||
|
self.controls.play.show();
|
||||||
|
|
||||||
|
self.player.pause();
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
|
fn stop(&self) {
|
||||||
|
self.controls.pause.hide();
|
||||||
|
self.controls.play.show();
|
||||||
|
|
||||||
|
self.player.stop();
|
||||||
|
|
||||||
|
// Reset the slider bar to the start
|
||||||
|
self.timer.on_position_updated(Position(ClockTime::from_seconds(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adapted from https://github.com/philn/glide/blob/b52a65d99daeab0b487f79a0e1ccfad0cd433e22/src/player_context.rs#L219-L245
|
||||||
|
fn seek(&self, offset: ClockTime, direction: SeekDirection) {
|
||||||
|
let position = self.player.get_position();
|
||||||
|
if position.is_none() || offset.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let duration = self.player.get_duration();
|
||||||
|
let destination = match direction {
|
||||||
|
SeekDirection::Backwards if position >= offset => Some(position - offset),
|
||||||
|
SeekDirection::Forward if !duration.is_none() && position + offset <= duration => {
|
||||||
|
Some(position + offset)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
destination.map(|d| self.player.seek(d));
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: make the interval a GSetting
|
||||||
|
fn rewind(&self) {
|
||||||
|
self.seek(ClockTime::from_seconds(10), SeekDirection::Backwards)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: make the interval a GSetting
|
||||||
|
fn fast_forward(&self) {
|
||||||
|
self.seek(ClockTime::from_seconds(10), SeekDirection::Forward)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_playback_rate(&self, rate: f64) {
|
||||||
|
self.player.set_rate(rate);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -20,6 +20,7 @@
|
|||||||
"--share=ipc",
|
"--share=ipc",
|
||||||
"--socket=x11",
|
"--socket=x11",
|
||||||
"--socket=wayland",
|
"--socket=wayland",
|
||||||
|
"--socket=pulseaudio",
|
||||||
"--talk-name=org.freedesktop.Desktop"
|
"--talk-name=org.freedesktop.Desktop"
|
||||||
],
|
],
|
||||||
"build-options" : {
|
"build-options" : {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user