aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsalmonik <raisersoft@proton.me>2026-05-18 21:46:26 +0200
committersalmonik <raisersoft@proton.me>2026-05-18 21:46:26 +0200
commit6cceb3cbfacb6f4416e49321a9b63b4ce7a9f036 (patch)
tree9d883ada0e6cfb4fd533635885dfb4459f200128
parentfirst public release. (diff)
downloadrsplayer-6cceb3cbfacb6f4416e49321a9b63b4ce7a9f036.tar.gz
rsplayer-6cceb3cbfacb6f4416e49321a9b63b4ce7a9f036.zip
fix for new symphony API and less debug info in release
-rw-r--r--Cargo.lock311
-rw-r--r--Cargo.toml3
-rw-r--r--README.md66
-rw-r--r--src/audio.rs97
-rw-r--r--src/daemon.rs39
-rw-r--r--src/lib.rs7
-rw-r--r--src/main.rs1
-rw-r--r--src/queue.rs64
-rw-r--r--src/scrobbler.rs72
9 files changed, 306 insertions, 354 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 6d359ef..f2593f8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -9,7 +9,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
9checksum = "812947049edcd670a82cd5c73c3661d2e58468577ba8489de58e1a73c04cbd5d" 9checksum = "812947049edcd670a82cd5c73c3661d2e58468577ba8489de58e1a73c04cbd5d"
10dependencies = [ 10dependencies = [
11 "alsa-sys", 11 "alsa-sys",
12 "bitflags 2.11.1", 12 "bitflags",
13 "cfg-if", 13 "cfg-if",
14 "libc", 14 "libc",
15] 15]
@@ -90,12 +90,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
90checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" 90checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
91 91
92[[package]] 92[[package]]
93name = "arrayvec"
94version = "0.7.6"
95source = "registry+https://github.com/rust-lang/crates.io-index"
96checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
97
98[[package]]
99name = "async-broadcast" 93name = "async-broadcast"
100version = "0.7.2" 94version = "0.7.2"
101source = "registry+https://github.com/rust-lang/crates.io-index" 95source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -268,12 +262,6 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
268 262
269[[package]] 263[[package]]
270name = "bitflags" 264name = "bitflags"
271version = "1.3.2"
272source = "registry+https://github.com/rust-lang/crates.io-index"
273checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
274
275[[package]]
276name = "bitflags"
277version = "2.11.1" 265version = "2.11.1"
278source = "registry+https://github.com/rust-lang/crates.io-index" 266source = "registry+https://github.com/rust-lang/crates.io-index"
279checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" 267checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
@@ -502,7 +490,7 @@ version = "0.14.2"
502source = "registry+https://github.com/rust-lang/crates.io-index" 490source = "registry+https://github.com/rust-lang/crates.io-index"
503checksum = "7d5d7dca3ebcf65a035582c9ad4385371a9d9ee6537474d2a278f4e1e475bb58" 491checksum = "7d5d7dca3ebcf65a035582c9ad4385371a9d9ee6537474d2a278f4e1e475bb58"
504dependencies = [ 492dependencies = [
505 "bitflags 2.11.1", 493 "bitflags",
506 "libc", 494 "libc",
507 "objc2-audio-toolbox", 495 "objc2-audio-toolbox",
508 "objc2-core-audio", 496 "objc2-core-audio",
@@ -637,7 +625,7 @@ version = "0.3.1"
637source = "registry+https://github.com/rust-lang/crates.io-index" 625source = "registry+https://github.com/rust-lang/crates.io-index"
638checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" 626checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38"
639dependencies = [ 627dependencies = [
640 "bitflags 2.11.1", 628 "bitflags",
641 "block2", 629 "block2",
642 "libc", 630 "libc",
643 "objc2", 631 "objc2",
@@ -1388,15 +1376,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1388checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" 1376checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
1389 1377
1390[[package]] 1378[[package]]
1391name = "lock_api"
1392version = "0.4.14"
1393source = "registry+https://github.com/rust-lang/crates.io-index"
1394checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
1395dependencies = [
1396 "scopeguard",
1397]
1398
1399[[package]]
1400name = "log" 1379name = "log"
1401version = "0.4.29" 1380version = "0.4.29"
1402source = "registry+https://github.com/rust-lang/crates.io-index" 1381source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1477,7 +1456,7 @@ version = "0.9.0"
1477source = "registry+https://github.com/rust-lang/crates.io-index" 1456source = "registry+https://github.com/rust-lang/crates.io-index"
1478checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" 1457checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4"
1479dependencies = [ 1458dependencies = [
1480 "bitflags 2.11.1", 1459 "bitflags",
1481 "jni-sys 0.3.1", 1460 "jni-sys 0.3.1",
1482 "log", 1461 "log",
1483 "ndk-sys", 1462 "ndk-sys",
@@ -1506,7 +1485,7 @@ version = "0.31.2"
1506source = "registry+https://github.com/rust-lang/crates.io-index" 1485source = "registry+https://github.com/rust-lang/crates.io-index"
1507checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3" 1486checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3"
1508dependencies = [ 1487dependencies = [
1509 "bitflags 2.11.1", 1488 "bitflags",
1510 "cfg-if", 1489 "cfg-if",
1511 "cfg_aliases", 1490 "cfg_aliases",
1512 "libc", 1491 "libc",
@@ -1527,6 +1506,15 @@ dependencies = [
1527] 1506]
1528 1507
1529[[package]] 1508[[package]]
1509name = "num-complex"
1510version = "0.4.6"
1511source = "registry+https://github.com/rust-lang/crates.io-index"
1512checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
1513dependencies = [
1514 "num-traits",
1515]
1516
1517[[package]]
1530name = "num-conv" 1518name = "num-conv"
1531version = "0.2.1" 1519version = "0.2.1"
1532source = "registry+https://github.com/rust-lang/crates.io-index" 1520source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1544,6 +1532,15 @@ dependencies = [
1544] 1532]
1545 1533
1546[[package]] 1534[[package]]
1535name = "num-integer"
1536version = "0.1.46"
1537source = "registry+https://github.com/rust-lang/crates.io-index"
1538checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
1539dependencies = [
1540 "num-traits",
1541]
1542
1543[[package]]
1547name = "num-traits" 1544name = "num-traits"
1548version = "0.2.19" 1545version = "0.2.19"
1549source = "registry+https://github.com/rust-lang/crates.io-index" 1546source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1589,7 +1586,7 @@ version = "0.3.2"
1589source = "registry+https://github.com/rust-lang/crates.io-index" 1586source = "registry+https://github.com/rust-lang/crates.io-index"
1590checksum = "6948501a91121d6399b79abaa33a8aa4ea7857fe019f341b8c23ad6e81b79b08" 1587checksum = "6948501a91121d6399b79abaa33a8aa4ea7857fe019f341b8c23ad6e81b79b08"
1591dependencies = [ 1588dependencies = [
1592 "bitflags 2.11.1", 1589 "bitflags",
1593 "libc", 1590 "libc",
1594 "objc2", 1591 "objc2",
1595 "objc2-core-audio", 1592 "objc2-core-audio",
@@ -1627,7 +1624,7 @@ version = "0.3.2"
1627source = "registry+https://github.com/rust-lang/crates.io-index" 1624source = "registry+https://github.com/rust-lang/crates.io-index"
1628checksum = "5a89f2ec274a0cf4a32642b2991e8b351a404d290da87bb6a9a9d8632490bd1c" 1625checksum = "5a89f2ec274a0cf4a32642b2991e8b351a404d290da87bb6a9a9d8632490bd1c"
1629dependencies = [ 1626dependencies = [
1630 "bitflags 2.11.1", 1627 "bitflags",
1631 "objc2", 1628 "objc2",
1632] 1629]
1633 1630
@@ -1637,7 +1634,7 @@ version = "0.3.2"
1637source = "registry+https://github.com/rust-lang/crates.io-index" 1634source = "registry+https://github.com/rust-lang/crates.io-index"
1638checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" 1635checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
1639dependencies = [ 1636dependencies = [
1640 "bitflags 2.11.1", 1637 "bitflags",
1641 "block2", 1638 "block2",
1642 "dispatch2", 1639 "dispatch2",
1643 "libc", 1640 "libc",
@@ -1656,7 +1653,7 @@ version = "0.3.2"
1656source = "registry+https://github.com/rust-lang/crates.io-index" 1653source = "registry+https://github.com/rust-lang/crates.io-index"
1657checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" 1654checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
1658dependencies = [ 1655dependencies = [
1659 "bitflags 2.11.1", 1656 "bitflags",
1660 "block2", 1657 "block2",
1661 "libc", 1658 "libc",
1662 "objc2", 1659 "objc2",
@@ -1704,29 +1701,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1704checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" 1701checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
1705 1702
1706[[package]] 1703[[package]]
1707name = "parking_lot"
1708version = "0.12.5"
1709source = "registry+https://github.com/rust-lang/crates.io-index"
1710checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
1711dependencies = [
1712 "lock_api",
1713 "parking_lot_core",
1714]
1715
1716[[package]]
1717name = "parking_lot_core"
1718version = "0.9.12"
1719source = "registry+https://github.com/rust-lang/crates.io-index"
1720checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
1721dependencies = [
1722 "cfg-if",
1723 "libc",
1724 "redox_syscall",
1725 "smallvec",
1726 "windows-link 0.2.1",
1727]
1728
1729[[package]]
1730name = "percent-encoding" 1704name = "percent-encoding"
1731version = "2.3.2" 1705version = "2.3.2"
1732source = "registry+https://github.com/rust-lang/crates.io-index" 1706source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1804,6 +1778,15 @@ dependencies = [
1804] 1778]
1805 1779
1806[[package]] 1780[[package]]
1781name = "primal-check"
1782version = "0.3.4"
1783source = "registry+https://github.com/rust-lang/crates.io-index"
1784checksum = "dc0d895b311e3af9902528fbb8f928688abbd95872819320517cc24ca6b2bd08"
1785dependencies = [
1786 "num-integer",
1787]
1788
1789[[package]]
1807name = "proc-macro-crate" 1790name = "proc-macro-crate"
1808version = "3.5.0" 1791version = "3.5.0"
1809source = "registry+https://github.com/rust-lang/crates.io-index" 1792source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1954,15 +1937,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1954checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" 1937checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
1955 1938
1956[[package]] 1939[[package]]
1957name = "redox_syscall"
1958version = "0.5.18"
1959source = "registry+https://github.com/rust-lang/crates.io-index"
1960checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
1961dependencies = [
1962 "bitflags 2.11.1",
1963]
1964
1965[[package]]
1966name = "redox_users" 1940name = "redox_users"
1967version = "0.5.2" 1941version = "0.5.2"
1968source = "registry+https://github.com/rust-lang/crates.io-index" 1942source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1974,6 +1948,12 @@ dependencies = [
1974] 1948]
1975 1949
1976[[package]] 1950[[package]]
1951name = "regex-lite"
1952version = "0.1.9"
1953source = "registry+https://github.com/rust-lang/crates.io-index"
1954checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973"
1955
1956[[package]]
1977name = "reqwest" 1957name = "reqwest"
1978version = "0.13.3" 1958version = "0.13.3"
1979source = "registry+https://github.com/rust-lang/crates.io-index" 1959source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2053,7 +2033,6 @@ dependencies = [
2053 "symphonia", 2033 "symphonia",
2054 "tempfile", 2034 "tempfile",
2055 "terminal_size", 2035 "terminal_size",
2056 "tokio",
2057 "toml", 2036 "toml",
2058] 2037]
2059 2038
@@ -2073,7 +2052,7 @@ version = "0.39.0"
2073source = "registry+https://github.com/rust-lang/crates.io-index" 2052source = "registry+https://github.com/rust-lang/crates.io-index"
2074checksum = "a0d2b0146dd9661bf67bb107c0bb2a55064d556eeb3fc314151b957f313bcd4e" 2053checksum = "a0d2b0146dd9661bf67bb107c0bb2a55064d556eeb3fc314151b957f313bcd4e"
2075dependencies = [ 2054dependencies = [
2076 "bitflags 2.11.1", 2055 "bitflags",
2077 "fallible-iterator", 2056 "fallible-iterator",
2078 "fallible-streaming-iterator", 2057 "fallible-streaming-iterator",
2079 "hashlink", 2058 "hashlink",
@@ -2098,12 +2077,26 @@ dependencies = [
2098] 2077]
2099 2078
2100[[package]] 2079[[package]]
2080name = "rustfft"
2081version = "6.4.1"
2082source = "registry+https://github.com/rust-lang/crates.io-index"
2083checksum = "21db5f9893e91f41798c88680037dba611ca6674703c1a18601b01a72c8adb89"
2084dependencies = [
2085 "num-complex",
2086 "num-integer",
2087 "num-traits",
2088 "primal-check",
2089 "strength_reduce",
2090 "transpose",
2091]
2092
2093[[package]]
2101name = "rustix" 2094name = "rustix"
2102version = "1.1.4" 2095version = "1.1.4"
2103source = "registry+https://github.com/rust-lang/crates.io-index" 2096source = "registry+https://github.com/rust-lang/crates.io-index"
2104checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" 2097checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
2105dependencies = [ 2098dependencies = [
2106 "bitflags 2.11.1", 2099 "bitflags",
2107 "errno", 2100 "errno",
2108 "libc", 2101 "libc",
2109 "linux-raw-sys", 2102 "linux-raw-sys",
@@ -2216,18 +2209,12 @@ dependencies = [
2216] 2209]
2217 2210
2218[[package]] 2211[[package]]
2219name = "scopeguard"
2220version = "1.2.0"
2221source = "registry+https://github.com/rust-lang/crates.io-index"
2222checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
2223
2224[[package]]
2225name = "security-framework" 2212name = "security-framework"
2226version = "3.7.0" 2213version = "3.7.0"
2227source = "registry+https://github.com/rust-lang/crates.io-index" 2214source = "registry+https://github.com/rust-lang/crates.io-index"
2228checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" 2215checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
2229dependencies = [ 2216dependencies = [
2230 "bitflags 2.11.1", 2217 "bitflags",
2231 "core-foundation 0.10.1", 2218 "core-foundation 0.10.1",
2232 "core-foundation-sys", 2219 "core-foundation-sys",
2233 "libc", 2220 "libc",
@@ -2398,6 +2385,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
2398checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" 2385checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
2399 2386
2400[[package]] 2387[[package]]
2388name = "strength_reduce"
2389version = "0.2.4"
2390source = "registry+https://github.com/rust-lang/crates.io-index"
2391checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82"
2392
2393[[package]]
2401name = "strsim" 2394name = "strsim"
2402version = "0.11.1" 2395version = "0.11.1"
2403source = "registry+https://github.com/rust-lang/crates.io-index" 2396source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2411,22 +2404,15 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
2411 2404
2412[[package]] 2405[[package]]
2413name = "symphonia" 2406name = "symphonia"
2414version = "0.5.5" 2407version = "0.6.0"
2415source = "registry+https://github.com/rust-lang/crates.io-index" 2408source = "registry+https://github.com/rust-lang/crates.io-index"
2416checksum = "5773a4c030a19d9bfaa090f49746ff35c75dfddfa700df7a5939d5e076a57039" 2409checksum = "1758d6c853020a7244de03cc3e0185eaea3f58715122422dd3cc7452e6d4c16a"
2417dependencies = [ 2410dependencies = [
2418 "lazy_static", 2411 "lazy_static",
2419 "symphonia-bundle-flac", 2412 "symphonia-bundle-flac",
2420 "symphonia-bundle-mp3", 2413 "symphonia-bundle-mp3",
2421 "symphonia-codec-aac",
2422 "symphonia-codec-adpcm",
2423 "symphonia-codec-alac",
2424 "symphonia-codec-pcm",
2425 "symphonia-codec-vorbis", 2414 "symphonia-codec-vorbis",
2426 "symphonia-core", 2415 "symphonia-core",
2427 "symphonia-format-caf",
2428 "symphonia-format-isomp4",
2429 "symphonia-format-mkv",
2430 "symphonia-format-ogg", 2416 "symphonia-format-ogg",
2431 "symphonia-format-riff", 2417 "symphonia-format-riff",
2432 "symphonia-metadata", 2418 "symphonia-metadata",
@@ -2434,33 +2420,21 @@ dependencies = [
2434 2420
2435[[package]] 2421[[package]]
2436name = "symphonia-bundle-flac" 2422name = "symphonia-bundle-flac"
2437version = "0.5.5" 2423version = "0.6.0"
2438source = "registry+https://github.com/rust-lang/crates.io-index" 2424source = "registry+https://github.com/rust-lang/crates.io-index"
2439checksum = "c91565e180aea25d9b80a910c546802526ffd0072d0b8974e3ebe59b686c9976" 2425checksum = "ee69ad01236a67260b82fd1ff9790dd75ead29f2f46af145e63b7e72273e0e03"
2440dependencies = [ 2426dependencies = [
2441 "log", 2427 "log",
2428 "symphonia-common",
2442 "symphonia-core", 2429 "symphonia-core",
2443 "symphonia-metadata", 2430 "symphonia-metadata",
2444 "symphonia-utils-xiph",
2445] 2431]
2446 2432
2447[[package]] 2433[[package]]
2448name = "symphonia-bundle-mp3" 2434name = "symphonia-bundle-mp3"
2449version = "0.5.5" 2435version = "0.6.0"
2450source = "registry+https://github.com/rust-lang/crates.io-index"
2451checksum = "4872dd6bb56bf5eac799e3e957aa1981086c3e613b27e0ac23b176054f7c57ed"
2452dependencies = [
2453 "lazy_static",
2454 "log",
2455 "symphonia-core",
2456 "symphonia-metadata",
2457]
2458
2459[[package]]
2460name = "symphonia-codec-aac"
2461version = "0.5.5"
2462source = "registry+https://github.com/rust-lang/crates.io-index" 2436source = "registry+https://github.com/rust-lang/crates.io-index"
2463checksum = "4c263845aa86881416849c1729a54c7f55164f8b96111dba59de46849e73a790" 2437checksum = "350f1f2f2e19ad4dd315db94304d1eb361b29af070681f94e51b8fdaad769546"
2464dependencies = [ 2438dependencies = [
2465 "lazy_static", 2439 "lazy_static",
2466 "log", 2440 "log",
@@ -2468,113 +2442,59 @@ dependencies = [
2468] 2442]
2469 2443
2470[[package]] 2444[[package]]
2471name = "symphonia-codec-adpcm"
2472version = "0.5.5"
2473source = "registry+https://github.com/rust-lang/crates.io-index"
2474checksum = "2dddc50e2bbea4cfe027441eece77c46b9f319748605ab8f3443350129ddd07f"
2475dependencies = [
2476 "log",
2477 "symphonia-core",
2478]
2479
2480[[package]]
2481name = "symphonia-codec-alac"
2482version = "0.5.5"
2483source = "registry+https://github.com/rust-lang/crates.io-index"
2484checksum = "8413fa754942ac16a73634c9dfd1500ed5c61430956b33728567f667fdd393ab"
2485dependencies = [
2486 "log",
2487 "symphonia-core",
2488]
2489
2490[[package]]
2491name = "symphonia-codec-pcm"
2492version = "0.5.5"
2493source = "registry+https://github.com/rust-lang/crates.io-index"
2494checksum = "4e89d716c01541ad3ebe7c91ce4c8d38a7cf266a3f7b2f090b108fb0cb031d95"
2495dependencies = [
2496 "log",
2497 "symphonia-core",
2498]
2499
2500[[package]]
2501name = "symphonia-codec-vorbis" 2445name = "symphonia-codec-vorbis"
2502version = "0.5.5" 2446version = "0.6.0"
2503source = "registry+https://github.com/rust-lang/crates.io-index"
2504checksum = "f025837c309cd69ffef572750b4a2257b59552c5399a5e49707cc5b1b85d1c73"
2505dependencies = [
2506 "log",
2507 "symphonia-core",
2508 "symphonia-utils-xiph",
2509]
2510
2511[[package]]
2512name = "symphonia-core"
2513version = "0.5.5"
2514source = "registry+https://github.com/rust-lang/crates.io-index"
2515checksum = "ea00cc4f79b7f6bb7ff87eddc065a1066f3a43fe1875979056672c9ef948c2af"
2516dependencies = [
2517 "arrayvec",
2518 "bitflags 1.3.2",
2519 "bytemuck",
2520 "lazy_static",
2521 "log",
2522]
2523
2524[[package]]
2525name = "symphonia-format-caf"
2526version = "0.5.5"
2527source = "registry+https://github.com/rust-lang/crates.io-index" 2447source = "registry+https://github.com/rust-lang/crates.io-index"
2528checksum = "b8faf379316b6b6e6bbc274d00e7a592e0d63ff1a7e182ce8ba25e24edd3d096" 2448checksum = "45b07b4423cd8e0fc472575909a5554b12c2f58e3c190b38c24f042e732fd8de"
2529dependencies = [ 2449dependencies = [
2530 "log", 2450 "log",
2451 "symphonia-common",
2531 "symphonia-core", 2452 "symphonia-core",
2532 "symphonia-metadata",
2533] 2453]
2534 2454
2535[[package]] 2455[[package]]
2536name = "symphonia-format-isomp4" 2456name = "symphonia-common"
2537version = "0.5.5" 2457version = "0.6.0"
2538source = "registry+https://github.com/rust-lang/crates.io-index" 2458source = "registry+https://github.com/rust-lang/crates.io-index"
2539checksum = "243739585d11f81daf8dac8d9f3d18cc7898f6c09a259675fc364b382c30e0a5" 2459checksum = "8257891ffa7f05e02b58f4761e2abf7e5278c8744fd59e981559e050f86eef55"
2540dependencies = [ 2460dependencies = [
2541 "encoding_rs",
2542 "log", 2461 "log",
2543 "symphonia-core", 2462 "symphonia-core",
2544 "symphonia-metadata", 2463 "symphonia-metadata",
2545 "symphonia-utils-xiph",
2546] 2464]
2547 2465
2548[[package]] 2466[[package]]
2549name = "symphonia-format-mkv" 2467name = "symphonia-core"
2550version = "0.5.5" 2468version = "0.6.0"
2551source = "registry+https://github.com/rust-lang/crates.io-index" 2469source = "registry+https://github.com/rust-lang/crates.io-index"
2552checksum = "122d786d2c43a49beb6f397551b4a050d8229eaa54c7ddf9ee4b98899b8742d0" 2470checksum = "95ec293b5f288383b72a7bffcade6b2860b642cf66f28b3bd5967349a49938b1"
2553dependencies = [ 2471dependencies = [
2472 "bitflags",
2473 "bytemuck",
2554 "lazy_static", 2474 "lazy_static",
2555 "log", 2475 "log",
2556 "symphonia-core", 2476 "num-complex",
2557 "symphonia-metadata", 2477 "rustfft",
2558 "symphonia-utils-xiph", 2478 "smallvec",
2559] 2479]
2560 2480
2561[[package]] 2481[[package]]
2562name = "symphonia-format-ogg" 2482name = "symphonia-format-ogg"
2563version = "0.5.5" 2483version = "0.6.0"
2564source = "registry+https://github.com/rust-lang/crates.io-index" 2484source = "registry+https://github.com/rust-lang/crates.io-index"
2565checksum = "2b4955c67c1ed3aa8ae8428d04ca8397fbef6a19b2b051e73b5da8b1435639cb" 2485checksum = "b05a67e02b1e4fca1a261ba4fe06910a9357489ad8c36aafdd2960e9c6559433"
2566dependencies = [ 2486dependencies = [
2567 "log", 2487 "log",
2488 "symphonia-common",
2568 "symphonia-core", 2489 "symphonia-core",
2569 "symphonia-metadata", 2490 "symphonia-metadata",
2570 "symphonia-utils-xiph",
2571] 2491]
2572 2492
2573[[package]] 2493[[package]]
2574name = "symphonia-format-riff" 2494name = "symphonia-format-riff"
2575version = "0.5.5" 2495version = "0.6.0"
2576source = "registry+https://github.com/rust-lang/crates.io-index" 2496source = "registry+https://github.com/rust-lang/crates.io-index"
2577checksum = "c2d7c3df0e7d94efb68401d81906eae73c02b40d5ec1a141962c592d0f11a96f" 2497checksum = "17424452a777666d3eaf09a5c651029b15b6a333812fcc5b5474f2a3f0cff3f0"
2578dependencies = [ 2498dependencies = [
2579 "extended", 2499 "extended",
2580 "log", 2500 "log",
@@ -2584,27 +2504,18 @@ dependencies = [
2584 2504
2585[[package]] 2505[[package]]
2586name = "symphonia-metadata" 2506name = "symphonia-metadata"
2587version = "0.5.5" 2507version = "0.6.0"
2588source = "registry+https://github.com/rust-lang/crates.io-index" 2508source = "registry+https://github.com/rust-lang/crates.io-index"
2589checksum = "36306ff42b9ffe6e5afc99d49e121e0bd62fe79b9db7b9681d48e29fa19e6b16" 2509checksum = "a31acf5cd623398a6208e2225d18f4b20f761c55098a796a5247ad516a4a8681"
2590dependencies = [ 2510dependencies = [
2591 "encoding_rs",
2592 "lazy_static", 2511 "lazy_static",
2593 "log", 2512 "log",
2513 "regex-lite",
2514 "smallvec",
2594 "symphonia-core", 2515 "symphonia-core",
2595] 2516]
2596 2517
2597[[package]] 2518[[package]]
2598name = "symphonia-utils-xiph"
2599version = "0.5.5"
2600source = "registry+https://github.com/rust-lang/crates.io-index"
2601checksum = "ee27c85ab799a338446b68eec77abf42e1a6f1bb490656e121c6e27bfbab9f16"
2602dependencies = [
2603 "symphonia-core",
2604 "symphonia-metadata",
2605]
2606
2607[[package]]
2608name = "syn" 2519name = "syn"
2609version = "2.0.117" 2520version = "2.0.117"
2610source = "registry+https://github.com/rust-lang/crates.io-index" 2521source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2641,7 +2552,7 @@ version = "0.7.0"
2641source = "registry+https://github.com/rust-lang/crates.io-index" 2552source = "registry+https://github.com/rust-lang/crates.io-index"
2642checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" 2553checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b"
2643dependencies = [ 2554dependencies = [
2644 "bitflags 2.11.1", 2555 "bitflags",
2645 "core-foundation 0.9.4", 2556 "core-foundation 0.9.4",
2646 "system-configuration-sys", 2557 "system-configuration-sys",
2647] 2558]
@@ -2784,26 +2695,12 @@ dependencies = [
2784 "bytes", 2695 "bytes",
2785 "libc", 2696 "libc",
2786 "mio", 2697 "mio",
2787 "parking_lot",
2788 "pin-project-lite", 2698 "pin-project-lite",
2789 "signal-hook-registry",
2790 "socket2", 2699 "socket2",
2791 "tokio-macros",
2792 "windows-sys 0.61.2", 2700 "windows-sys 0.61.2",
2793] 2701]
2794 2702
2795[[package]] 2703[[package]]
2796name = "tokio-macros"
2797version = "2.7.0"
2798source = "registry+https://github.com/rust-lang/crates.io-index"
2799checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
2800dependencies = [
2801 "proc-macro2",
2802 "quote",
2803 "syn",
2804]
2805
2806[[package]]
2807name = "tokio-rustls" 2704name = "tokio-rustls"
2808version = "0.26.4" 2705version = "0.26.4"
2809source = "registry+https://github.com/rust-lang/crates.io-index" 2706source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2898,7 +2795,7 @@ version = "0.6.9"
2898source = "registry+https://github.com/rust-lang/crates.io-index" 2795source = "registry+https://github.com/rust-lang/crates.io-index"
2899checksum = "a28f0d049ccfaa566e14e9663d304d8577427b368cb4710a20528690287a738b" 2796checksum = "a28f0d049ccfaa566e14e9663d304d8577427b368cb4710a20528690287a738b"
2900dependencies = [ 2797dependencies = [
2901 "bitflags 2.11.1", 2798 "bitflags",
2902 "bytes", 2799 "bytes",
2903 "futures-util", 2800 "futures-util",
2904 "http", 2801 "http",
@@ -2954,6 +2851,16 @@ dependencies = [
2954] 2851]
2955 2852
2956[[package]] 2853[[package]]
2854name = "transpose"
2855version = "0.2.3"
2856source = "registry+https://github.com/rust-lang/crates.io-index"
2857checksum = "1ad61aed86bc3faea4300c7aee358b4c6d0c8d6ccc36524c96e4c92ccf26e77e"
2858dependencies = [
2859 "num-integer",
2860 "strength_reduce",
2861]
2862
2863[[package]]
2957name = "try-lock" 2864name = "try-lock"
2958version = "0.2.5" 2865version = "0.2.5"
2959source = "registry+https://github.com/rust-lang/crates.io-index" 2866source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3161,7 +3068,7 @@ version = "0.244.0"
3161source = "registry+https://github.com/rust-lang/crates.io-index" 3068source = "registry+https://github.com/rust-lang/crates.io-index"
3162checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" 3069checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
3163dependencies = [ 3070dependencies = [
3164 "bitflags 2.11.1", 3071 "bitflags",
3165 "hashbrown 0.15.5", 3072 "hashbrown 0.15.5",
3166 "indexmap", 3073 "indexmap",
3167 "semver", 3074 "semver",
@@ -3719,7 +3626,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
3719checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" 3626checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
3720dependencies = [ 3627dependencies = [
3721 "anyhow", 3628 "anyhow",
3722 "bitflags 2.11.1", 3629 "bitflags",
3723 "indexmap", 3630 "indexmap",
3724 "log", 3631 "log",
3725 "serde", 3632 "serde",
diff --git a/Cargo.toml b/Cargo.toml
index 0393d3b..86a9b54 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,9 +22,8 @@ rusqlite = { version = "0.39", features = ["bundled"] }
22directories = "6.0" 22directories = "6.0"
23serde = { version = "1.0.228", features = ["derive"] } 23serde = { version = "1.0.228", features = ["derive"] }
24toml = "1.1.2" 24toml = "1.1.2"
25tokio = { version = "1.52.2", features = ["full"] }
26reqwest = { version = "0.13.3", features = ["json", "form", "blocking"] } 25reqwest = { version = "0.13.3", features = ["json", "form", "blocking"] }
27symphonia = { version = "0.5.5", features = ["all"] } 26symphonia = { version = "0.6", default-features = false, features = ["mp3", "flac", "vorbis", "wav", "ogg", "opt-simd"] }
28cpal = "0.17.3" 27cpal = "0.17.3"
29chrono = "0.4.44" 28chrono = "0.4.44"
30serde_json = "1.0.149" 29serde_json = "1.0.149"
diff --git a/README.md b/README.md
index 2092f2a..7826c41 100644
--- a/README.md
+++ b/README.md
@@ -7,47 +7,47 @@ cd RSPlayer
7./install.sh 7./install.sh
8``` 8```
9 9
10install.sh will: 10install.sh will:
11compile RSP (make sure you have rust properly installed!) 11- compile RSP (make sure you have rust properly installed!)
12install shell autocompletions (zsh, bash and fish supported) 12- install shell autocompletions (zsh, bash and fish supported)
13install user systemd service and activate it 13- install user systemd service and activate it
14 14
15## Usage 15## Usage
16 16
17configuration is generated at startup in $XDG_CONFIG_HOME/rsplayer/config.toml 17configuration is generated at startup in $XDG_CONFIG_HOME/rsplayer/config.toml
18most important values there are: 18- most important values there are:
19listenbrainz_token (last.fm scrobbling not yet tested) 19- listenbrainz_token (last.fm scrobbling not yet tested)
20display_format (supports $ARTIST $ALBUM and $TRACK) 20- display_format (supports $ARTIST $ALBUM and $TRACK)
21music_paths (for DB usage, still WIP) 21- music_paths (for DB usage, still WIP)
22scan_interval (same as above) 22- scan_interval (same as above)
23 23
24all other values can be changed in runtime using commands 24all other values can be changed in runtime using commands
25 25
26as for commands: 26as for commands:
27RSP will be installed as rsp, in this fragment, rsp will be ommited 27RSP will be installed as rsp, in this fragment, rsp will be ommited
28 28
29these commands should only be used in testing or by systemd service: 29these commands should only be used in testing or by systemd service:
30`start-daemon` Start the daemon 30- `start-daemon` Start the daemon
31`stop-daemon` Stop the daemon 31- `stop-daemon` Stop the daemon
32`restart` Restart the daemon (kill existing and start new) 32- `restart` Restart the daemon (kill existing and start new)
33 33
34these are normal commands: 34these are normal commands:
35`play` Play a track, album, or artist 35- `play` Play a track, album, or artist
36`pause` Pause playback 36- `pause` Pause playback
37`resume` Resume playback 37- `resume` Resume playback
38`toggle` Toggle play/pause 38- `toggle` Toggle play/pause
39`stop` Stop playback 39- `stop` Stop playback
40`next` Skip to next track 40- `next` Skip to next track
41`previous` Skip to previous track 41- `previous` Skip to previous track
42`volume` Set volume (0-100) 42- `volume` Set volume (0-100)
43`shuffle` Shuffle mode 43- `shuffle` Shuffle mode
44`queue` Queue management 44- `queue` Queue management
45`status` Show player status 45- `status` Show player status
46`scan` Scan music paths to populate database 46- `scan` Scan music paths to populate database
47`settings` Settings management 47- `settings` Settings management
48`help` Print this message or the help of the given subcommand(s) 48- `help` Print this message or the help of the given subcommand(s)
49 49
50 50
51## Technical stuff 51## Technical stuff
52The main culprit of this player is lack of integration with programs such as playerctl 52The main culprit of this player is lack of integration with programs such as playerctl
53this is due to the daemon not being asynchronous 53this is due to the daemon not being asynchronous
diff --git a/src/audio.rs b/src/audio.rs
index 62afd93..62d13ff 100644
--- a/src/audio.rs
+++ b/src/audio.rs
@@ -101,7 +101,7 @@ impl AudioEngine {
101 101
102struct AudioData { 102struct AudioData {
103 format: Box<dyn symphonia::core::formats::FormatReader>, 103 format: Box<dyn symphonia::core::formats::FormatReader>,
104 decoder: Box<dyn symphonia::core::codecs::Decoder>, 104 decoder: Box<dyn symphonia::core::codecs::audio::AudioDecoder>,
105 sample_rate: u32, 105 sample_rate: u32,
106 channels: u16, 106 channels: u16,
107 sample_buffer: Vec<f32>, 107 sample_buffer: Vec<f32>,
@@ -165,10 +165,10 @@ fn audio_thread(
165 165
166 let device_sample_rate = config.sample_rate(); 166 let device_sample_rate = config.sample_rate();
167 let channels = config.channels(); 167 let channels = config.channels();
168 eprintln!( 168 crate::debug_eprintln(format_args!(
169 "Default output: {} Hz, {} channels", 169 "Default output: {} Hz, {} channels",
170 device_sample_rate, channels 170 device_sample_rate, channels
171 ); 171 ));
172 172
173 let mut current_stream: Option<cpal::Stream> = None; 173 let mut current_stream: Option<cpal::Stream> = None;
174 let audio_data: Arc<Mutex<Option<AudioData>>> = Arc::new(Mutex::new(None)); 174 let audio_data: Arc<Mutex<Option<AudioData>>> = Arc::new(Mutex::new(None));
@@ -186,13 +186,16 @@ fn audio_thread(
186 186
187 match open_audio_file(&path) { 187 match open_audio_file(&path) {
188 Ok(mut data) => { 188 Ok(mut data) => {
189 eprintln!("File: {} Hz, {} channels", data.sample_rate, data.channels); 189 crate::debug_eprintln(format_args!(
190 "File: {} Hz, {} channels",
191 data.sample_rate, data.channels
192 ));
190 193
191 if data.sample_rate != device_sample_rate { 194 if data.sample_rate != device_sample_rate {
192 eprintln!( 195 crate::debug_eprintln(format_args!(
193 "Resampling from {} Hz to {} Hz", 196 "Resampling from {} Hz to {} Hz",
194 data.sample_rate, device_sample_rate 197 data.sample_rate, device_sample_rate
195 ); 198 ));
196 data.resample_ratio = 199 data.resample_ratio =
197 Some(device_sample_rate as f64 / data.sample_rate as f64); 200 Some(device_sample_rate as f64 / data.sample_rate as f64);
198 } 201 }
@@ -201,28 +204,23 @@ fn audio_thread(
201 let channels = data.channels as usize; 204 let channels = data.channels as usize;
202 for _ in 0..100 { 205 for _ in 0..100 {
203 match data.format.next_packet() { 206 match data.format.next_packet() {
204 Ok(packet) => match data.decoder.decode(&packet) { 207 Ok(Some(packet)) => match data.decoder.decode(&packet) {
205 Ok(decoded) => { 208 Ok(decoded) => {
206 let spec = *decoded.spec(); 209 let num_samples = decoded.samples_interleaved();
207 let mut sample_buf = symphonia::core::audio::SampleBuffer::< 210 let mut samples = vec![0.0f32; num_samples];
208 f32, 211 decoded.copy_to_slice_interleaved(&mut samples);
209 >::new(
210 decoded.capacity() as u64, spec
211 );
212 sample_buf.copy_interleaved_ref(decoded);
213 let samples = sample_buf.samples();
214 212
215 let resampled = if let Some(ratio) = data.resample_ratio { 213 let resampled = if let Some(ratio) = data.resample_ratio {
216 resample_linear(samples, ratio, channels) 214 resample_linear(&samples, ratio, channels)
217 } else { 215 } else {
218 samples.to_vec() 216 samples
219 }; 217 };
220 218
221 data.sample_buffer.extend(resampled); 219 data.sample_buffer.extend(resampled);
222 } 220 }
223 Err(_) => break, 221 Err(_) => break,
224 }, 222 },
225 Err(_) => break, 223 _ => break,
226 } 224 }
227 } 225 }
228 data.buffer_pos = 0; 226 data.buffer_pos = 0;
@@ -274,29 +272,26 @@ fn audio_thread(
274 272
275 // Decode more data 273 // Decode more data
276 match state.format.next_packet() { 274 match state.format.next_packet() {
277 Ok(packet) => { 275 Ok(Some(packet)) => {
278 match state.decoder.decode(&packet) { 276 match state.decoder.decode(&packet) {
279 Ok(decoded) => { 277 Ok(decoded) => {
280 let spec = *decoded.spec(); 278 let num_samples =
281 let mut sample_buf = 279 decoded.samples_interleaved();
282 symphonia::core::audio::SampleBuffer::< 280 let mut samples =
283 f32, 281 vec![0.0f32; num_samples];
284 >::new( 282 decoded.copy_to_slice_interleaved(
285 decoded.capacity() as u64, spec 283 &mut samples,
286 ); 284 );
287 sample_buf
288 .copy_interleaved_ref(decoded);
289 let samples = sample_buf.samples();
290 285
291 // Resample if needed 286 // Resample if needed
292 let resampled = if let Some(ratio) = 287 let resampled = if let Some(ratio) =
293 state.resample_ratio 288 state.resample_ratio
294 { 289 {
295 resample_linear( 290 resample_linear(
296 samples, ratio, channels, 291 &samples, ratio, channels,
297 ) 292 )
298 } else { 293 } else {
299 samples.to_vec() 294 samples
300 }; 295 };
301 296
302 // Copy what we can to output 297 // Copy what we can to output
@@ -323,7 +318,7 @@ fn audio_thread(
323 Err(_) => break, 318 Err(_) => break,
324 } 319 }
325 } 320 }
326 Err(_) => break, 321 _ => break,
327 } 322 }
328 } 323 }
329 324
@@ -419,36 +414,50 @@ fn audio_thread(
419} 414}
420 415
421fn open_audio_file(path: &PathBuf) -> Result<AudioData, Box<dyn std::error::Error>> { 416fn open_audio_file(path: &PathBuf) -> Result<AudioData, Box<dyn std::error::Error>> {
422 use symphonia::core::audio::Channels; 417 use symphonia::core::audio::layouts::CHANNEL_LAYOUT_STEREO;
418 use symphonia::core::codecs::audio::AudioDecoderOptions;
419 use symphonia::core::formats::probe::Hint;
423 use symphonia::core::io::MediaSourceStream; 420 use symphonia::core::io::MediaSourceStream;
424 421
425 let file = std::fs::File::open(path)?; 422 let file = std::fs::File::open(path)?;
426 let mss = MediaSourceStream::new(Box::new(file), Default::default()); 423 let mss = MediaSourceStream::new(Box::new(file), Default::default());
427 424
428 let hint = symphonia::core::probe::Hint::new(); 425 let hint = Hint::new();
429 let probed = symphonia::default::get_probe().format( 426 let format = symphonia::default::get_probe().probe(
430 &hint, 427 &hint,
431 mss, 428 mss,
432 &Default::default(), 429 Default::default(),
433 &Default::default(), 430 Default::default(),
434 )?; 431 )?;
435 432
436 let format = probed.format;
437 let track = format 433 let track = format
438 .tracks() 434 .tracks()
439 .iter() 435 .iter()
440 .find(|t| t.codec_params.sample_rate.is_some()) 436 .find(|t| {
437 t.codec_params
438 .as_ref()
439 .and_then(|p| p.audio())
440 .and_then(|a| a.sample_rate)
441 .is_some()
442 })
441 .ok_or("No suitable track found")?; 443 .ok_or("No suitable track found")?;
442 444
443 let sample_rate = track.codec_params.sample_rate.unwrap_or(44100); 445 let codec_params = track
444 let channels = track
445 .codec_params 446 .codec_params
447 .as_ref()
448 .ok_or("No codec parameters")?
449 .audio()
450 .ok_or("Not an audio track")?;
451
452 let sample_rate = codec_params.sample_rate.unwrap_or(44100);
453 let channels = codec_params
446 .channels 454 .channels
447 .unwrap_or(Channels::FRONT_LEFT | Channels::FRONT_RIGHT) 455 .clone()
456 .unwrap_or(CHANNEL_LAYOUT_STEREO)
448 .count() as u16; 457 .count() as u16;
449 458
450 let decoder = 459 let decoder = symphonia::default::get_codecs()
451 symphonia::default::get_codecs().make(&track.codec_params, &Default::default())?; 460 .make_audio_decoder(codec_params, &AudioDecoderOptions::default())?;
452 461
453 Ok(AudioData { 462 Ok(AudioData {
454 format, 463 format,
diff --git a/src/daemon.rs b/src/daemon.rs
index 59fc3d8..8b74357 100644
--- a/src/daemon.rs
+++ b/src/daemon.rs
@@ -33,10 +33,10 @@ impl ScrobbleState {
33 .as_secs(); 33 .as_secs();
34 let title = track.title.clone().unwrap_or_default(); 34 let title = track.title.clone().unwrap_or_default();
35 let artist = track.artist.clone().unwrap_or_default(); 35 let artist = track.artist.clone().unwrap_or_default();
36 eprintln!( 36 crate::debug_eprintln(format_args!(
37 "scrobble: tracking start for '{} - {}' (ts={})", 37 "scrobble: tracking start for '{} - {}' (ts={})",
38 artist, title, ts 38 artist, title, ts
39 ); 39 ));
40 self.track = Some(track); 40 self.track = Some(track);
41 self.start_time = Some(Instant::now()); 41 self.start_time = Some(Instant::now());
42 self.timestamp = Some(ts); 42 self.timestamp = Some(ts);
@@ -44,7 +44,9 @@ impl ScrobbleState {
44 44
45 fn should_scrobble(&self) -> bool { 45 fn should_scrobble(&self) -> bool {
46 let (Some(track), Some(start)) = (&self.track, self.start_time) else { 46 let (Some(track), Some(start)) = (&self.track, self.start_time) else {
47 eprintln!("scrobble: should_scrobble false — no track or start_time"); 47 crate::debug_eprintln(format_args!(
48 "scrobble: should_scrobble false — no track or start_time"
49 ));
48 return false; 50 return false;
49 }; 51 };
50 let elapsed = start.elapsed().as_secs(); 52 let elapsed = start.elapsed().as_secs();
@@ -54,10 +56,10 @@ impl ScrobbleState {
54 .map(|d| (d / 2.0) as u64) 56 .map(|d| (d / 2.0) as u64)
55 .unwrap_or(u64::MAX) 57 .unwrap_or(u64::MAX)
56 .min(240); 58 .min(240);
57 eprintln!( 59 crate::debug_eprintln(format_args!(
58 "scrobble: should_scrobble {} (elapsed={}s threshold={}s duration={:?})", 60 "scrobble: should_scrobble {} (elapsed={}s threshold={}s duration={:?})",
59 result, elapsed, threshold, track.duration_secs 61 result, elapsed, threshold, track.duration_secs
60 ); 62 ));
61 result 63 result
62 } 64 }
63 65
@@ -158,10 +160,10 @@ fn try_scrobble_current(scrobbler: &RwLock<Scrobbler>, state: &mut ScrobbleState
158 let artist = track.artist.as_deref().unwrap_or("(unknown artist)"); 160 let artist = track.artist.as_deref().unwrap_or("(unknown artist)");
159 let title = track.title.as_deref().unwrap_or("(unknown title)"); 161 let title = track.title.as_deref().unwrap_or("(unknown title)");
160 let album = track.album.as_deref(); 162 let album = track.album.as_deref();
161 eprintln!( 163 crate::debug_eprintln(format_args!(
162 "scrobble: sending scrobble for '{} - {}' at ts={}", 164 "scrobble: sending scrobble for '{} - {}' at ts={}",
163 artist, title, ts 165 artist, title, ts
164 ); 166 ));
165 if let Ok(s) = scrobbler.read() { 167 if let Ok(s) = scrobbler.read() {
166 s.scrobble(artist, title, album, ts); 168 s.scrobble(artist, title, album, ts);
167 } 169 }
@@ -171,10 +173,10 @@ fn try_scrobble_current(scrobbler: &RwLock<Scrobbler>, state: &mut ScrobbleState
171 .as_ref() 173 .as_ref()
172 .and_then(|t| t.title.as_deref()) 174 .and_then(|t| t.title.as_deref())
173 .unwrap_or("(no track)"); 175 .unwrap_or("(no track)");
174 eprintln!( 176 crate::debug_eprintln(format_args!(
175 "scrobble: skipping scrobble for '{}' — not enough play time", 177 "scrobble: skipping scrobble for '{}' — not enough play time",
176 title 178 title
177 ); 179 ));
178 } 180 }
179 state.clear(); 181 state.clear();
180} 182}
@@ -183,7 +185,10 @@ fn send_now_playing(scrobbler: &RwLock<Scrobbler>, track: &Track) {
183 let artist = track.artist.as_deref().unwrap_or("(unknown artist)"); 185 let artist = track.artist.as_deref().unwrap_or("(unknown artist)");
184 let title = track.title.as_deref().unwrap_or("(unknown title)"); 186 let title = track.title.as_deref().unwrap_or("(unknown title)");
185 let album = track.album.as_deref(); 187 let album = track.album.as_deref();
186 eprintln!("scrobble: sending now_playing for '{} - {}'", artist, title); 188 crate::debug_eprintln(format_args!(
189 "scrobble: sending now_playing for '{} - {}'",
190 artist, title
191 ));
187 if let Ok(s) = scrobbler.read() { 192 if let Ok(s) = scrobbler.read() {
188 s.now_playing(artist, title, album); 193 s.now_playing(artist, title, album);
189 } 194 }
@@ -260,9 +265,17 @@ pub fn start_daemon() -> Result<(), Box<dyn std::error::Error>> {
260 && let Ok(s) = scrobbler.read() 265 && let Ok(s) = scrobbler.read()
261 { 266 {
262 match s.validate_listenbrainz_token() { 267 match s.validate_listenbrainz_token() {
263 Ok(Some(user)) => eprintln!("scrobble: listenbrainz token valid (user: {})", user), 268 Ok(Some(user)) => crate::debug_eprintln(format_args!(
264 Ok(None) => eprintln!("scrobble: listenbrainz token invalid, scrobbling disabled"), 269 "scrobble: listenbrainz token valid (user: {})",
265 Err(e) => eprintln!("scrobble: listenbrainz validation error: {}", e), 270 user
271 )),
272 Ok(None) => crate::debug_eprintln(format_args!(
273 "scrobble: listenbrainz token invalid, scrobbling disabled"
274 )),
275 Err(e) => crate::debug_eprintln(format_args!(
276 "scrobble: listenbrainz validation error: {}",
277 e
278 )),
266 } 279 }
267 } 280 }
268 let scrobble_state = Arc::new(Mutex::new(ScrobbleState::new())); 281 let scrobble_state = Arc::new(Mutex::new(ScrobbleState::new()));
diff --git a/src/lib.rs b/src/lib.rs
index 825872f..4cdbb9b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,3 +1,10 @@
1#[inline(always)]
2pub fn debug_eprintln(args: core::fmt::Arguments<'_>) {
3 if cfg!(debug_assertions) {
4 eprintln!("{}", args);
5 }
6}
7
1pub mod audio; 8pub mod audio;
2pub mod cli; 9pub mod cli;
3pub mod config; 10pub mod config;
diff --git a/src/main.rs b/src/main.rs
index c1e42d0..fe450dd 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -10,6 +10,7 @@ mod scrobbler;
10 10
11use clap::Parser; 11use clap::Parser;
12use cli::Cli; 12use cli::Cli;
13pub use rsp::debug_eprintln;
13 14
14fn main() { 15fn main() {
15 let cli = Cli::parse(); 16 let cli = Cli::parse();
diff --git a/src/queue.rs b/src/queue.rs
index 26aeb8b..b0acf5f 100644
--- a/src/queue.rs
+++ b/src/queue.rs
@@ -17,28 +17,27 @@ pub struct AudioMetadata {
17} 17}
18 18
19pub fn extract_metadata(path: &Path) -> Option<AudioMetadata> { 19pub fn extract_metadata(path: &Path) -> Option<AudioMetadata> {
20 use symphonia::core::formats::probe::Hint;
20 use symphonia::core::io::MediaSourceStream; 21 use symphonia::core::io::MediaSourceStream;
21 use symphonia::core::meta::StandardTagKey; 22 use symphonia::core::meta::StandardTag;
22 use symphonia::core::probe::Hint;
23 23
24 let file = std::fs::File::open(path).ok()?; 24 let file = std::fs::File::open(path).ok()?;
25 let mss = MediaSourceStream::new(Box::new(file), Default::default()); 25 let mss = MediaSourceStream::new(Box::new(file), Default::default());
26 26
27 let hint = Hint::new(); 27 let hint = Hint::new();
28 let mut probed = symphonia::default::get_probe() 28 let mut probed = symphonia::default::get_probe()
29 .format(&hint, mss, &Default::default(), &Default::default()) 29 .probe(&hint, mss, Default::default(), Default::default())
30 .ok()?; 30 .ok()?;
31 31
32 // Extract duration from format track's codec parameters 32 // Extract duration from track's num_frames and time_base
33 let duration_secs = probed 33 let duration_secs = probed
34 .format
35 .tracks() 34 .tracks()
36 .iter() 35 .iter()
37 .find(|t| t.codec_params.n_frames.is_some() && t.codec_params.time_base.is_some()) 36 .find(|t| t.num_frames.is_some() && t.time_base.is_some())
38 .and_then(|t| { 37 .and_then(|t| {
39 let n_frames = t.codec_params.n_frames?; 38 let n_frames = t.num_frames?;
40 let tb = t.codec_params.time_base?; 39 let tb = t.time_base?;
41 Some(n_frames as f64 * tb.numer as f64 / tb.denom as f64) 40 Some(n_frames as f64 * tb.numer.get() as f64 / tb.denom.get() as f64)
42 }); 41 });
43 42
44 let mut metadata = AudioMetadata { 43 let mut metadata = AudioMetadata {
@@ -46,38 +45,39 @@ pub fn extract_metadata(path: &Path) -> Option<AudioMetadata> {
46 ..Default::default() 45 ..Default::default()
47 }; 46 };
48 47
49 if let Some(meta) = probed.metadata.get()
50 && let Some(rev) = meta.current()
51 { 48 {
52 for tag in rev.tags() { 49 let meta = probed.metadata();
53 match tag.std_key { 50 if let Some(rev) = meta.current() {
54 Some(StandardTagKey::TrackTitle) => { 51 for tag in &rev.media.tags {
55 metadata.title = Some(tag.value.to_string()); 52 match &tag.std {
56 } 53 Some(StandardTag::TrackTitle(value)) => {
57 Some(StandardTagKey::Artist) => { 54 metadata.title = Some(value.to_string());
58 metadata.artist = Some(tag.value.to_string()); 55 }
59 } 56 Some(StandardTag::Artist(value)) => {
60 Some(StandardTagKey::Album) => { 57 metadata.artist = Some(value.to_string());
61 metadata.album = Some(tag.value.to_string()); 58 }
59 Some(StandardTag::Album(value)) => {
60 metadata.album = Some(value.to_string());
61 }
62 _ => {}
62 } 63 }
63 _ => {}
64 } 64 }
65 } 65 }
66 } 66 }
67 67
68 if metadata.title.is_none() && metadata.artist.is_none() && metadata.album.is_none() { 68 if metadata.title.is_none() && metadata.artist.is_none() && metadata.album.is_none() {
69 let mut format = probed.format; 69 let meta = probed.metadata();
70 if let Some(rev) = format.metadata().current() { 70 if let Some(rev) = meta.current() {
71 for tag in rev.tags() { 71 for tag in &rev.media.tags {
72 match tag.std_key { 72 match &tag.std {
73 Some(StandardTagKey::TrackTitle) => { 73 Some(StandardTag::TrackTitle(value)) => {
74 metadata.title = Some(tag.value.to_string()); 74 metadata.title = Some(value.to_string());
75 } 75 }
76 Some(StandardTagKey::Artist) => { 76 Some(StandardTag::Artist(value)) => {
77 metadata.artist = Some(tag.value.to_string()); 77 metadata.artist = Some(value.to_string());
78 } 78 }
79 Some(StandardTagKey::Album) => { 79 Some(StandardTag::Album(value)) => {
80 metadata.album = Some(tag.value.to_string()); 80 metadata.album = Some(value.to_string());
81 } 81 }
82 _ => {} 82 _ => {}
83 } 83 }
diff --git a/src/scrobbler.rs b/src/scrobbler.rs
index 2e79933..93cb3c2 100644
--- a/src/scrobbler.rs
+++ b/src/scrobbler.rs
@@ -47,10 +47,10 @@ impl Scrobbler {
47 .send()?; 47 .send()?;
48 48
49 if !resp.status().is_success() { 49 if !resp.status().is_success() {
50 eprintln!( 50 crate::debug_eprintln(format_args!(
51 "scrobble: listenbrainz token validation failed: {}", 51 "scrobble: listenbrainz token validation failed: {}",
52 resp.status() 52 resp.status()
53 ); 53 ));
54 return Ok(None); 54 return Ok(None);
55 } 55 }
56 56
@@ -62,13 +62,18 @@ impl Scrobbler {
62 .and_then(|v| v.as_str()) 62 .and_then(|v| v.as_str())
63 .map(|s| s.to_string()); 63 .map(|s| s.to_string());
64 if let Some(ref u) = user { 64 if let Some(ref u) = user {
65 eprintln!("scrobble: listenbrainz token valid (user: {})", u); 65 crate::debug_eprintln(format_args!(
66 "scrobble: listenbrainz token valid (user: {})",
67 u
68 ));
66 } else { 69 } else {
67 eprintln!("scrobble: listenbrainz token valid"); 70 crate::debug_eprintln(format_args!("scrobble: listenbrainz token valid"));
68 } 71 }
69 Ok(user) 72 Ok(user)
70 } else { 73 } else {
71 eprintln!("scrobble: listenbrainz token invalid — scrobbling will fail"); 74 crate::debug_eprintln(format_args!(
75 "scrobble: listenbrainz token invalid — scrobbling will fail"
76 ));
72 Ok(None) 77 Ok(None)
73 } 78 }
74 } 79 }
@@ -82,22 +87,29 @@ impl Scrobbler {
82 && !secret.is_empty() 87 && !secret.is_empty()
83 && !session_key.is_empty() 88 && !session_key.is_empty()
84 { 89 {
85 eprintln!("scrobble: now_playing -> lastfm '{} - {}'", artist, track); 90 crate::debug_eprintln(format_args!(
91 "scrobble: now_playing -> lastfm '{} - {}'",
92 artist, track
93 ));
86 let _ = 94 let _ =
87 self.send_lastfm_now_playing(api_key, secret, session_key, artist, track, album); 95 self.send_lastfm_now_playing(api_key, secret, session_key, artist, track, album);
88 } else { 96 } else {
89 eprintln!("scrobble: now_playing -> lastfm skipped (no credentials)"); 97 crate::debug_eprintln(format_args!(
98 "scrobble: now_playing -> lastfm skipped (no credentials)"
99 ));
90 } 100 }
91 if let Some(token) = &self.listenbrainz_token 101 if let Some(token) = &self.listenbrainz_token
92 && !token.is_empty() 102 && !token.is_empty()
93 { 103 {
94 eprintln!( 104 crate::debug_eprintln(format_args!(
95 "scrobble: now_playing -> listenbrainz '{} - {}'", 105 "scrobble: now_playing -> listenbrainz '{} - {}'",
96 artist, track 106 artist, track
97 ); 107 ));
98 let _ = self.send_listenbrainz_now_playing(token, artist, track, album); 108 let _ = self.send_listenbrainz_now_playing(token, artist, track, album);
99 } else { 109 } else {
100 eprintln!("scrobble: now_playing -> listenbrainz skipped (no token)"); 110 crate::debug_eprintln(format_args!(
111 "scrobble: now_playing -> listenbrainz skipped (no token)"
112 ));
101 } 113 }
102 } 114 }
103 115
@@ -110,10 +122,10 @@ impl Scrobbler {
110 && !secret.is_empty() 122 && !secret.is_empty()
111 && !session_key.is_empty() 123 && !session_key.is_empty()
112 { 124 {
113 eprintln!( 125 crate::debug_eprintln(format_args!(
114 "scrobble: scrobble -> lastfm '{} - {}' (ts={})", 126 "scrobble: scrobble -> lastfm '{} - {}' (ts={})",
115 artist, track, timestamp 127 artist, track, timestamp
116 ); 128 ));
117 let _ = self.send_lastfm_scrobble( 129 let _ = self.send_lastfm_scrobble(
118 api_key, 130 api_key,
119 secret, 131 secret,
@@ -124,18 +136,22 @@ impl Scrobbler {
124 timestamp, 136 timestamp,
125 ); 137 );
126 } else { 138 } else {
127 eprintln!("scrobble: scrobble -> lastfm skipped (no credentials)"); 139 crate::debug_eprintln(format_args!(
140 "scrobble: scrobble -> lastfm skipped (no credentials)"
141 ));
128 } 142 }
129 if let Some(token) = &self.listenbrainz_token 143 if let Some(token) = &self.listenbrainz_token
130 && !token.is_empty() 144 && !token.is_empty()
131 { 145 {
132 eprintln!( 146 crate::debug_eprintln(format_args!(
133 "scrobble: scrobble -> listenbrainz '{} - {}' (ts={})", 147 "scrobble: scrobble -> listenbrainz '{} - {}' (ts={})",
134 artist, track, timestamp 148 artist, track, timestamp
135 ); 149 ));
136 let _ = self.send_listenbrainz_scrobble(token, artist, track, album, timestamp); 150 let _ = self.send_listenbrainz_scrobble(token, artist, track, album, timestamp);
137 } else { 151 } else {
138 eprintln!("scrobble: scrobble -> listenbrainz skipped (no token)"); 152 crate::debug_eprintln(format_args!(
153 "scrobble: scrobble -> listenbrainz skipped (no token)"
154 ));
139 } 155 }
140 } 156 }
141 157
@@ -177,13 +193,13 @@ impl Scrobbler {
177 193
178 let resp = self.client.post(LASTFM_API_URL).form(&params).send()?; 194 let resp = self.client.post(LASTFM_API_URL).form(&params).send()?;
179 if resp.status().is_success() { 195 if resp.status().is_success() {
180 eprintln!("scrobble: lastfm now_playing ok"); 196 crate::debug_eprintln(format_args!("scrobble: lastfm now_playing ok"));
181 } else { 197 } else {
182 eprintln!( 198 crate::debug_eprintln(format_args!(
183 "scrobble: lastfm now_playing error: {} {}", 199 "scrobble: lastfm now_playing error: {} {}",
184 resp.status(), 200 resp.status(),
185 resp.text().unwrap_or_default() 201 resp.text().unwrap_or_default()
186 ); 202 ));
187 } 203 }
188 Ok(()) 204 Ok(())
189 } 205 }
@@ -217,13 +233,13 @@ impl Scrobbler {
217 233
218 let resp = self.client.post(LASTFM_API_URL).form(&params).send()?; 234 let resp = self.client.post(LASTFM_API_URL).form(&params).send()?;
219 if resp.status().is_success() { 235 if resp.status().is_success() {
220 eprintln!("scrobble: lastfm scrobble ok"); 236 crate::debug_eprintln(format_args!("scrobble: lastfm scrobble ok"));
221 } else { 237 } else {
222 eprintln!( 238 crate::debug_eprintln(format_args!(
223 "scrobble: lastfm scrobble error: {} {}", 239 "scrobble: lastfm scrobble error: {} {}",
224 resp.status(), 240 resp.status(),
225 resp.text().unwrap_or_default() 241 resp.text().unwrap_or_default()
226 ); 242 ));
227 } 243 }
228 Ok(()) 244 Ok(())
229 } 245 }
@@ -257,13 +273,13 @@ impl Scrobbler {
257 .json(&payload) 273 .json(&payload)
258 .send()?; 274 .send()?;
259 if resp.status().is_success() { 275 if resp.status().is_success() {
260 eprintln!("scrobble: listenbrainz now_playing ok"); 276 crate::debug_eprintln(format_args!("scrobble: listenbrainz now_playing ok"));
261 } else { 277 } else {
262 eprintln!( 278 crate::debug_eprintln(format_args!(
263 "scrobble: listenbrainz now_playing error: {} {}", 279 "scrobble: listenbrainz now_playing error: {} {}",
264 resp.status(), 280 resp.status(),
265 resp.text().unwrap_or_default() 281 resp.text().unwrap_or_default()
266 ); 282 ));
267 } 283 }
268 Ok(()) 284 Ok(())
269 } 285 }
@@ -299,13 +315,13 @@ impl Scrobbler {
299 .json(&payload) 315 .json(&payload)
300 .send()?; 316 .send()?;
301 if resp.status().is_success() { 317 if resp.status().is_success() {
302 eprintln!("scrobble: listenbrainz scrobble ok"); 318 crate::debug_eprintln(format_args!("scrobble: listenbrainz scrobble ok"));
303 } else { 319 } else {
304 eprintln!( 320 crate::debug_eprintln(format_args!(
305 "scrobble: listenbrainz scrobble error: {} {}", 321 "scrobble: listenbrainz scrobble error: {} {}",
306 resp.status(), 322 resp.status(),
307 resp.text().unwrap_or_default() 323 resp.text().unwrap_or_default()
308 ); 324 ));
309 } 325 }
310 Ok(()) 326 Ok(())
311 } 327 }