Compare commits

...

170 Commits

Author SHA1 Message Date
warwickh 4fffcd143e
Merge pull request #47 from GioF71/feature/search-album
[Feature] Search Album
2023-02-03 08:25:49 +11:00
Giovanni Fulco e59b65d5e0
[Feature] Search Album 2023-02-02 19:43:09 +01:00
warwickh fa0c4ec165 Update repo link 2022-09-18 18:39:33 +10:00
warwickh 2c785729ee
Merge pull request #43 from BlackIkeEagle/collections-abc-import
also use collections.abc in simpleplugin to import MutableMapping
2022-03-05 16:12:22 +11:00
BlackEagle d11505bd04 also use collections.abc in simpleplugin to import MutableMapping
Signed-off-by: BlackEagle <ike.devolder@gmail.com>
2022-03-03 22:40:02 +01:00
warwickh d221fa4f39
Resolved incorrect repo link 2022-01-30 16:25:47 +11:00
warwickh ec86d2abdd Modify separator for verifypeer 2022-01-28 11:09:11 +11:00
warwickh b9a556eded Handle subfolder in server setting 2021-10-11 12:50:10 +11:00
warwickh 4e15a1d0d3
Update addon.xml for v3.0.2 2021-09-28 16:06:38 +10:00
warwickh 1295d078e5
Update readme for v3.0.2 2021-09-28 09:16:33 +10:00
warwickh 4b1e0ad104
Update changelog for v3.0.2 2021-09-28 09:13:44 +10:00
warwickh 7baa12f06f remove future and dateutil dependency 2021-09-28 09:05:01 +10:00
warwickh f97b9e1de9 Don't start service if scrobbling switched off 2021-09-10 09:24:48 +10:00
warwickh 8ca52e6cc8
Resolve merge conflict 2021-09-09 18:04:37 +10:00
Warwick Harris c4c0aa00c6 Merge scrobble to master 2021-09-09 18:01:32 +10:00
Warwick Harris 67bd25496e Testing when setting doesn't exist 2021-09-09 12:10:43 +10:00
warwickh 0932e650f8 Notify service start 2021-09-09 11:48:06 +10:00
Warwick Harris 57141eed02 Merge branch 'scrobble' of github.com:warwickh/plugin.audio.subsonic into scrobble 2021-09-09 09:40:37 +10:00
Warwick Harris 2335973433 Some error fixes 2021-09-09 09:40:18 +10:00
warwickh 150b5012da
Update readme to include scrobbling 2021-09-08 17:00:23 +10:00
warwickh 8c4f31db05 More efficient settings and return value 2021-09-08 16:47:08 +10:00
warwickh 596ddaacdb First working scrobble service 2021-09-08 16:25:36 +10:00
warwickh c25e5848cf
Update settings for merge default 2021-09-07 17:01:24 +10:00
warwickh 469681bf5e Add insecure fix - still needs correct hostname 2021-09-07 15:29:59 +10:00
warwickh ced597b6d2
Update changelog for v3.0.1 2021-09-06 10:55:20 +10:00
warwickh 03da132550 Added option for merging albums in browse mode 2021-09-05 15:13:23 +10:00
warwickh 742c5b66fc Working with random update, need to format info better 2021-09-05 14:34:41 +10:00
warwickh 640157dad0 Added artist info - first working 2021-09-04 16:40:44 +10:00
warwickh 56dffe70bc Added artist info - some errors remaining 2021-09-04 16:00:49 +10:00
warwickh 34a7c249be
Update settings string 2021-09-04 14:13:08 +10:00
warwickh 67d2302bc4 Update installation details in readme 2021-09-04 10:23:26 +10:00
warwickh 9fbb67cc3c
Update readme with repo details 2021-09-03 18:35:39 +10:00
warwickh 0ac9d36187
Update readme to include repository zip 2021-09-03 18:34:06 +10:00
warwickh 49754bccaa
Update version number 2021-09-03 13:05:07 +10:00
warwickh 358df44fb1 Cleanup and prepare for merge 2021-09-02 16:54:48 +10:00
Warwick Harris a551df5c8c Clean up deprecation warnings and add some logging to resolve http client timeout 2021-09-02 12:56:34 +10:00
warwickh e640d0f81d
Update Readme to include Navidrome compatibility 2021-08-31 09:22:18 +10:00
warwickh 3aac9d1c54
Change to use GET as default 2021-08-31 09:16:03 +10:00
Warwick Harris 7564e75f63 Catch empty star response 2021-08-31 08:52:05 +10:00
Warwick Harris ed3f7da8b5 Remove dependency for integer ids to suit Navidrome requirement 2021-08-30 16:42:28 +10:00
warwickh 194844571c Fix ndent 2021-07-02 18:12:01 +10:00
warwickh 6035ff49b8 Avoid errors in walk functions 2021-07-02 17:37:44 +10:00
warwickh bc30d36466 Correct fanart path in addon.xml 2021-07-02 08:57:04 +10:00
gordielachance 445303a2fc
Merge pull request #30 from warwickh/master
Merge Matrix compatible to master
2021-06-30 11:13:38 +02:00
warwickh 2d76c74f42 All logging to LOGDEBUG 2021-06-29 20:28:48 +10:00
warwickh a7f74582de Add fanart 2021-06-29 20:05:09 +10:00
warwickh 41a747a2b7 Add fanart 2021-06-29 20:02:35 +10:00
warwickh 2e5d012cae Add fanart 2021-06-29 20:01:30 +10:00
warwickh 53ecd6e5d1 Add fanart 2021-06-29 19:56:16 +10:00
warwickh 2efa209227
Clean up main.py 2021-06-29 15:47:38 +10:00
warwickh 1703d433a3 Move Matrix compatible version to 3.0.0 2021-06-29 14:47:07 +10:00
warwickh 17f77aca93
Change layout 2021-06-29 14:42:03 +10:00
warwickh a136a7b47e
Change layout 2021-06-29 14:40:26 +10:00
warwickh b461af0269
Change layout 2021-06-29 14:39:39 +10:00
warwickh bf1ee6bd1b
Update installation instructions 2021-06-29 14:39:16 +10:00
warwickh 79a9b1ebc3
Updated simpleplugin link 2021-06-29 14:32:53 +10:00
warwickh 46da611895
Update contact information 2021-06-29 14:14:58 +10:00
warwickh d524b8cc8e
set version for script.module.future 2021-06-29 14:14:07 +10:00
warwickh 0ddda4676c
Added requirement for script.module.future 2021-06-29 07:38:03 +10:00
warwickh 1d22cd75c1
Update settings.xml 2021-06-28 19:26:44 +10:00
warwickh 1fc06bd790 Add api version 1.16.0 2021-06-28 19:21:36 +10:00
warwickh e958c5bb2a Stream and coverart requests assume url request and add useget by default in connection 2021-06-28 19:07:56 +10:00
warwickh 9d86ff2051 Readd some logging for stream auth 2021-06-28 17:59:22 +10:00
warwickh 71e208b662
Need to check why useget required for streaming 2021-06-28 17:51:27 +10:00
warwickh 6910755b36
Update settings.xml
Ensure default bitrate
2021-06-28 17:41:01 +10:00
warwickh aca6ad6e83
Change port storage to text 2021-06-28 16:50:21 +10:00
warwickh adc78f9783 Clean up some logging 2021-06-28 15:47:08 +10:00
warwickh 0c32547c0d Update readme 2021-06-28 15:40:33 +10:00
warwickh 67b5e4ab8c Update readme 2021-06-28 15:13:17 +10:00
warwickh 861e7534c6 Fix connection port issue 2021-06-28 14:55:56 +10:00
warwickh c65548a73b Fix empty folder crash 2021-06-28 14:06:59 +10:00
warwickh f5202f2229 Restore notes against TO FIX items 2021-06-23 09:56:28 +10:00
warwickh 2b3789fe5c Fix browse function 2021-06-22 18:25:52 +10:00
warwickh 92356ab533 2.0.9 2021-06-22 16:47:09 +10:00
warwickh 1d3181fe4e Initial Matrix Compatible commit 2021-06-22 16:35:39 +10:00
gordielachance 577181bb73
Merge pull request #20 from gordielachance/2.0.8
2 0 8
2017-12-04 11:35:04 +01:00
gordielachance f0649bf2b5
Fix missing DE translation for "Search" 2017-12-03 18:32:32 +01:00
gordielachance b630801150
Merge pull request #19 from comxd/patch-1
Fix missing FR translation for "Search"
2017-12-03 18:30:06 +01:00
David 99d57fa1f9
Fix missing FR translation for "Search"
@see commit #9e7e542585eb67392fad3191f44a973c0e59839f
2017-12-03 18:16:21 +01:00
gordielachance f7ccef89ef 2.0.8 2017-11-29 11:30:58 +01:00
gordielachance d90af02d8b
Merge pull request #18 from Heruwar/feature/path-support_and_pw-improvements
Path support and security improvements
2017-11-29 11:09:56 +01:00
heru 4429451454 Dont sent plaintext password in requests and use subsonic hex-encoding with legacy auth 2017-11-25 18:15:12 +01:00
heru 313b413fc5 Add support for path in server URL 2017-11-25 18:13:44 +01:00
heru 30d0e8641e Make sure password and username are always strings 2017-11-25 18:12:53 +01:00
gordielachance b18b8e0094 Update addon.xml 2017-04-18 15:26:34 +02:00
gordielachance 66ae5ee093 Update CHANGELOG.md 2017-04-18 15:25:20 +02:00
gordielachance 426dcf964f Merge pull request #16 from silascutler/master
Added Search
2017-04-18 15:19:10 +02:00
Silas Cutler ce58b6993f Updates 2017-04-02 12:12:49 -04:00
Silas Cutler f51e5c3c28 Removed pop-up option 2017-04-02 11:44:22 -04:00
Silas Cutler e966b5bfa2 Removed pop-up option 2017-04-02 11:44:14 -04:00
Silas Cutler 9e7e542585 Added Search 2017-04-02 09:06:49 -04:00
gordielachance 10bb432a14 Merge pull request #12 from gordielachance/media-folders
Media folders
2017-01-14 12:47:36 +01:00
rickybiscus e6b00afc27 version update 2017-01-14 12:38:50 +01:00
rickybiscus 60ebe928b0 minor 2017-01-10 18:36:34 +01:00
rickybiscus 2482e56fdc minor 2017-01-09 11:36:23 +01:00
rickybiscus 290fa837b3 simpleplugin 2.1.0 2017-01-09 11:01:41 +01:00
rickybiscus 0344163cd5 typo 2017-01-09 10:50:00 +01:00
rickybiscus 3a0efadebb use Addon().get_localized_string (simpleplugin) 2017-01-09 10:48:30 +01:00
rickybiscus 8c2ffe006c localized strings : settings typo 2017-01-09 10:46:24 +01:00
rickybiscus ebf82b8218 fixes for localized strings 2017-01-09 10:46:11 +01:00
rickybiscus d9a18d41e3 reorganize code 2017-01-09 10:45:12 +01:00
rickybiscus c6e9cc92e8 Browse/Library menus (file structure vs ID3 tags) 2017-01-09 01:02:37 +01:00
rickybiscus 3ed7b231cc if there is only one media folder, return list_indexes() directly 2017-01-08 11:35:53 +01:00
rickybiscus 7d26333ce0 WIP : list_indexes function (not working yet) 2017-01-08 11:11:05 +01:00
rickybiscus d9210dcaef new 'Browse' top menu to list media folders 2017-01-08 11:09:54 +01:00
gordielachance 0cda25e07a Merge pull request #9 from Moshkopp/patch-2
german description.
2017-01-03 23:19:38 +01:00
Moshkopp be07844e38 german description. 2017-01-03 22:56:07 +01:00
rickybiscus 5484614cc1 typo 2017-01-03 22:07:47 +01:00
rickybiscus 7e67edd64a removed some strings
+ typos
+ changelog
2017-01-03 21:14:00 +01:00
rickybiscus 8ce871a9f7 typo 2017-01-03 20:54:51 +01:00
rickybiscus 3acb61d605 french translation 2017-01-03 20:40:08 +01:00
gordielachance c621458704 Merge pull request #7 from Moshkopp/master
Added multilanguage support
2017-01-03 20:14:41 +01:00
Moshkopp aaac00f69a Update strings.po 2016-12-28 18:13:22 +01:00
Moshkopp afea46a9e1 Add files via upload
typo
2016-12-28 18:12:29 +01:00
Moshkopp fb64a2e6fe Delete strings.po 2016-12-28 18:12:03 +01:00
Moshkopp a029490fe8 Add files via upload
typo
2016-12-28 18:10:05 +01:00
Moshkopp 4a057ba83c Add files via upload
Added multilanguage support
2016-12-28 14:33:26 +01:00
rickybiscus ef6ba710e2 doc 2016-10-15 01:33:55 +02:00
rickybiscus 4179ca8ada Fixed images when listing entries 2016-10-15 01:33:43 +02:00
rickybiscus fcbcccaeb9 doc 2016-10-15 01:27:12 +02:00
gordielachance fcfee371f8 doc 2016-10-04 17:13:00 +02:00
gordielachance 68a8fec4f8 try to fix https://github.com/gordielachance/plugin.audio.subsonic/issues/2 2016-10-04 17:08:05 +02:00
gordielachance 2285d5ca0b minor 2016-10-04 16:56:36 +02:00
gordielachance 537524d7fe fix simpleplugin dependency (finally!) 2016-10-04 16:56:29 +02:00
gordielachance 3cb2bc702b doc 2016-10-04 15:16:46 +02:00
gordielachance b66129b448 temporary disable sorting stuff 2016-10-04 15:13:57 +02:00
gordielachance 8bbeabc0dc removed list_artist_albums() + colorize starred items labels 2016-10-04 15:12:06 +02:00
gordielachance 5117218f86 hide artist in lists if it is unique 2016-10-04 14:57:24 +02:00
gordielachance f80b6458a1 improved Sort by 2016-10-04 14:47:01 +02:00
gordielachance 8bae5abd88 new function get_track_entry_label() 2016-10-04 12:53:53 +02:00
gordielachance c026d31836 better stars/unstar code
now update/get a stars (persistent) cache to handle the starred items
2016-10-04 12:45:01 +02:00
gordielachance f905da4f43 better import modules 2016-10-04 11:15:12 +02:00
gordielachance 8a786b1eae removed symlink to py-sonic
was crashing on KODI OpenELEC, safer not to use it.
2016-10-04 02:57:55 +02:00
gordielachance 73c6bd5721 Added Random Tracks
+ minor fixes
2016-10-04 02:52:42 +02:00
gordielachance 32f706ffbe match py-sonic variable names 2016-10-04 02:39:38 +02:00
gordielachance 5c1ba5015b doc update 2016-10-04 02:20:17 +02:00
gordielachance 5307f5d8c4 improved addon options 2016-10-04 02:19:56 +02:00
gordielachance ff52a69087 check for Simpleplugin when starting the plugin
Official repos are not up to date so let's force a check.
2016-10-04 01:33:22 +02:00
gordielachance 8d3d425089 tracks and albums download works
+ progress bar !
2016-10-04 01:06:52 +02:00
gordielachance c0a7386ff1 minor 2016-10-03 21:20:55 +02:00
gordielachance 3b9642ea85 now able to download tracks ! 2016-10-03 21:18:43 +02:00
gordielachance 52c2c2d2d7 improved context menus 2016-10-03 15:57:52 +02:00
gordielachance 48dc6a555b context menu : "star" and "download"
* star : implemented
* unstar : functions ready but not yet implemented
* download : function exists but not yet developed
2016-10-03 03:07:19 +02:00
gordielachance d92a831743 change appName from 'py-sonic' to 'Kodi' 2016-10-03 01:17:05 +02:00
gordielachance 19d39e4da6 abord when connection is False 2016-10-03 00:48:57 +02:00
gordielachance a9b2f10015 fix for "local variable 'connected' referenced before assignment" 2016-10-03 00:35:10 +02:00
gordielachance d94a6985e2 don't use Addon() but Plugin() instead
https://github.com/romanvm/script.module.simpleplugin/issues/1#issuecomm
ent-250970005
2016-10-02 22:24:03 +02:00
gordielachance d2ca6218ce @plugin.action() decorator
In v.2+ the recommended way to add actions is via @plugin.action()
decorator. This makes code more readable and Pythonic.
2016-10-02 22:20:10 +02:00
gordielachance 99196c32eb reverted 'Kodisonic' name change 2016-10-02 20:35:20 +02:00
gordielachance c08c8d28ee minor (doc) 2016-10-02 20:31:45 +02:00
gordielachance a4e8f2376d minor (doc) 2016-10-02 20:30:37 +02:00
gordielachance 6fee1db7ec switched name to Kodisonic 2016-10-02 20:14:59 +02:00
gordielachance ac9160ea2c Use SimplePlugin framework 2016-10-02 20:10:40 +02:00
gordielachance f16f234679 match py-sonic variable names 2016-10-02 20:07:50 +02:00
gordielachance 4073737008 add credits for lrusak 2016-10-01 01:17:19 +02:00
gordielachance 03d77f0c83 minor 2016-10-01 01:04:49 +02:00
gordielachance 44418a384e add legacyauth setting
cloned from
https://github.com/lrusak/plugin.audio.subsonic/commit/ad4fc0d0be33ea827
5a73e9a9d10bc6a5f772dba
2016-10-01 00:56:59 +02:00
gordielachance b35dfd278d add libsonic as a submodule
cloned from
https://github.com/lrusak/plugin.audio.subsonic/commit/ad830b67ddf88926e
12f75952e341d8d842cdb27
2016-10-01 00:54:49 +02:00
gordielachance 47d0fcbe77 add insecure setting
cloned from
https://github.com/lrusak/plugin.audio.subsonic/commit/5c5040a7d92a8e88c
8e371140a0fa2c6e606d0d5
2016-10-01 00:33:03 +02:00
gordielachance 6b4807bbf5 add apiversion setting
cloned from
https://github.com/lrusak/plugin.audio.subsonic/commit/761fc593c4a94027b
6483b98886a995b88bb03af
2016-10-01 00:31:01 +02:00
gordielachance fe158c33fb fixed spaces between arguments
https://github.com/basilfx/plugin.audio.subsonic/pull/4#pullrequestrevie
w-682340
2016-10-01 00:21:50 +02:00
gordielachance a6c18a2eb0 added pagination for albums 2016-10-01 00:17:55 +02:00
gordielachance 86888b5a7a Merge pull request #1 from gordielachance/walk_album_list
Walk album list
2016-09-20 16:27:25 +02:00
gordielachance 7be23bfd8d * New setting 'albums_per_page'
* New menu structure
* Albums : Newest / Most played / Rencently played / by Genre
* Tracks : Starred / Random by genre / Random by year
* Code cleanup
2016-09-20 16:26:32 +02:00
gordielachance 2580050353 kwargs fix 2016-09-20 09:11:16 +02:00
gordielachance 81e6bd7ae0 camelCase fix 2016-09-20 09:11:06 +02:00
gordielachance 16e8f4996e snake_case instead camelCase 2016-09-20 09:01:45 +02:00
gordielachance e8aa3c38a8 added 'Recently Added Albums' menu 2016-09-20 01:14:19 +02:00
gordielachance f929cdbdef replaced walk_album_list_genre() with walk_album_list()
+ new ‘album_list_size’ setting
2016-09-20 01:09:28 +02:00
gordielachance fb01d0eaa9 update libsonic to v0.5.1
makes plugin work again on Kodi Krypton (fixes issue
https://github.com/basilfx/plugin.audio.subsonic/issues/3)
2016-09-19 23:30:48 +02:00
16 changed files with 4592 additions and 1157 deletions

View File

@ -1,5 +1,72 @@
# Changelog
## v3.0.2
Released 29th September 2021 (by warwickh)
* Removed dependency on future and dateutil
* Simpleplugin modified - no longer py2 compatible
## v3.0.1
Released 2nd September 2021 (by warwickh)
* Added Navidrome compatibility (remove dependency on integer ids)
## v3.0.0
Released 29th June 2021 (by warwickh)
* Basic update to provide Matrix compatility. Not tested on Kodi below v19
* Updates simpleplugin to latest version (3.0.0) https://github.com/vlmaksime/script.module.simpleplugin
* Moves some legacy simpleplugin static routines into main.py
* Removes dependancy on libsonic_extra by moving some walk functions into main.py
* Updates libsonic to latest version and adds functions for returning raw url for populating menus
* Move to version 3+ for diffferentiation from Leia compatible version
## v2.0.8
Released 29th November 2017 (by Heruwar)
* Fixes a security issue where the password is sent as plaintext in the URL query parameters when methods from libsonic_extas are used.
Also adds Subsonic hex encoding when using legacy auth.
* Adds support for URL paths like https://hostname.com/subsonic as requested in Github issue #17 and also encountered in some of the reports (#14 and #5)
* Fixes an error when the password only contains digits, which simpleplugin converts to a Long, which later fails when libsonic tries to salt the password expecting a string.
## v2.0.7
Released 18 April 2017
* Added Search (by silascutler)
## v2.0.6
Released 14 January 2017
* Upgrade to simpleplugin 2.1.0
* Browse/Library menus (file structure vs ID3 tags)
* Added multilanguage support
## v2.0.5
Released 15 October 2016
* Fixed images when listing entries
## v2.0.4
Released 5 October 2016
* Cache (permanently) starred items so we can know everywhere if an item is starred or not without querying it
* Colorize starred items
* Hide artist in lists if it is unique
* Removed list_artist_albums()
* Include simpleplugin v2.0.1 as library and remove KODI dependency
## v2.0.3
Released 4 October 2016
* Random tracks submenu
* Download tracks
* Star tracks
* Context menu for downloading or marking items as favorite
## v2.0.2
* main code (main.py) fully rewritten
* Use SimplePlugin framework (http://romanvm.github.io/script.module.simpleplugin/index.html)
* Cache datas
## v2.0.1
* New setting 'albums_per_page'
* New menu structure
* Albums : Newest / Most played / Rencently played / by Genre
* Tracks : Starred / Random by genre / Random by year
* Upgraded: libsonic to v.0.5.0 (https://github.com/crustymonkey/py-sonic)
* Code cleanup
## v2.0.0
Released 14 September 2015

View File

@ -1,19 +1,48 @@
# Subsonic for Kodi
Kodi plugin to stream music from Subsonic.
# Subsonic
Kodi plugin to stream, star and download music from Subsonic/Airsonic/Navidrome (requires Subsonic API compatibility)
For feature requests / issues:
https://github.com/warwickh/plugin.audio.subsonic/issues
Contributions are welcome:
https://github.com/warwickh/plugin.audio.subsonic
Master branch updated to support Kodi 19 Matrix
Leia compatible version available in alternate branch
## Features
* Playlist support
* Browse by artist, album and genre
* Random playlists
* Browse by artist, albums (newest/most played/recently played/random), tracks (starred/random), and playlists
* Download songs
* Star songs
* Navidrome compatibility added (please report any issues)
* Scrobble to Last.FM
## Installation
From repository
[repository.warwickh](https://github.com/warwickh/repository.warwickh/raw/master/matrix/zips/repository.warwickh) (Please report any issues)
From GitHub
* Click the code button and download
* Enable unknown sources and install from zip in Kodi
or
* Navigate to your `.kodi/addons/` folder
* Clone this repository: `git clone https://github.com/basilfx/plugin.audio.subsonic.git`
* Clone this repository: `git clone https://github.com/warwickh/plugin.audio.subsonic.git`
* (Re)start Kodi.
Note: You will need to enter your server settings into the plugin configuration before use
## TODO
* Improve the caching system
* Search filter GUI for tracks and albums
## License
See the `LICENSE` file.
Additional copyright notices:
* The original [SubKodi](https://github.com/DarkAllMan/SubKodi) plugin.
* [`py-sonic`](https://github.com/crustymonkey/py-sonic) Python module.
* [Previous version of this plugin](https://github.com/gordielachance/plugin.audio.subsonic) by gordielachance
* [Previous version of this plugin](https://github.com/basilfx/plugin.audio.subsonic) by basilfx
* [SimplePlugin](https://github.com/romanvm/script.module.simpleplugin/stargazers) by romanvm now at [SimplePlugin3](https://github.com/vlmaksime/script.module.simpleplugin)
* The original [SubKodi](https://github.com/DarkAllMan/SubKodi) plugin
* [`py-sonic`](https://github.com/crustymonkey/py-sonic) Python module

383
addon.py
View File

@ -1,383 +0,0 @@
import os
import sys
import urllib
import urlparse
import xbmc
import xbmcgui
import xbmcaddon
import xbmcplugin
# Make sure library folder is on the path
addon = xbmcaddon.Addon("plugin.audio.subsonic")
sys.path.append(xbmc.translatePath(os.path.join(
addon.getAddonInfo("path"), "lib")))
import libsonic_extra
class Plugin(object):
"""
Plugin container.
"""
def __init__(self, addon_url, addon_handle, addon_args):
self.addon_url = addon_url
self.addon_handle = addon_handle
self.addon_args = addon_args
# Retrieve plugin settings
self.url = addon.getSetting("subsonic_url")
self.username = addon.getSetting("username")
self.password = addon.getSetting("password")
self.random_count = addon.getSetting("random_count")
self.bitrate = addon.getSetting("bitrate")
self.transcode_format = addon.getSetting("transcode_format")
# Create connection
self.connection = libsonic_extra.SubsonicClient(
self.url, self.username, self.password)
def build_url(self, query):
"""
Create URL for page.
"""
parts = list(urlparse.urlparse(self.addon_url))
parts[4] = urllib.urlencode(query)
return urlparse.urlunparse(parts)
def route(self):
"""
Map a Kodi request to certain action. This takes the `mode' query
parameter and executed the function in this instance with that name.
"""
mode = self.addon_args.get("mode", ["main_page"])[0]
if not mode.startswith("_"):
getattr(self, mode)()
def add_track(self, track, show_artist=False):
"""
Display one track in the list.
"""
url = self.connection.streamUrl(
sid=track["id"], maxBitRate=self.bitrate,
tformat=self.transcode_format)
# Create list item
if show_artist:
title = "%s - %s" % (
track.get("artist", "<Unknown>"),
track.get("title", "<Unknown>"))
else:
title = track.get("title", "<Unknown>")
# Create item
li = xbmcgui.ListItem(title)
# Handle cover art
if "coverArt" in track:
cover_art_url = self.connection.getCoverArtUrl(track["coverArt"])
li.setIconImage(cover_art_url)
li.setThumbnailImage(cover_art_url)
li.setProperty("fanart_image", cover_art_url)
# Handle metadata
li.setProperty("IsPlayable", "true")
li.setMimeType(track.get("contentType"))
li.setInfo(type="Music", infoLabels={
"Artist": track.get("artist"),
"Title": track.get("title"),
"Year": track.get("year"),
"Duration": track.get("duration"),
"Genre": track.get("genre"),
"TrackNumber": track.get("track")})
xbmcplugin.addDirectoryItem(
handle=self.addon_handle, url=url, listitem=li)
def add_album(self, album, show_artist=False):
"""
Display one album in the list.
"""
url = self.build_url({
"mode": "track_list",
"album_id": album["id"]})
# Create list item
if show_artist:
title = "%s - %s" % (
album.get("artist", "<Unknown>"),
album.get("name", "<Unknown>"))
else:
title = album.get("name", "<Unknown>")
# Add year if applicable
if album.get("year"):
title = "%s [%d]" % (title, album.get("year"))
# Create item
li = xbmcgui.ListItem()
li.setLabel(title)
# Handle cover art
if "coverArt" in album:
cover_art_url = self.connection.getCoverArtUrl(album["coverArt"])
li.setIconImage(cover_art_url)
li.setThumbnailImage(cover_art_url)
li.setProperty("fanart_image", cover_art_url)
# Handle metadata
li.setInfo(type="music", infoLabels={
"Artist": album.get("artist"),
"Album": album.get("name"),
"Year": album.get("year")})
xbmcplugin.addDirectoryItem(
handle=self.addon_handle, url=url, listitem=li, isFolder=True)
def main_page(self):
"""
Display main menu.
"""
menu = [
{"mode": "starred_list", "foldername": "Starred"},
{"mode": "playlists_list", "foldername": "Playlists"},
{"mode": "artist_list", "foldername": "Artists"},
{"mode": "genre_list", "foldername": "Genres"},
{"mode": "random_list", "foldername": "Random songs"}]
for entry in menu:
url = self.build_url(entry)
li = xbmcgui.ListItem(entry["foldername"])
xbmcplugin.addDirectoryItem(
handle=self.addon_handle, url=url, listitem=li, isFolder=True)
xbmcplugin.endOfDirectory(self.addon_handle)
def starred_list(self):
"""
Display starred songs.
"""
xbmcplugin.setContent(self.addon_handle, "songs")
for starred in self.connection.walk_starred():
self.add_track(starred, show_artist=True)
xbmcplugin.endOfDirectory(self.addon_handle)
def playlists_list(self):
"""
Display playlists.
"""
for playlist in self.connection.walk_playlists():
cover_art_url = self.connection.getCoverArtUrl(
playlist["coverArt"])
url = self.build_url({
"mode": "playlist_list", "playlist_id": playlist["id"]})
li = xbmcgui.ListItem(playlist["name"], iconImage=cover_art_url)
xbmcplugin.addDirectoryItem(
handle=self.addon_handle, url=url, listitem=li, isFolder=True)
xbmcplugin.endOfDirectory(self.addon_handle)
def playlist_list(self):
"""
Display playlist tracks.
"""
playlist_id = self.addon_args["playlist_id"][0]
xbmcplugin.setContent(self.addon_handle, "songs")
for track in self.connection.walk_playlist(playlist_id):
self.add_track(track, show_artist=True)
xbmcplugin.endOfDirectory(self.addon_handle)
def genre_list(self):
"""
Display list of genres menu.
"""
for genre in self.connection.walk_genres():
url = self.build_url({
"mode": "albums_by_genre_list",
"foldername": genre["value"].encode("utf-8")})
li = xbmcgui.ListItem(genre["value"])
xbmcplugin.addDirectoryItem(
handle=self.addon_handle, url=url, listitem=li, isFolder=True)
xbmcplugin.endOfDirectory(self.addon_handle)
def albums_by_genre_list(self):
"""
Display album list by genre menu.
"""
genre = self.addon_args["foldername"][0].decode("utf-8")
xbmcplugin.setContent(self.addon_handle, "albums")
for album in self.connection.walk_album_list_genre(genre):
self.add_album(album, show_artist=True)
xbmcplugin.endOfDirectory(self.addon_handle)
def artist_list(self):
"""
Display artist list
"""
xbmcplugin.setContent(self.addon_handle, "artists")
for artist in self.connection.walk_artists():
cover_art_url = self.connection.getCoverArtUrl(artist["id"])
url = self.build_url({
"mode": "album_list",
"artist_id": artist["id"]})
li = xbmcgui.ListItem(artist["name"])
li.setIconImage(cover_art_url)
li.setThumbnailImage(cover_art_url)
li.setProperty("fanart_image", cover_art_url)
xbmcplugin.addDirectoryItem(
handle=self.addon_handle, url=url, listitem=li, isFolder=True)
xbmcplugin.endOfDirectory(self.addon_handle)
def album_list(self):
"""
Display list of albums for certain artist.
"""
artist_id = self.addon_args["artist_id"][0]
xbmcplugin.setContent(self.addon_handle, "albums")
for album in self.connection.walk_artist(artist_id):
self.add_album(album)
xbmcplugin.addSortMethod(
self.addon_handle, xbmcplugin.SORT_METHOD_UNSORTED)
xbmcplugin.addSortMethod(
self.addon_handle, xbmcplugin.SORT_METHOD_ALBUM)
xbmcplugin.addSortMethod(
self.addon_handle, xbmcplugin.SORT_METHOD_ARTIST)
xbmcplugin.addSortMethod(
self.addon_handle, xbmcplugin.SORT_METHOD_VIDEO_YEAR)
xbmcplugin.endOfDirectory(self.addon_handle)
def track_list(self):
"""
Display track list.
"""
album_id = self.addon_args["album_id"][0]
xbmcplugin.setContent(self.addon_handle, "songs")
for track in self.connection.walk_album(album_id):
self.add_track(track)
xbmcplugin.endOfDirectory(self.addon_handle)
def random_list(self):
"""
Display random options.
"""
menu = [
{"mode": "random_by_genre_list", "foldername": "By genre"},
{"mode": "random_by_year_list", "foldername": "By year"}]
for entry in menu:
url = self.build_url(entry)
li = xbmcgui.ListItem(entry["foldername"])
xbmcplugin.addDirectoryItem(
handle=self.addon_handle, url=url, listitem=li, isFolder=True)
xbmcplugin.endOfDirectory(self.addon_handle)
def random_by_genre_list(self):
"""
Display random genre list.
"""
for genre in self.connection.walk_genres():
url = self.build_url({
"mode": "random_by_genre_track_list",
"foldername": genre["value"].encode("utf-8")})
li = xbmcgui.ListItem(genre["value"])
xbmcplugin.addDirectoryItem(
handle=self.addon_handle, url=url, listitem=li, isFolder=True)
xbmcplugin.endOfDirectory(self.addon_handle)
def random_by_genre_track_list(self):
"""
Display random tracks by genre
"""
genre = self.addon_args["foldername"][0].decode("utf-8")
xbmcplugin.setContent(self.addon_handle, "songs")
for track in self.connection.walk_random_songs(
size=self.random_count, genre=genre):
self.add_track(track, show_artist=True)
xbmcplugin.endOfDirectory(self.addon_handle)
def random_by_year_list(self):
"""
Display random tracks by year.
"""
from_year = xbmcgui.Dialog().input(
"From year", type=xbmcgui.INPUT_NUMERIC)
to_year = xbmcgui.Dialog().input(
"To year", type=xbmcgui.INPUT_NUMERIC)
xbmcplugin.setContent(self.addon_handle, "songs")
for track in self.connection.walk_random_songs(
size=self.random_count, from_year=from_year, to_year=to_year):
self.add_track(track, show_artist=True)
xbmcplugin.endOfDirectory(self.addon_handle)
def main():
"""
Entry point for this plugin. Instantiates a new plugin object and runs the
action that is given.
"""
addon_url = sys.argv[0]
addon_handle = int(sys.argv[1])
addon_args = urlparse.parse_qs(sys.argv[2][1:])
# Route request to action.
Plugin(addon_url, addon_handle, addon_args).route()
# Start plugin from within Kodi.
if __name__ == "__main__":
main()

View File

@ -1,21 +1,48 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.audio.subsonic" name="Subsonic" version="2.0.0" provider-name="BasilFX">
<addon id="plugin.audio.subsonic" name="Subsonic" version="3.0.2" provider-name="BasilFX,warwickh">
<requires>
<import addon="xbmc.python" version="2.19.0"/>
<import addon="xbmc.python" version="3.0.0"/>
</requires>
<extension point="xbmc.python.pluginsource" library="addon.py">
<extension point="xbmc.python.pluginsource" library="main.py">
<provides>audio</provides>
</extension>
<extension point="xbmc.service" library="service.py" />
<extension point="xbmc.addon.metadata">
<summary lang="en">Subsonic music addon for Kodi.</summary>
<description lang="en">Subsonic music addon for Kodi. Stream your tunes directly to Kodi.</description>
<summary lang="fr">Extension Subsonic pour Kodi.</summary>
<summary lang="de">Subsonic Musik Addon für Kodi.</summary>
<description lang="en">
Stream, star and download your tunes, directly to Kodi !
For feature requests / issues:
https://github.com/warwickh/plugin.audio.subsonic/issues
Contributions are welcome:
https://github.com/warwickh/plugin.audio.subsonic
</description>
<description lang="fr">
Jouez, marquez vos favoris et téléchargez votre musique, directement dans Kodi !
Pour les demandes et problèmes :
https://github.com/warwickh/plugin.audio.subsonic/issues
Les contributions sont les bienvenues :
https://github.com/warwickh/plugin.audio.subsonic
</description>
<description lang="de">
Streame, bewerte und downloade deine Medien direkt in Kodi !
Für neue Eigentschaften oder Fehler:
https://github.com/warwickh/plugin.audio.subsonic/issues
Beihilfe ist Willkommen:
https://github.com/warwickh/plugin.audio.subsonic
</description>
<assets>
<icon>icon.png</icon>
<fanart>fanart.jpg</fanart>
</assets>
<disclaimer lang="en"></disclaimer>
<language>en</language>
<language>multi</language>
<platform>all</platform>
<license>MIT</license>
<forum></forum>
<website>http://www.subsonic.org</website>
<source>https://github.com/basilfx/plugin.audio.subsonic</source>
<source>https://github.com/warwickh/plugin.audio.subsonic</source>
<email></email>
</extension>
</addon>

BIN
fanart.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

View File

@ -1,32 +1,24 @@
"""
This file is part of py-sonic.
py-sonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
py-sonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with py-sonic. If not, see <http://www.gnu.org/licenses/>
For information on method calls, see 'pydoc libsonic.connection'
----------
Basic example:
----------
import libsonic
conn = libsonic.Connection('http://localhost' , 'admin' , 'password')
print conn.ping()
"""
from connection import *
from .connection import *
__version__ = '0.3.4'
__version__ = '0.7.9'

File diff suppressed because it is too large Load Diff

View File

@ -1,433 +0,0 @@
import urllib
import urlparse
import libsonic
def force_list(value):
"""
Coerce the input value to a list.
If `value` is `None`, return an empty list. If it is a single value, create
a new list with that element on index 0.
:param value: Input value to coerce.
:return: Value as list.
:rtype: list
"""
if value is None:
return []
elif type(value) == list:
return value
else:
return [value]
class SubsonicClient(libsonic.Connection):
"""
Extend `libsonic.Connection` with new features and fix a few issues.
- Parse URL for host and port for constructor.
- Make sure API results are of of uniform type.
- Provide methods to intercept URL of binary requests.
- Add order property to playlist items.
- Add conventient `walk_*' methods to iterate over the API responses.
"""
def __init__(self, url, username, password):
"""
Construct a new SubsonicClient.
:param str url: Full URL (including scheme) of the Subsonic server.
:param str username: Username of the server.
:param str password: Password of the server.
"""
self.intercept_url = False
# Parse Subsonic URL
parts = urlparse.urlparse(url)
scheme = parts.scheme or "http"
# Make sure there is hostname
if not parts.hostname:
raise ValueError("Expected hostname for URL: %s" % url)
# Validate scheme
if scheme not in ("http", "https"):
raise ValueError("Unexpected scheme '%s' for URL: %s" % (
scheme, url))
# Pick a default port
host = "%s://%s" % (scheme, parts.hostname)
port = parts.port or {"http": 80, "https": 443}[scheme]
# Invoke original constructor
super(SubsonicClient, self).__init__(
host, username, password, port=port)
def getIndexes(self, *args, **kwargs):
"""
Improve the getIndexes method. Ensures IDs are integers.
"""
def _artists_iterator(artists):
for artist in force_list(artists):
artist["id"] = int(artist["id"])
yield artist
def _index_iterator(index):
for index in force_list(index):
index["artist"] = list(_artists_iterator(index.get("artist")))
yield index
def _children_iterator(children):
for child in force_list(children):
child["id"] = int(child["id"])
if "parent" in child:
child["parent"] = int(child["parent"])
if "coverArt" in child:
child["coverArt"] = int(child["coverArt"])
if "artistId" in child:
child["artistId"] = int(child["artistId"])
if "albumId" in child:
child["albumId"] = int(child["albumId"])
yield child
response = super(SubsonicClient, self).getIndexes(*args, **kwargs)
response["indexes"] = response.get("indexes", {})
response["indexes"]["index"] = list(
_index_iterator(response["indexes"].get("index")))
response["indexes"]["child"] = list(
_children_iterator(response["indexes"].get("child")))
return response
def getPlaylists(self, *args, **kwargs):
"""
Improve the getPlaylists method. Ensures IDs are integers.
"""
def _playlists_iterator(playlists):
for playlist in force_list(playlists):
playlist["id"] = int(playlist["id"])
yield playlist
response = super(SubsonicClient, self).getPlaylists(*args, **kwargs)
response["playlists"]["playlist"] = list(
_playlists_iterator(response["playlists"].get("playlist")))
return response
def getPlaylist(self, *args, **kwargs):
"""
Improve the getPlaylist method. Ensures IDs are integers and add an
order property to each entry.
"""
def _entries_iterator(entries):
for order, entry in enumerate(force_list(entries), start=1):
entry["id"] = int(entry["id"])
entry["order"] = order
yield entry
response = super(SubsonicClient, self).getPlaylist(*args, **kwargs)
response["playlist"]["entry"] = list(
_entries_iterator(response["playlist"].get("entry")))
return response
def getArtists(self, *args, **kwargs):
"""
Improve the getArtists method. Ensures IDs are integers.
"""
def _artists_iterator(artists):
for artist in force_list(artists):
artist["id"] = int(artist["id"])
yield artist
def _index_iterator(index):
for index in force_list(index):
index["artist"] = list(_artists_iterator(index.get("artist")))
yield index
response = super(SubsonicClient, self).getArtists(*args, **kwargs)
response["artists"] = response.get("artists", {})
response["artists"]["index"] = list(
_index_iterator(response["artists"].get("index")))
return response
def getArtist(self, *args, **kwargs):
"""
Improve the getArtist method. Ensures IDs are integers.
"""
def _albums_iterator(albums):
for album in force_list(albums):
album["id"] = int(album["id"])
if "artistId" in album:
album["artistId"] = int(album["artistId"])
yield album
response = super(SubsonicClient, self).getArtist(*args, **kwargs)
response["artist"]["album"] = list(
_albums_iterator(response["artist"].get("album")))
return response
def getMusicDirectory(self, *args, **kwargs):
"""
Improve the getMusicDirectory method. Ensures IDs are integers.
"""
def _children_iterator(children):
for child in force_list(children):
child["id"] = int(child["id"])
if "parent" in child:
child["parent"] = int(child["parent"])
if "coverArt" in child:
child["coverArt"] = int(child["coverArt"])
if "artistId" in child:
child["artistId"] = int(child["artistId"])
if "albumId" in child:
child["albumId"] = int(child["albumId"])
yield child
response = super(SubsonicClient, self).getMusicDirectory(
*args, **kwargs)
response["directory"]["child"] = list(
_children_iterator(response["directory"].get("child")))
return response
def getAlbum(self, *args, **kwargs):
"""
Improve the getAlbum method. Ensures the IDs are real integers.
"""
def _songs_iterator(songs):
for song in force_list(songs):
song["id"] = int(song["id"])
yield song
response = super(SubsonicClient, self).getAlbum(*args, **kwargs)
response["album"]["song"] = list(
_songs_iterator(response["album"].get("song")))
return response
def getAlbumList2(self, *args, **kwargs):
"""
Improve the getAlbumList2 method. Ensures the IDs are real integers.
"""
def _album_iterator(albums):
for album in force_list(albums):
album["id"] = int(album["id"])
yield album
response = super(SubsonicClient, self).getAlbumList2(*args, **kwargs)
response["albumList2"]["album"] = list(
_album_iterator(response["albumList2"].get("album")))
return response
def getStarred(self, *args, **kwargs):
"""
Improve the getStarred method. Ensures the IDs are real integers.
"""
def _song_iterator(songs):
for song in force_list(songs):
song["id"] = int(song["id"])
yield song
response = super(SubsonicClient, self).getStarred(*args, **kwargs)
response["starred"]["song"] = list(
_song_iterator(response["starred"].get("song")))
return response
def getCoverArtUrl(self, *args, **kwargs):
"""
Return an URL to the cover art.
"""
self.intercept_url = True
url = self.getCoverArt(*args, **kwargs)
self.intercept_url = False
return url
def streamUrl(self, *args, **kwargs):
"""
Return an URL to the file to stream.
"""
self.intercept_url = True
url = self.stream(*args, **kwargs)
self.intercept_url = False
return url
def _doBinReq(self, *args, **kwargs):
"""
Intercept request URL to provide the URL of the item that is requested.
If the URL is intercepted, the request is not executed. A username and
password is added to provide direct access to the stream.
"""
if self.intercept_url:
parts = list(urlparse.urlparse(
args[0].get_full_url() + "?" + args[0].data))
parts[4] = dict(urlparse.parse_qsl(parts[4]))
parts[4].update({"u": self.username, "p": self.password})
parts[4] = urllib.urlencode(parts[4])
return urlparse.urlunparse(parts)
else:
return super(SubsonicClient, self)._doBinReq(*args, **kwargs)
def walk_index(self):
"""
Request Subsonic's index and iterate each item.
"""
response = self.getIndexes()
for index in response["indexes"]["index"]:
for index in index["artist"]:
for item in self.walk_directory(index["id"]):
yield item
for child in response["indexes"]["child"]:
if child.get("isDir"):
for child in self.walk_directory(child["id"]):
yield child
else:
yield child
def walk_playlists(self):
"""
Request Subsonic's playlists and iterate over each item.
"""
response = self.getPlaylists()
for child in response["playlists"]["playlist"]:
yield child
def walk_playlist(self, playlist_id):
"""
Request Subsonic's playlist items and iterate over each item.
"""
response = self.getPlaylist(playlist_id)
for child in response["playlist"]["entry"]:
yield child
def walk_starred(self):
"""
Request Subsonic's starred songs and iterate over each item.
"""
response = self.getStarred()
for song in response["starred"]["song"]:
yield song
def walk_directory(self, directory_id):
"""
Request a Subsonic music directory and iterate over each item.
"""
response = self.getMusicDirectory(directory_id)
for child in response["directory"]["child"]:
if child.get("isDir"):
for child in self.walk_directory(child["id"]):
yield child
else:
yield child
def walk_artist(self, artist_id):
"""
Request a Subsonic artist and iterate over each album.
"""
response = self.getArtist(artist_id)
for child in response["artist"]["album"]:
yield child
def walk_artists(self):
"""
Request all artists and iterate over each item.
"""
response = self.getArtists()
for index in response["artists"]["index"]:
for artist in index["artist"]:
yield artist
def walk_genres(self):
"""
Request all genres and iterate over each item.
"""
response = self.getGenres()
for genre in response["genres"]["genre"]:
yield genre
def walk_album_list_genre(self, genre):
"""
Request all albums for a given genre and iterate over each album.
"""
offset = 0
while True:
response = self.getAlbumList2(
ltype="byGenre", genre=genre, size=500, offset=offset)
if not response["albumList2"]["album"]:
break
for album in response["albumList2"]["album"]:
yield album
offset += 500
def walk_album(self, album_id):
"""
Request an alum and iterate over each item.
"""
response = self.getAlbum(album_id)
for song in response["album"]["song"]:
yield song
def walk_random_songs(self, size, genre=None, from_year=None,
to_year=None):
"""
Request random songs by genre and/or year and iterate over each song.
"""
response = self.getRandomSongs(
size=size, genre=genre, fromYear=from_year, toYear=to_year)
for song in response["randomSongs"]["song"]:
yield song

View File

@ -0,0 +1,4 @@
#v2.1.0
#https://github.com/romanvm/script.module.simpleplugin/releases
from .simpleplugin import *

File diff suppressed because it is too large Load Diff

1561
main.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,182 @@
# XBMC Media Center language file
# Addon Name: Subsonic
# Addon id: plugin.audio.subsonic
# Addon Provider:
# Addon Translate: Moshkopp
msgid ""
msgstr ""
msgctxt "#30000"
msgid "General"
msgstr ""
msgctxt "#30001"
msgid "Server"
msgstr ""
msgctxt "#30002"
msgid "Server URL"
msgstr ""
msgctxt "#30003"
msgid "Username"
msgstr ""
msgctxt "#30004"
msgid "Password"
msgstr ""
msgctxt "#30005"
msgid "Display"
msgstr ""
msgctxt "#30006"
msgid "Albums per page"
msgstr ""
msgctxt "#30007"
msgid "Tracks per page (ignored in albums & playlists)"
msgstr ""
msgctxt "#30008"
msgid "Download"
msgstr ""
msgctxt "#30009"
msgid "Download folder"
msgstr ""
msgctxt "#30010"
msgid "Streaming"
msgstr ""
msgctxt "#30011"
msgid "Transcode format"
msgstr ""
msgctxt "#30012"
msgid "Bitrate"
msgstr ""
msgctxt "#30013"
msgid "Advanced Settings"
msgstr ""
msgctxt "#30014"
msgid "API version"
msgstr ""
msgctxt "#30016"
msgid "Allow self signed certificates"
msgstr ""
msgctxt "#30017"
msgid "Cache (in minutes)"
msgstr ""
msgctxt "#30018"
msgid "Cache data time"
msgstr ""
msgctxt "#30019"
msgid "Library"
msgstr ""
msgctxt "#30020"
msgid "Albums"
msgstr ""
msgctxt "#30021"
msgid "Tracks"
msgstr ""
msgctxt "#30022"
msgid "Playlists"
msgstr ""
msgctxt "#30023"
msgid "Newest albums"
msgstr ""
msgctxt "#30024"
msgid "Most played albums"
msgstr ""
msgctxt "#30025"
msgid "Recently played albums"
msgstr ""
msgctxt "#30026"
msgid "Random albums"
msgstr ""
msgctxt "#30029"
msgid "Next page"
msgstr ""
msgctxt "#30030"
msgid "Back to Menu"
msgstr ""
msgctxt "#30031"
msgid "Item has been unstarred."
msgstr ""
msgctxt "#30032"
msgid "Item has been starred!"
msgstr ""
msgctxt "#30033"
msgid "Star on Subsonic"
msgstr ""
msgctxt "#30034"
msgid "Unstar on Subsonic"
msgstr ""
msgctxt "#30035"
msgid "Download"
msgstr ""
msgctxt "#30036"
msgid "Starred tracks"
msgstr ""
msgctxt "#30037"
msgid "Random tracks"
msgstr ""
msgctxt "#30038"
msgid "Browse"
msgstr ""
msgctxt "#30039"
msgid "Search Songs"
msgstr ""
msgctxt "#30040"
msgid "useGET"
msgstr ""
msgctxt "#30041"
msgid "legacyauth"
msgstr ""
msgctxt "#30042"
msgid "port"
msgstr ""
msgctxt "#30043"
msgid "Merge album folders"
msgstr ""
msgctxt "#30044"
msgid "Scrobble to Last.FM"
msgstr ""
msgctxt "#30045"
msgid "Search Albums"
msgstr ""

View File

@ -0,0 +1,182 @@
# XBMC Media Center language file
# Addon Name: Subsonic
# Addon id: plugin.audio.subsonic
# Addon Provider:
# Addon Translate: Gordie
msgid ""
msgstr ""
msgctxt "#30000"
msgid "General"
msgstr "Général"
msgctxt "#30001"
msgid "Server"
msgstr "Serveur"
msgctxt "#30002"
msgid "Server URL"
msgstr "URL du serveur"
msgctxt "#30003"
msgid "Username"
msgstr "Nom d'utilisateur"
msgctxt "#30004"
msgid "Password"
msgstr "Mot de passe"
msgctxt "#30005"
msgid "Display"
msgstr "Affichage"
msgctxt "#30006"
msgid "Albums per page"
msgstr "Albums par page"
msgctxt "#30007"
msgid "Tracks per page (ignored in albums & playlists)"
msgstr "Pistes par page (ignoré dans les albums & listes de lecture)"
msgctxt "#30008"
msgid "Download"
msgstr "Télécharger"
msgctxt "#30009"
msgid "Download folder"
msgstr "Répertoire de téléchargement"
msgctxt "#30010"
msgid "Streaming"
msgstr "Diffusion"
msgctxt "#30011"
msgid "Transcode format"
msgstr "Format de transcodage"
msgctxt "#30012"
msgid "Bitrate"
msgstr "Bitrate"
msgctxt "#30013"
msgid "Advanced Settings"
msgstr "Paramètres avancés"
msgctxt "#30014"
msgid "API version"
msgstr "Version de l'API"
msgctxt "#30016"
msgid "Allow self signed certificates"
msgstr "Autoriser les certificats auto-signés"
msgctxt "#30017"
msgid "Cache (in minutes)"
msgstr "Cache (en minutes)"
msgctxt "#30018"
msgid "Cache datas time"
msgstr "Durée du cache pour les données"
msgctxt "#30019"
msgid "Library"
msgstr "Bibliothèque"
msgctxt "#30020"
msgid "Albums"
msgstr "Albums"
msgctxt "#30021"
msgid "Tracks"
msgstr "Pistes"
msgctxt "#30022"
msgid "Playlists"
msgstr "Playlists"
msgctxt "#30023"
msgid "Newest albums"
msgstr "Nouveaux albums"
msgctxt "#30024"
msgid "Most played albums"
msgstr "Albums les plus joués"
msgctxt "#30025"
msgid "Recently played albums"
msgstr "Albums joués récemment"
msgctxt "#30026"
msgid "Random albums"
msgstr "Albums au hasard"
msgctxt "#30029"
msgid "Next page"
msgstr "Page suivante"
msgctxt "#30030"
msgid "Back to Menu"
msgstr "Retour au menu"
msgctxt "#30031"
msgid "Item has been unstarred."
msgstr "Cet élément a été retiré des favoris"
msgctxt "#30032"
msgid "Item has been starred!"
msgstr "Cet élément a été ajouté aux favoris !"
msgctxt "#30033"
msgid "Star on Subsonic"
msgstr "Ajouter aux favoris Subsonic"
msgctxt "#30034"
msgid "Unstar on Subsonic"
msgstr "Retirer des favoris Subsonic"
msgctxt "#30035"
msgid "Download"
msgstr "Télécharger"
msgctxt "#30036"
msgid "Starred tracks"
msgstr "Pistes favorites"
msgctxt "#30037"
msgid "Random tracks"
msgstr "Pistes au hasard"
msgctxt "#30038"
msgid "Browse"
msgstr "Parcourir"
msgctxt "#30039"
msgid "Search Songs"
msgstr "Rechercher Chansons"
msgctxt "#30040"
msgid "useGET"
msgstr ""
msgctxt "#30041"
msgid "legacyauth"
msgstr ""
msgctxt "#30042"
msgid "port"
msgstr ""
msgctxt "#30043"
msgid "Merge album folders"
msgstr ""
msgctxt "#30044"
msgid "Scrobble to Last.FM"
msgstr ""
msgctxt "#30045"
msgid "Search Albums"
msgstr "Rechercher Albums"

View File

@ -0,0 +1,181 @@
# XBMC Media Center language file
# Addon Name: Subsonic
# Addon id: plugin.audio.subsonic
# Addon Provider:
# Addon Translate: Moshkopp
msgid ""
msgstr ""
msgctxt "#30000"
msgid "General"
msgstr "Allgemein"
msgctxt "#30001"
msgid "Server"
msgstr "Server"
msgctxt "#30002"
msgid "Server URL"
msgstr "Serveradresse"
msgctxt "#30003"
msgid "Username"
msgstr "Benutzername"
msgctxt "#30004"
msgid "Password"
msgstr "Passwort"
msgctxt "#30005"
msgid "Display"
msgstr "Anzeige"
msgctxt "#30006"
msgid "Albums per page"
msgstr "Alben pro Seite"
msgctxt "#30007"
msgid "Tracks per page (ignored in albums & playlists)"
msgstr "Lieder pro Seite (wird in Alben und Playlisten ignoriert)"
msgctxt "#30008"
msgid "Download"
msgstr "Download"
msgctxt "#30009"
msgid "Download folder"
msgstr "Download Verzeichnis"
msgctxt "#30010"
msgid "Streaming"
msgstr "Übertragung"
msgctxt "#30011"
msgid "Transcode format"
msgstr "Umwandlungs Format"
msgctxt "#30012"
msgid "Bitrate"
msgstr "Bitrate"
msgctxt "#30013"
msgid "Advanced Settings"
msgstr "Erweitert"
msgctxt "#30014"
msgid "API version"
msgstr "API Version"
msgctxt "#30016"
msgid "Allow self signed certificates"
msgstr "Erlaube eigensignierte Zertifikate"
msgctxt "#30017"
msgid "Cache (in minutes)"
msgstr "Speicher (in Minuten)"
msgctxt "#30018"
msgid "Cache datas time"
msgstr "Speicher Daten Zeit"
msgctxt "#30019"
msgid "Library"
msgstr "Bibliothek"
msgctxt "#30020"
msgid "Albums"
msgstr "Alben"
msgctxt "#30021"
msgid "Tracks"
msgstr "Lieder"
msgctxt "#30022"
msgid "Playlists"
msgstr "Playlisten"
msgctxt "#30023"
msgid "Newest albums"
msgstr "Neueste Alben"
msgctxt "#30024"
msgid "Most played albums"
msgstr "Häufig gehörte Alben"
msgctxt "#30025"
msgid "Recently played albums"
msgstr "Zuletzt gehörte Alben"
msgctxt "#30026"
msgid "Random albums"
msgstr "Zufällige Alben"
msgctxt "#30029"
msgid "Next page"
msgstr "Nächste Seite"
msgctxt "#30030"
msgid "Back to Menu"
msgstr "Hauptmenü"
msgctxt "#30031"
msgid "Item has been unstarred."
msgstr "Bewertung entfernt"
msgctxt "#30032"
msgid "Item has been starred!"
msgstr "Bewertung hinzugefügt"
msgctxt "#30033"
msgid "Star on Subsonic"
msgstr "Bewerten auf Subsonic"
msgctxt "#30034"
msgid "Unstar on Subsonic"
msgstr "Löschen auf Subsonic"
msgctxt "#30035"
msgid "Download"
msgstr "Herunterladen"
msgctxt "#30036"
msgid "Starred tracks"
msgstr "Lieblings lieder"
msgctxt "#30037"
msgid "Random tracks"
msgstr "Zufällig lieder"
msgctxt "#30038"
msgid "Browse"
msgstr "Durchsuchen"
msgctxt "#30039"
msgid "Search Songs"
msgstr "Suche Lieder"
msgctxt "#30040"
msgid "useGET"
msgstr ""
msgctxt "#30041"
msgid "legacyauth"
msgstr ""
msgctxt "#30042"
msgid "port"
msgstr ""
msgctxt "#30043"
msgid "Merge album folders"
msgstr ""
msgctxt "#30044"
msgid "Scrobble to Last.FM"
msgstr ""
msgctxt "#30045"
msgid "Search Albums"
msgstr "Suche Albums"

View File

@ -1,9 +1,33 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<settings>
<setting id="subsonic_url" type="text" label="URL" default="http://demo.subsonic.org"/>
<setting id="username" type="text" label="Username" default="guest3"/>
<setting id="password" type="text" option="hidden" label="Password" default="guest"/>
<setting id="random_count" type="labelenum" label="Random songs" values="10|15|20|25"/>
<setting id="transcode_format" type="labelenum" label="Transcode format" values="mp3|raw|flv|ogg"/>
<setting id="bitrate" type="labelenum" label="Bitrate" values="320|256|224|192|160|128|112|96|80|64|56|48|40|32"/>
<!-- GENERAL -->
<category label="30000">
<setting label="30001" type="lsep" />
<setting label="30002" id="subsonic_url" type="text" default="http://demo.subsonic.org"/>
<setting label="30042" id="port" type="text" default="80"/>
<setting label="30003" id="username" type="text" default="guest3"/>
<setting label="30004" id="password" type="text" option="hidden" default="guest"/>
<setting label="30005" type="lsep" />
<setting label="30006" id="albums_per_page" type="labelenum" default="50" values="10|25|50|100|250|500"/>
<setting label="30007" id="tracks_per_page" type="labelenum" default="100" values="10|25|50|100|250|500"/>
<setting label="30008" type="lsep" />
<setting label="30009" id="download_folder" type="folder" source="auto" option="writeable"/>
<setting label="30010" type="lsep" />
<setting label="30011" id="transcode_format_streaming" type="labelenum" values="mp3|raw|flv|ogg"/>
<setting label="30012" id="bitrate_streaming" type="labelenum" default="0" values="320|256|224|192|160|128|112|96|80|64|56|48|40|32|0"/>
</category>
<!-- ADVANCED -->
<category label="30013">
<setting label="30001" type="lsep" />
<setting label="30014" id="apiversion" type="labelenum" values="1.11.0|1.12.0|1.13.0|1.14.0|1.15.0|1.16.0" default="1.15.0"/>
<setting label="30016" id="insecure" type="bool" default="false" />
<setting label="30040" id="useget" type="bool" default="true" />
<setting label="30041" id="legacyauth" type="bool" default="false" />
<setting label="30017" type="lsep" />
<setting label="30018" id="cachetime" type="labelenum" default="3600" values="1|5|15|30|60|120|180|720|1440|3600"/>
<setting label="30043" id="merge" type="bool" default="false" />
<setting label="30044" id="scrobble" type="bool" default="false" />
</category>
</settings>

105
service.py Normal file
View File

@ -0,0 +1,105 @@
import re
import xbmc
import xbmcvfs
import os
import xbmcaddon
# Add the /lib folder to sys
sys.path.append(xbmcvfs.translatePath(os.path.join(xbmcaddon.Addon("plugin.audio.subsonic").getAddonInfo("path"), "lib")))
import libsonic
from simpleplugin import Plugin
from simpleplugin import Addon
# Create plugin instance
plugin = Plugin()
connection = None
try:
scrobbleEnabled = Addon().get_setting('scrobble')
except:
scrobbleEnabled = False
scrobbled = False
def popup(text, time=5000, image=None):
title = plugin.addon.getAddonInfo('name')
icon = plugin.addon.getAddonInfo('icon')
xbmc.executebuiltin('Notification(%s, %s, %d, %s)' % (title, text,
time, icon))
def get_connection():
global connection
if connection==None:
connected = False
# Create connection
try:
connection = libsonic.Connection(
baseUrl=Addon().get_setting('subsonic_url'),
username=Addon().get_setting('username', convert=False),
password=Addon().get_setting('password', convert=False),
port=Addon().get_setting('port'),
apiVersion=Addon().get_setting('apiversion'),
insecure=Addon().get_setting('insecure'),
legacyAuth=Addon().get_setting('legacyauth'),
useGET=Addon().get_setting('useget'),
)
connected = connection.ping()
except:
pass
if connected==False:
popup('Connection error')
return False
return connection
def scrobble_track(track_id):
connection = get_connection()
if connection==False:
return False
res = connection.scrobble(track_id)
#xbmc.log("response %s"%(res), xbmc.LOGINFO)
if res['status'] == 'ok':
popup("Scrobbled track")
return True
else:
popup("Scrobble failed")
return False
if __name__ == '__main__':
if(scrobbleEnabled):
monitor = xbmc.Monitor()
xbmc.log("Subsonic service started", xbmc.LOGINFO)
popup("Subsonic service started")
while not monitor.abortRequested():
if monitor.waitForAbort(10):
break
if (xbmc.getCondVisibility("Player.HasMedia")):
try:
currentFileName = xbmc.getInfoLabel("Player.Filenameandpath")
currentFileProgress = xbmc.getInfoLabel("Player.Progress")
pattern = re.compile(r'plugin:\/\/plugin\.audio\.subsonic\/\?action=play_track&id=(.*?)&')
currentTrackId = re.findall(pattern, currentFileName)[0]
#xbmc.log("Name %s Id %s Progress %s"%(currentFileName,currentTrackId,currentFileProgress), xbmc.LOGDEBUG)
if (int(currentFileProgress)<50):
scrobbled = False
elif (int(currentFileProgress)>=50 and scrobbled == False):
xbmc.log("Scrobbling Track Id %s"%(currentTrackId), xbmc.LOGDEBUG)
success = scrobble_track(currentTrackId)
if success:
scrobbled = True
else:
pass
except IndexError:
print ("Not a Subsonic track")
scrobbled = True
except Exception as e:
xbmc.log("Subsonic service failed %e"%e, xbmc.LOGINFO)
else:
pass
#xbmc.log("Playing stopped", xbmc.LOGINFO)
else:
xbmc.log("Subsonic service not started due to settings", xbmc.LOGINFO)